Fixed help files.
This commit is contained in:
commit
b8f912cc79
1543 changed files with 395123 additions and 0 deletions
43
AclEngine-Tests/AclChangedEventArgsTests.cs
Normal file
43
AclEngine-Tests/AclChangedEventArgsTests.cs
Normal file
|
@ -0,0 +1,43 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ScrewTurn.Wiki.AclEngine.Tests {
|
||||
|
||||
[TestFixture]
|
||||
public class AclChangedEventArgsTests {
|
||||
|
||||
[Test]
|
||||
public void Constructor() {
|
||||
AclEntry entry = new AclEntry("Res", "Action", "U.User", Value.Grant);
|
||||
|
||||
AclChangedEventArgs args = new AclChangedEventArgs(new AclEntry[] { entry }, Change.EntryStored);
|
||||
|
||||
Assert.AreEqual(1, args.Entries.Length, "Wrong entry count");
|
||||
Assert.AreSame(entry, args.Entries[0], "Wrong Entry instance");
|
||||
Assert.AreEqual(Change.EntryStored, args.Change, "Wrong change");
|
||||
|
||||
args = new AclChangedEventArgs(new AclEntry[] { entry }, Change.EntryDeleted);
|
||||
|
||||
Assert.AreEqual(1, args.Entries.Length, "Wrong entry count");
|
||||
Assert.AreSame(entry, args.Entries[0], "Wrong Entry instance");
|
||||
Assert.AreEqual(Change.EntryDeleted, args.Change, "Wrong change");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void Constructor_NullEntries() {
|
||||
AclChangedEventArgs args = new AclChangedEventArgs(null, Change.EntryDeleted);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[ExpectedException(typeof(ArgumentException))]
|
||||
public void Constructor_EmptyEntries() {
|
||||
AclChangedEventArgs args = new AclChangedEventArgs(new AclEntry[0], Change.EntryDeleted);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
73
AclEngine-Tests/AclEngine-Tests.csproj
Normal file
73
AclEngine-Tests/AclEngine-Tests.csproj
Normal file
|
@ -0,0 +1,73 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.30729</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{9F22D0A6-115B-4EB1-8506-65263674CEA3}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>ScrewTurn.Wiki.AclEngine.Tests</RootNamespace>
|
||||
<AssemblyName>ScrewTurn.Wiki.AclEngine.Tests</AssemblyName>
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="nunit.framework, Version=2.5.1.9189, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\References\Tools\NUnit\framework\nunit.framework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Rhino.Mocks, Version=3.5.0.1337, Culture=neutral, PublicKeyToken=0b3305902db7183f, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\References\Tools\Rhino.Mocks\Rhino.Mocks.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\AssemblyVersion.cs">
|
||||
<Link>AssemblyVersion.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="AclChangedEventArgsTests.cs" />
|
||||
<Compile Include="AclEntryTests.cs" />
|
||||
<Compile Include="AclEvaluatorTests.cs" />
|
||||
<Compile Include="AclManagerBaseTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AclEngine\AclEngine.csproj">
|
||||
<Project>{44B0F4C1-8CDC-4272-B2A2-C0AF689CEB81}</Project>
|
||||
<Name>AclEngine</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
77
AclEngine-Tests/AclEntryTests.cs
Normal file
77
AclEngine-Tests/AclEntryTests.cs
Normal file
|
@ -0,0 +1,77 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ScrewTurn.Wiki.AclEngine.Tests {
|
||||
|
||||
[TestFixture]
|
||||
public class AclEntryTests {
|
||||
|
||||
[Test]
|
||||
public void Constructor() {
|
||||
AclEntry entry = new AclEntry("Res", "Action", "U.User", Value.Grant);
|
||||
|
||||
Assert.AreEqual("Res", entry.Resource, "Wrong resource");
|
||||
Assert.AreEqual("Action", entry.Action, "Wrong action");
|
||||
Assert.AreEqual("U.User", entry.Subject, "Wrong subject");
|
||||
Assert.AreEqual(Value.Grant, entry.Value, "Wrong value");
|
||||
|
||||
entry = new AclEntry("Res", "Action", "G.Group", Value.Deny);
|
||||
|
||||
Assert.AreEqual("Res", entry.Resource, "Wrong resource");
|
||||
Assert.AreEqual("Action", entry.Action, "Wrong action");
|
||||
Assert.AreEqual("G.Group", entry.Subject, "Wrong subject");
|
||||
Assert.AreEqual(Value.Deny, entry.Value, "Wrong value");
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void Constructor_InvalidResource(string r) {
|
||||
AclEntry entry = new AclEntry(r, "Action", "U.USer", Value.Grant);
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void Constructor_InvalidAction(string a) {
|
||||
AclEntry entry = new AclEntry("Res", a, "G.Group", Value.Deny);
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void Constructor_InvalidSubject(string s) {
|
||||
AclEntry entry = new AclEntry("Res", "Action", s, Value.Grant);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Equals() {
|
||||
AclEntry entry = new AclEntry("Res", "Action", "U.User", Value.Grant);
|
||||
|
||||
Assert.IsFalse(entry.Equals(null), "Equals should return false (testing null)");
|
||||
Assert.IsFalse(entry.Equals("blah"), "Equals should return false (testing a string)");
|
||||
Assert.IsFalse(entry.Equals(new AclEntry("Res1", "Action", "U.User", Value.Grant)), "Equals should return false");
|
||||
Assert.IsFalse(entry.Equals(new AclEntry("Res", "Action1", "U.User", Value.Grant)), "Equals should return false");
|
||||
Assert.IsFalse(entry.Equals(new AclEntry("Res", "Action", "U.User1", Value.Grant)), "Equals should return false");
|
||||
Assert.IsTrue(entry.Equals(new AclEntry("Res", "Action", "U.User", Value.Deny)), "Equals should return true");
|
||||
Assert.IsTrue(entry.Equals(new AclEntry("Res", "Action", "U.User", Value.Grant)), "Equals should return true");
|
||||
Assert.IsTrue(entry.Equals(entry), "Equals should return true");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Static_Equals() {
|
||||
AclEntry entry = new AclEntry("Res", "Action", "U.User", Value.Grant);
|
||||
|
||||
Assert.IsFalse(AclEntry.Equals(entry, null), "Equals should return false (testing null)");
|
||||
Assert.IsFalse(AclEntry.Equals(entry, "blah"), "Equals should return false (testing a string)");
|
||||
Assert.IsFalse(AclEntry.Equals(entry, new AclEntry("Res1", "Action", "U.User", Value.Grant)), "Equals should return false");
|
||||
Assert.IsFalse(AclEntry.Equals(entry, new AclEntry("Res", "Action1", "U.User", Value.Grant)), "Equals should return false");
|
||||
Assert.IsFalse(AclEntry.Equals(entry, new AclEntry("Res", "Action", "U.User1", Value.Grant)), "Equals should return false");
|
||||
Assert.IsTrue(AclEntry.Equals(entry, new AclEntry("Res", "Action", "U.User", Value.Deny)), "Equals should return true");
|
||||
Assert.IsTrue(AclEntry.Equals(entry, new AclEntry("Res", "Action", "U.User", Value.Grant)), "Equals should return true");
|
||||
Assert.IsTrue(AclEntry.Equals(entry, entry), "Equals should return true");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
440
AclEngine-Tests/AclEvaluatorTests.cs
Normal file
440
AclEngine-Tests/AclEvaluatorTests.cs
Normal file
|
@ -0,0 +1,440 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ScrewTurn.Wiki.AclEngine.Tests {
|
||||
|
||||
[TestFixture]
|
||||
public class AclEvaluatorTests {
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_InexistentResource() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User2", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "Action2", "U.User", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "*", "U.User3", Value.Grant));
|
||||
entries.Add(new AclEntry("Res2", "Action", "U.User", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Unknown, AclEvaluator.AuthorizeAction("Res3", "Action", "U.User", new string[0], entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_InexistentAction() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User2", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "Action2", "U.User", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "*", "U.User3", Value.Grant));
|
||||
entries.Add(new AclEntry("Res2", "Action", "U.User", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Unknown, AclEvaluator.AuthorizeAction("Res", "Action3", "U.User", new string[0], entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_GrantUserExplicit() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User2", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "Action2", "U.User", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "*", "U.User3", Value.Grant));
|
||||
entries.Add(new AclEntry("Res2", "Action", "U.User", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Granted, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[0], entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_GrantUserFullControl() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "*", "U.User", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User2", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "Action2", "U.User", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "*", "U.User3", Value.Grant));
|
||||
entries.Add(new AclEntry("Res2", "Action", "U.User", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Granted, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[0], entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_DenyUserExplicit() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User2", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "Action2", "U.User", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "*", "U.User3", Value.Grant));
|
||||
entries.Add(new AclEntry("Res2", "Action", "U.User", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Denied, AclEvaluator.AuthorizeAction("Res", "Action", "U.User2", new string[0], entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_DenyUserFullControl() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "*", "U.User2", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "Action2", "U.User", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "*", "U.User3", Value.Grant));
|
||||
entries.Add(new AclEntry("Res2", "Action", "U.User", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Denied, AclEvaluator.AuthorizeAction("Res", "Action", "U.User2", new string[0], entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_GrantGroupExplicit() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User2", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "Action2", "G.Group", Value.Deny));
|
||||
entries.Add(new AclEntry("Res2", "Action", "G.Group", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "*", "U.User3", Value.Grant));
|
||||
|
||||
Assert.AreEqual(Authorization.Granted, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_GrantGroupFullControl() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "*", "G.Group", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User2", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "Action2", "G.Group", Value.Deny));
|
||||
entries.Add(new AclEntry("Res2", "Action", "G.Group", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "*", "U.User3", Value.Grant));
|
||||
|
||||
Assert.AreEqual(Authorization.Granted, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_DenyGroupExplicit() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User2", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "Action2", "G.Group", Value.Grant));
|
||||
entries.Add(new AclEntry("Res2", "Action", "G.Group", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "*", "U.User3", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Denied, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_DenyGroupFullControl() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "*", "G.Group", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User2", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "Action2", "G.Group", Value.Grant));
|
||||
entries.Add(new AclEntry("Res2", "Action", "G.Group", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "*", "U.User3", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Denied, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_GrantGroupFullControl_DenyUserFullControl() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "*", "G.Group", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "*", "U.User", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Denied, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_GrantGroupExplicit_DenyUserFullControl() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "*", "U.User", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Denied, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_GrantGroupFullControl_DenyUserExplicit() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "*", "G.Group", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Denied, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_DenyGroupFullControl_GrantUserFullControl() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "*", "G.Group", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "*", "U.User", Value.Grant));
|
||||
|
||||
Assert.AreEqual(Authorization.Granted, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_DenyGroupExplicit_GrantUserFullControl() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "*", "U.User", Value.Grant));
|
||||
|
||||
Assert.AreEqual(Authorization.Granted, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_DenyGroupFullControl_GrantUserExplicit() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "*", "G.Group", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User", Value.Grant));
|
||||
|
||||
Assert.AreEqual(Authorization.Granted, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_GrantGroupFullControl_GrantUserFullControl() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "*", "G.Group", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "*", "U.User", Value.Grant));
|
||||
|
||||
Assert.AreEqual(Authorization.Granted, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_GrantGroupExplicit_GrantUserExplicit() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User", Value.Grant));
|
||||
|
||||
Assert.AreEqual(Authorization.Granted, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_DenyGroupExplicit_GrantUserExplicit() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User", Value.Grant));
|
||||
|
||||
Assert.AreEqual(Authorization.Granted, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_GrantGroupFullControl_GrantUserExplicit() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "*", "G.Group", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User", Value.Grant));
|
||||
|
||||
Assert.AreEqual(Authorization.Granted, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_GrantGroupExplicit_GrantUserFullControl() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "*", "U.User", Value.Grant));
|
||||
|
||||
Assert.AreEqual(Authorization.Granted, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_GrantGroupExplicit_DenyUserExpicit() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Denied, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_DenyGroupExplicit_DenyUserExplicit() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Denied, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_DenyGroupFullControl_DenyUserFullControl() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "*", "G.Group", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "*", "U.User", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Denied, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_DenyGroupFullControl_DenyUserExplicit() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "*", "G.Group", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Denied, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_DenyGroupExplicit_DenyUserFullControl() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "*", "U.User", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Denied, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_GrantOneGroupExplicit_GrantOtherGroupExplicit() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group1", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group2", Value.Grant));
|
||||
|
||||
Assert.AreEqual(Authorization.Granted, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group1", "G.Group2" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_DenyOneGroupExplicit_GrantOtherGroupExplicit() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group1", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group2", Value.Grant));
|
||||
|
||||
Assert.AreEqual(Authorization.Denied, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group1", "G.Group2" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_DenyOneGroupExplicit_DenyOtherGroupExplicit() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group1", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group2", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Denied, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group1", "G.Group2" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_GrantOneGroupFullControl_GrantOtherGroupFullControl() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "*", "G.Group1", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "*", "G.Group2", Value.Grant));
|
||||
|
||||
Assert.AreEqual(Authorization.Granted, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group1", "G.Group2" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_DenyOneGroupFullControl_DenyOtherGroupFullControl() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "*", "G.Group1", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "*", "G.Group2", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Denied, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group1", "G.Group2" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_DenyOneGroupFullControl_GrantOtherGroupFullControl() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "*", "G.Group1", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "*", "G.Group2", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Denied, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group1", "G.Group2" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_DenyOneGroupFullControl_GrantOtherGroupExplicit() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group1", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "*", "G.Group2", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Granted, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group1", "G.Group2" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_DenyOneGroupExplicit_GrantOtherGroupFullControl() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "*", "G.Group1", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group2", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Denied, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group1", "G.Group2" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_GrantOneGroupExplicit_GrantOtherGroupFullControl() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "*", "G.Group1", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group2", Value.Grant));
|
||||
|
||||
Assert.AreEqual(Authorization.Granted, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group1", "G.Group2" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_DenyOneGroupExplicit_DenyOtherGroupFullControl() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "*", "G.Group1", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group2", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Denied, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group1", "G.Group2" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_DenyUserExplicit_GrantUserFullControl() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "*", "U.User", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Denied, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[0], entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_DenyGroupExplicit_GrantGroupFullControl() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "*", "G.Group", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group", Value.Deny));
|
||||
|
||||
Assert.AreEqual(Authorization.Denied, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_GrantUserExplicit_DenyUserFullControl() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "*", "U.User", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User", Value.Grant));
|
||||
|
||||
Assert.AreEqual(Authorization.Granted, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[0], entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_GrantGroupExplicit_DenyGroupFullControl() {
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "*", "G.Group", Value.Deny));
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group", Value.Grant));
|
||||
|
||||
Assert.AreEqual(Authorization.Granted, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[] { "G.Group" }, entries.ToArray()), "Wrong auth result");
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void AuthorizeAction_InvalidResource(string r) {
|
||||
AclEvaluator.AuthorizeAction(r, "Action", "U.User", new string[0], new AclEntry[0]);
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
[TestCase(AclEntry.FullControlAction, ExpectedException = typeof(ArgumentException))]
|
||||
public void AuthorizeAction_InvalidAction(string a) {
|
||||
AclEvaluator.AuthorizeAction("Res", a, "U.User", new string[0], new AclEntry[0]);
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void AuthorizeAction_InvalidUser(string u) {
|
||||
AclEvaluator.AuthorizeAction("Res", "Action", u, new string[0], new AclEntry[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void AuthorizeAction_NullGroups() {
|
||||
AclEvaluator.AuthorizeAction("Res", "Action", "U.User", null, new AclEntry[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void AuthorizeAction_NullEntries() {
|
||||
AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[0], null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuthorizeAction_EmptyEntries() {
|
||||
Assert.AreEqual(Authorization.Unknown, AclEvaluator.AuthorizeAction("Res", "Action", "U.User", new string[0], new AclEntry[0]), "Wrong auth result");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
498
AclEngine-Tests/AclManagerBaseTests.cs
Normal file
498
AclEngine-Tests/AclManagerBaseTests.cs
Normal file
|
@ -0,0 +1,498 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Rhino.Mocks;
|
||||
|
||||
namespace ScrewTurn.Wiki.AclEngine.Tests {
|
||||
|
||||
[TestFixture]
|
||||
public class AclManagerBaseTests {
|
||||
|
||||
private MockRepository mocks = new MockRepository();
|
||||
|
||||
private AclManagerBase MockAclManager() {
|
||||
AclManagerBase manager = mocks.DynamicMock<AclManagerBase>();
|
||||
|
||||
return manager;
|
||||
}
|
||||
|
||||
private void AssertAclEntriesAreEqual(AclEntry expected, AclEntry actual) {
|
||||
Assert.AreEqual(expected.Resource, actual.Resource, "Wrong resource");
|
||||
Assert.AreEqual(expected.Action, actual.Action, "Wrong action");
|
||||
Assert.AreEqual(expected.Subject, actual.Subject, "Wrong subject");
|
||||
Assert.AreEqual(expected.Value, actual.Value, "Wrong value");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StoreEntry_RetrieveAllEntries() {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
|
||||
Assert.AreEqual(0, manager.TotalEntries, "Wrong initial entry count");
|
||||
Assert.AreEqual(0, manager.RetrieveAllEntries().Length, "Wrong initial entry count");
|
||||
|
||||
Assert.IsTrue(manager.StoreEntry("Res", "Action", "U.User", Value.Grant), "StoreEntry should return true");
|
||||
Assert.IsTrue(manager.StoreEntry("Res", "Action", "G.Group", Value.Deny), "StoreEntry should return true");
|
||||
|
||||
Assert.AreEqual(2, manager.TotalEntries, "Wrong entry count");
|
||||
|
||||
AclEntry[] allEntries = manager.RetrieveAllEntries();
|
||||
Assert.AreEqual(2, allEntries.Length, "Wrong entry count");
|
||||
|
||||
Array.Sort(allEntries, delegate(AclEntry x, AclEntry y) { return x.Subject.CompareTo(y.Subject); });
|
||||
|
||||
AssertAclEntriesAreEqual(new AclEntry("Res", "Action", "G.Group", Value.Deny), allEntries[0]);
|
||||
AssertAclEntriesAreEqual(new AclEntry("Res", "Action", "U.User", Value.Grant), allEntries[1]);
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void StoreEntry_InvalidResource(string s) {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
manager.StoreEntry(s, "Action", "U.User", Value.Grant);
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void StoreEntry_InvalidAction(string a) {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
manager.StoreEntry("Res", a, "U.User", Value.Grant);
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void StoreEntry_InvalidSubject(string s) {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
manager.StoreEntry("Res", "Action", s, Value.Grant);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StoreEntry_Overwrite() {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
|
||||
Assert.IsTrue(manager.StoreEntry("Res", "Action", "U.User", Value.Grant), "StoreEntry should return true");
|
||||
Assert.IsTrue(manager.StoreEntry("Res", "Action", "G.Group", Value.Grant), "StoreEntry should return true");
|
||||
|
||||
// Overwrite with a deny
|
||||
Assert.IsTrue(manager.StoreEntry("Res", "Action", "U.User", Value.Deny), "StoreEntry should return true");
|
||||
|
||||
Assert.AreEqual(2, manager.TotalEntries, "Wrong entry count");
|
||||
|
||||
AclEntry[] allEntries = manager.RetrieveAllEntries();
|
||||
Assert.AreEqual(2, allEntries.Length, "Wrong entry count");
|
||||
|
||||
Array.Sort(allEntries, delegate(AclEntry x, AclEntry y) { return x.Subject.CompareTo(y.Subject); });
|
||||
|
||||
AssertAclEntriesAreEqual(new AclEntry("Res", "Action", "G.Group", Value.Grant), allEntries[0]);
|
||||
AssertAclEntriesAreEqual(new AclEntry("Res", "Action", "U.User", Value.Deny), allEntries[1]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeleteEntry_RetrieveAllEntries() {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
|
||||
Assert.IsTrue(manager.StoreEntry("Res", "Action", "U.User", Value.Grant), "StoreEntry should return true");
|
||||
Assert.IsTrue(manager.StoreEntry("Res", "Action", "G.Group", Value.Deny), "StoreEntry should return true");
|
||||
|
||||
Assert.AreEqual(2, manager.TotalEntries, "Wrong entry count");
|
||||
|
||||
Assert.IsFalse(manager.DeleteEntry("Res1", "Action", "G.Group"), "DeleteEntry should return false");
|
||||
Assert.IsFalse(manager.DeleteEntry("Res", "Action1", "G.Group"), "DeleteEntry should return false");
|
||||
Assert.IsFalse(manager.DeleteEntry("Res", "Action", "G.Group1"), "DeleteEntry should return false");
|
||||
|
||||
Assert.AreEqual(2, manager.TotalEntries, "Wrong entry count");
|
||||
|
||||
Assert.IsTrue(manager.DeleteEntry("Res", "Action", "G.Group"), "DeleteEntry should return true");
|
||||
|
||||
Assert.AreEqual(1, manager.TotalEntries, "Wrong entry count");
|
||||
|
||||
AclEntry[] allEntries = manager.RetrieveAllEntries();
|
||||
Assert.AreEqual(1, allEntries.Length, "Wrong entry count");
|
||||
|
||||
AssertAclEntriesAreEqual(new AclEntry("Res", "Action", "U.User", Value.Grant), allEntries[0]);
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void DeleteEntry_InvalidResource(string r) {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
manager.DeleteEntry(r, "Action", "U.User");
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void DeleteEntry_InvalidAction(string a) {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
manager.DeleteEntry("Res", a, "U.User");
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void DeleteEntry_InvalidSubject(string s) {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
manager.DeleteEntry("Res", "Action", s);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeleteEntriesForResource_RetrieveAllEntries() {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
|
||||
Assert.IsTrue(manager.StoreEntry("Res", "Action", "U.User", Value.Grant), "StoreEntry should return true");
|
||||
Assert.IsTrue(manager.StoreEntry("Res", "Action", "G.Group", Value.Deny), "StoreEntry should return true");
|
||||
Assert.IsTrue(manager.StoreEntry("Res2", "Action2", "G.Group2", Value.Grant), "StoreEntry should return true");
|
||||
Assert.IsTrue(manager.StoreEntry("Res2", "Action3", "U.User", Value.Deny), "StoreEntry should return true");
|
||||
|
||||
Assert.AreEqual(4, manager.TotalEntries, "Wrong entry count");
|
||||
|
||||
Assert.IsFalse(manager.DeleteEntriesForResource("Inexistent"), "DeleteEntriesForResource should return false");
|
||||
|
||||
Assert.AreEqual(4, manager.TotalEntries, "Wrong entry count");
|
||||
|
||||
Assert.IsTrue(manager.DeleteEntriesForResource("Res2"), "DeleteEntriesForResource should return true");
|
||||
|
||||
Assert.AreEqual(2, manager.TotalEntries, "Wrong entry count");
|
||||
|
||||
AclEntry[] allEntries = manager.RetrieveAllEntries();
|
||||
Assert.AreEqual(2, allEntries.Length, "Wrong entry count");
|
||||
|
||||
Array.Sort(allEntries, delegate(AclEntry x, AclEntry y) { return x.Subject.CompareTo(y.Subject); });
|
||||
|
||||
AssertAclEntriesAreEqual(new AclEntry("Res", "Action", "G.Group", Value.Deny), allEntries[0]);
|
||||
AssertAclEntriesAreEqual(new AclEntry("Res", "Action", "U.User", Value.Grant), allEntries[1]);
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void DeleteEntriesForResource_InvalidResource(string r) {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
manager.DeleteEntriesForResource(r);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeleteEntriesForSubject_RetrieveAllEntries() {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
|
||||
Assert.IsTrue(manager.StoreEntry("Res", "Action", "U.User", Value.Grant), "StoreEntry should return true");
|
||||
Assert.IsTrue(manager.StoreEntry("Res", "Action", "G.Group", Value.Deny), "StoreEntry should return true");
|
||||
Assert.IsTrue(manager.StoreEntry("Res2", "Action2", "G.Group2", Value.Grant), "StoreEntry should return true");
|
||||
Assert.IsTrue(manager.StoreEntry("Res2", "Action3", "G.Group2", Value.Deny), "StoreEntry should return true");
|
||||
|
||||
Assert.AreEqual(4, manager.TotalEntries, "Wrong entry count");
|
||||
|
||||
Assert.IsFalse(manager.DeleteEntriesForSubject("I.Inexistent"), "DeleteEntriesForSubject should return false");
|
||||
|
||||
Assert.AreEqual(4, manager.TotalEntries, "Wrong entry count");
|
||||
|
||||
Assert.IsTrue(manager.DeleteEntriesForSubject("G.Group2"), "DeleteEntriesForSubject should return true");
|
||||
|
||||
Assert.AreEqual(2, manager.TotalEntries, "Wrong entry count");
|
||||
|
||||
AclEntry[] allEntries = manager.RetrieveAllEntries();
|
||||
Assert.AreEqual(2, allEntries.Length, "Wrong entry count");
|
||||
|
||||
Array.Sort(allEntries, delegate(AclEntry x, AclEntry y) { return x.Subject.CompareTo(y.Subject); });
|
||||
|
||||
AssertAclEntriesAreEqual(new AclEntry("Res", "Action", "G.Group", Value.Deny), allEntries[0]);
|
||||
AssertAclEntriesAreEqual(new AclEntry("Res", "Action", "U.User", Value.Grant), allEntries[1]);
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void DeleteEntriesForSubject_InvalidSubject(string s) {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
manager.DeleteEntriesForSubject(s);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RetrieveEntriesForResource() {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
|
||||
Assert.IsTrue(manager.StoreEntry("Res", "Action", "U.User", Value.Grant), "StoreEntry should return true");
|
||||
Assert.IsTrue(manager.StoreEntry("Res", "Action", "G.Group", Value.Deny), "StoreEntry should return true");
|
||||
Assert.IsTrue(manager.StoreEntry("Res2", "Action2", "G.Group2", Value.Grant), "StoreEntry should return true");
|
||||
Assert.IsTrue(manager.StoreEntry("Res2", "Action3", "G.Group2", Value.Deny), "StoreEntry should return true");
|
||||
|
||||
Assert.AreEqual(0, manager.RetrieveEntriesForResource("Inexistent").Length, "Wrong result count");
|
||||
|
||||
AclEntry[] allEntries = manager.RetrieveEntriesForResource("Res");
|
||||
Assert.AreEqual(2, allEntries.Length, "Wrong entry count");
|
||||
|
||||
Array.Sort(allEntries, delegate(AclEntry x, AclEntry y) { return x.Subject.CompareTo(y.Subject); });
|
||||
|
||||
AssertAclEntriesAreEqual(new AclEntry("Res", "Action", "G.Group", Value.Deny), allEntries[0]);
|
||||
AssertAclEntriesAreEqual(new AclEntry("Res", "Action", "U.User", Value.Grant), allEntries[1]);
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void RetrieveEntriesForResource_InvalidResource(string r) {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
manager.RetrieveEntriesForResource(r);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RetrieveEntriesForSubject() {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
|
||||
Assert.IsTrue(manager.StoreEntry("Res", "Action", "U.User", Value.Grant), "StoreEntry should return true");
|
||||
Assert.IsTrue(manager.StoreEntry("Res", "Action", "G.Group", Value.Deny), "StoreEntry should return true");
|
||||
Assert.IsTrue(manager.StoreEntry("Res2", "Action2", "G.Group2", Value.Grant), "StoreEntry should return true");
|
||||
Assert.IsTrue(manager.StoreEntry("Res2", "Action3", "G.Group2", Value.Deny), "StoreEntry should return true");
|
||||
|
||||
Assert.AreEqual(0, manager.RetrieveEntriesForSubject("I.Inexistent").Length, "Wrong result count");
|
||||
|
||||
AclEntry[] allEntries = manager.RetrieveEntriesForSubject("G.Group2");
|
||||
Assert.AreEqual(2, allEntries.Length, "Wrong entry count");
|
||||
|
||||
Array.Sort(allEntries, delegate(AclEntry x, AclEntry y) { return x.Action.CompareTo(y.Action); });
|
||||
|
||||
AssertAclEntriesAreEqual(new AclEntry("Res2", "Action2", "G.Group2", Value.Grant), allEntries[0]);
|
||||
AssertAclEntriesAreEqual(new AclEntry("Res2", "Action3", "G.Group2", Value.Deny), allEntries[1]);
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void RetrieveEntriesForSubject_InvalidSubject(string s) {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
manager.RetrieveEntriesForSubject(s);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void InitializeData() {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
|
||||
List<AclEntry> entries = new List<AclEntry>();
|
||||
entries.Add(new AclEntry("Res", "Action", "U.User", Value.Grant));
|
||||
entries.Add(new AclEntry("Res", "Action", "G.Group", Value.Deny));
|
||||
|
||||
manager.InitializeData(entries.ToArray());
|
||||
|
||||
Assert.AreEqual(2, manager.TotalEntries, "Wrong entry count");
|
||||
|
||||
AclEntry[] allEntries = manager.RetrieveAllEntries();
|
||||
Assert.AreEqual(2, allEntries.Length, "Wrong entry count");
|
||||
|
||||
Array.Sort(allEntries, delegate(AclEntry x, AclEntry y) { return x.Subject.CompareTo(y.Subject); });
|
||||
|
||||
AssertAclEntriesAreEqual(new AclEntry("Res", "Action", "G.Group", Value.Deny), allEntries[0]);
|
||||
AssertAclEntriesAreEqual(new AclEntry("Res", "Action", "U.User", Value.Grant), allEntries[1]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void InitializeData_NullData() {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
manager.InitializeData(null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Event_AclChanged_StoreEntry() {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
|
||||
AclEntry entry = new AclEntry("Res", "Action", "U.User", Value.Grant);
|
||||
|
||||
bool invoked = false;
|
||||
manager.AclChanged += delegate(object sender, AclChangedEventArgs e) {
|
||||
invoked = true;
|
||||
Assert.AreEqual(1, e.Entries.Length, "Wrong entry count");
|
||||
AssertAclEntriesAreEqual(entry, e.Entries[0]);
|
||||
Assert.AreEqual(Change.EntryStored, e.Change);
|
||||
};
|
||||
|
||||
manager.StoreEntry(entry.Resource, entry.Action, entry.Subject, entry.Value);
|
||||
|
||||
Assert.IsTrue(invoked, "Store event not invoked");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Event_AclChanged_OverwriteEntry() {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
|
||||
AclEntry entryOld = new AclEntry("Res", "Action", "U.User", Value.Deny);
|
||||
|
||||
AclEntry entryNew = new AclEntry("Res", "Action", "U.User", Value.Grant);
|
||||
|
||||
manager.StoreEntry(entryOld.Resource, entryOld.Action, entryOld.Subject, entryOld.Value);
|
||||
|
||||
bool invokedStore = false;
|
||||
bool invokedDelete = false;
|
||||
manager.AclChanged += delegate(object sender, AclChangedEventArgs e) {
|
||||
if(e.Change == Change.EntryStored) {
|
||||
invokedStore = true;
|
||||
Assert.AreEqual(1, e.Entries.Length, "Wrong entry count");
|
||||
AssertAclEntriesAreEqual(entryNew, e.Entries[0]);
|
||||
Assert.AreEqual(Change.EntryStored, e.Change);
|
||||
}
|
||||
else {
|
||||
invokedDelete = true;
|
||||
Assert.AreEqual(1, e.Entries.Length, "Wrong entry count");
|
||||
AssertAclEntriesAreEqual(entryOld, e.Entries[0]);
|
||||
Assert.AreEqual(Change.EntryDeleted, e.Change);
|
||||
}
|
||||
};
|
||||
|
||||
manager.StoreEntry(entryNew.Resource, entryNew.Action, entryNew.Subject, entryNew.Value);
|
||||
|
||||
Assert.IsTrue(invokedStore, "Store event not invoked");
|
||||
Assert.IsTrue(invokedDelete, "Delete event not invoked");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Event_AclChanged_DeleteEntry() {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
|
||||
AclEntry entry = new AclEntry("Res", "Action", "U.User", Value.Grant);
|
||||
|
||||
manager.StoreEntry(entry.Resource, entry.Action, entry.Subject, entry.Value);
|
||||
|
||||
bool invokedDelete = false;
|
||||
manager.AclChanged += delegate(object sender, AclChangedEventArgs e) {
|
||||
invokedDelete = true;
|
||||
Assert.AreEqual(1, e.Entries.Length, "Wrong entry count");
|
||||
AssertAclEntriesAreEqual(entry, e.Entries[0]);
|
||||
Assert.AreEqual(Change.EntryDeleted, e.Change, "Wrong change");
|
||||
};
|
||||
|
||||
manager.DeleteEntry(entry.Resource, entry.Action, entry.Subject);
|
||||
|
||||
Assert.IsTrue(invokedDelete, "Delete event not invoked");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Event_AclChanged_DeleteEntriesForResource() {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
|
||||
AclEntry entry = new AclEntry("Res", "Action", "U.User", Value.Grant);
|
||||
|
||||
manager.StoreEntry(entry.Resource, entry.Action, entry.Subject, entry.Value);
|
||||
manager.StoreEntry("Res2", "Action", "G.Group", Value.Deny);
|
||||
|
||||
bool invokedDelete = false;
|
||||
manager.AclChanged += delegate(object sender, AclChangedEventArgs e) {
|
||||
invokedDelete = true;
|
||||
Assert.AreEqual(1, e.Entries.Length, "Wrong entry count");
|
||||
AssertAclEntriesAreEqual(entry, e.Entries[0]);
|
||||
Assert.AreEqual(Change.EntryDeleted, e.Change, "Wrong change");
|
||||
};
|
||||
|
||||
manager.DeleteEntriesForResource(entry.Resource);
|
||||
|
||||
Assert.IsTrue(invokedDelete, "Delete event not invoked");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Event_AclChanged_DeleteEntriesForSubject() {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
|
||||
AclEntry entry = new AclEntry("Res", "Action", "U.User", Value.Grant);
|
||||
|
||||
manager.StoreEntry(entry.Resource, entry.Action, entry.Subject, entry.Value);
|
||||
manager.StoreEntry("Res2", "Action", "G.Group", Value.Deny);
|
||||
|
||||
bool invokedDelete = false;
|
||||
manager.AclChanged += delegate(object sender, AclChangedEventArgs e) {
|
||||
invokedDelete = true;
|
||||
Assert.AreEqual(1, e.Entries.Length, "Wrong entry count");
|
||||
AssertAclEntriesAreEqual(entry, e.Entries[0]);
|
||||
Assert.AreEqual(Change.EntryDeleted, e.Change, "Wrong change");
|
||||
};
|
||||
|
||||
manager.DeleteEntriesForSubject(entry.Subject);
|
||||
|
||||
Assert.IsTrue(invokedDelete, "Delete event not invoked");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RenameResource() {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
|
||||
Assert.IsFalse(manager.RenameResource("Res", "Res_Renamed"), "RenameResource should return false");
|
||||
|
||||
AclEntry entry1 = new AclEntry("Res", "Action", "U.User", Value.Grant);
|
||||
AclEntry newEntry1 = new AclEntry("Res_Renamed", "Action", "U.User", Value.Grant);
|
||||
AclEntry entry2 = new AclEntry("Res", "Action2", "U.User2", Value.Deny);
|
||||
AclEntry newEntry2 = new AclEntry("Res_Renamed", "Action2", "U.User", Value.Deny);
|
||||
|
||||
manager.StoreEntry(entry1.Resource, entry1.Action, entry1.Subject, entry1.Value);
|
||||
manager.StoreEntry(entry2.Resource, entry2.Action, entry2.Subject, entry2.Value);
|
||||
manager.StoreEntry("Res2", "Action", "G.Group", Value.Deny);
|
||||
|
||||
bool invokedDelete1 = false;
|
||||
bool invokedStore1 = false;
|
||||
bool invokedDelete2 = false;
|
||||
bool invokedStore2 = false;
|
||||
manager.AclChanged += delegate(object sender, AclChangedEventArgs e) {
|
||||
if(e.Change == Change.EntryDeleted) {
|
||||
Assert.AreEqual(1, e.Entries.Length, "Wrong entry count");
|
||||
Assert.AreEqual("Res", e.Entries[0].Resource, "Wrong resource");
|
||||
|
||||
if(e.Entries[0].Action == entry1.Action) invokedDelete1 = true;
|
||||
if(e.Entries[0].Action == entry2.Action) invokedDelete2 = true;
|
||||
}
|
||||
else {
|
||||
Assert.AreEqual(1, e.Entries.Length, "Wrong entry count");
|
||||
Assert.AreEqual("Res_Renamed", e.Entries[0].Resource, "Wrong resource");
|
||||
|
||||
if(e.Entries[0].Action == entry1.Action) invokedStore1 = true;
|
||||
if(e.Entries[0].Action == entry2.Action) invokedStore2 = true;
|
||||
}
|
||||
};
|
||||
|
||||
Assert.IsTrue(manager.RenameResource("Res", "Res_Renamed"), "RenameResource should return true");
|
||||
|
||||
Assert.IsTrue(invokedDelete1, "Delete event 1 not invoked");
|
||||
Assert.IsTrue(invokedStore1, "Store event 1 not invoked");
|
||||
Assert.IsTrue(invokedDelete2, "Delete event 2 not invoked");
|
||||
Assert.IsTrue(invokedStore2, "Store event 2 not invoked");
|
||||
|
||||
AclEntry[] entries = manager.RetrieveAllEntries();
|
||||
|
||||
Assert.AreEqual(3, entries.Length, "Wrong entry count");
|
||||
Array.Sort(entries, delegate(AclEntry x, AclEntry y) { return x.Resource.CompareTo(y.Resource); });
|
||||
|
||||
Assert.AreEqual("Res_Renamed", entries[0].Resource, "Wrong resource");
|
||||
if(entries[0].Value == Value.Grant) {
|
||||
Assert.AreEqual("Action", entries[0].Action, "Wrong action");
|
||||
Assert.AreEqual("U.User", entries[0].Subject, "Wrong subject");
|
||||
}
|
||||
else {
|
||||
Assert.AreEqual("Action2", entries[0].Action, "Wrong action");
|
||||
Assert.AreEqual("U.User2", entries[0].Subject, "Wrong subject");
|
||||
}
|
||||
|
||||
Assert.AreEqual("Res_Renamed", entries[1].Resource, "Wrong resource");
|
||||
if(entries[1].Value == Value.Grant) {
|
||||
Assert.AreEqual("Action", entries[1].Action, "Wrong action");
|
||||
Assert.AreEqual("U.User", entries[1].Subject, "Wrong subject");
|
||||
}
|
||||
else {
|
||||
Assert.AreEqual("Action2", entries[1].Action, "Wrong action");
|
||||
Assert.AreEqual("U.User2", entries[1].Subject, "Wrong subject");
|
||||
}
|
||||
|
||||
Assert.AreEqual("Res2", entries[2].Resource, "Wrong resource");
|
||||
Assert.AreEqual("Action", entries[2].Action, "Wrong action");
|
||||
Assert.AreEqual("G.Group", entries[2].Subject, "Wrong subject");
|
||||
Assert.AreEqual(Value.Deny, entries[2].Value, "Wrong value");
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void RenameResource_InvalidResource(string r) {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
|
||||
manager.RenameResource(r, "new_name");
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void RenameResource_InvalidNewName(string n) {
|
||||
AclManagerBase manager = MockAclManager();
|
||||
|
||||
manager.RenameResource("res", n);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
18
AclEngine-Tests/Properties/AssemblyInfo.cs
Normal file
18
AclEngine-Tests/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("ScrewTurn Wiki ACL Engine Tests")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("aa6f62d5-284a-443f-a5c1-d75fb638a8b7")]
|
61
AclEngine/AclChangedEventArgs.cs
Normal file
61
AclEngine/AclChangedEventArgs.cs
Normal file
|
@ -0,0 +1,61 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace ScrewTurn.Wiki.AclEngine {
|
||||
|
||||
/// <summary>
|
||||
/// Contains arguments for the <see cref="IAclManager.AclChanged" /> event.
|
||||
/// </summary>
|
||||
public class AclChangedEventArgs : EventArgs {
|
||||
|
||||
private AclEntry[] entries;
|
||||
private Change change;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:AclChangedEventArgs" /> class.
|
||||
/// </summary>
|
||||
/// <param name="entries">The entries that changed.</param>
|
||||
/// <param name="change">The change.</param>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="entries"/> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="entries"/> is empty.</exception>
|
||||
public AclChangedEventArgs(AclEntry[] entries, Change change) {
|
||||
if(entries == null) throw new ArgumentNullException("entry");
|
||||
if(entries.Length == 0) throw new ArgumentException("Entries cannot be empty", "entries");
|
||||
|
||||
this.entries = entries;
|
||||
this.change = change;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the entries that changed.
|
||||
/// </summary>
|
||||
public AclEntry[] Entries {
|
||||
get { return entries; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the change.
|
||||
/// </summary>
|
||||
public Change Change {
|
||||
get { return change; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists legal changes for ACL entries.
|
||||
/// </summary>
|
||||
public enum Change {
|
||||
/// <summary>
|
||||
/// An entry is stored.
|
||||
/// </summary>
|
||||
EntryStored,
|
||||
/// <summary>
|
||||
/// An entry is deleted.
|
||||
/// </summary>
|
||||
EntryDeleted
|
||||
}
|
||||
|
||||
}
|
63
AclEngine/AclEngine.csproj
Normal file
63
AclEngine/AclEngine.csproj
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.30729</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{44B0F4C1-8CDC-4272-B2A2-C0AF689CEB81}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>ScrewTurn.Wiki.AclEngine</RootNamespace>
|
||||
<AssemblyName>ScrewTurn.Wiki.AclEngine</AssemblyName>
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<DocumentationFile>bin\Debug\ScrewTurn.Wiki.AclEngine.XML</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<DocumentationFile>bin\Release\ScrewTurn.Wiki.AclEngine.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\AssemblyVersion.cs">
|
||||
<Link>AssemblyVersion.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="AclEntry.cs" />
|
||||
<Compile Include="AclChangedEventArgs.cs" />
|
||||
<Compile Include="AclEvaluator.cs" />
|
||||
<Compile Include="AclManagerBase.cs" />
|
||||
<Compile Include="AclStorerBase.cs" />
|
||||
<Compile Include="IAclManager.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
153
AclEngine/AclEntry.cs
Normal file
153
AclEngine/AclEntry.cs
Normal file
|
@ -0,0 +1,153 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace ScrewTurn.Wiki.AclEngine {
|
||||
|
||||
/// <summary>
|
||||
/// Represents an ACL Entry.
|
||||
/// </summary>
|
||||
public class AclEntry {
|
||||
|
||||
/// <summary>
|
||||
/// The full control action.
|
||||
/// </summary>
|
||||
public const string FullControlAction = "*";
|
||||
|
||||
/// <summary>
|
||||
/// The controlled resource.
|
||||
/// </summary>
|
||||
private string resource;
|
||||
/// <summary>
|
||||
/// The controlled action on the resource.
|
||||
/// </summary>
|
||||
private string action;
|
||||
/// <summary>
|
||||
/// The subject whose access to the resource/action is controlled.
|
||||
/// </summary>
|
||||
private string subject;
|
||||
/// <summary>
|
||||
/// The entry value.
|
||||
/// </summary>
|
||||
private Value value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:AclEntry" /> class.
|
||||
/// </summary>
|
||||
/// <param name="resource">The controlled resource.</param>
|
||||
/// <param name="action">The controlled action on the resource.</param>
|
||||
/// <param name="subject">The subject whose access to the resource/action is controlled.</param>
|
||||
/// <param name="value">The entry value.</param>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="resource"/>, <paramref name="action"/> or <paramref name="subject"/> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="resource"/>, <paramref name="action"/> or <paramref name="subject"/> are empty.</exception>
|
||||
public AclEntry(string resource, string action, string subject, Value value) {
|
||||
if(resource == null) throw new ArgumentNullException("resource");
|
||||
if(resource.Length == 0) throw new ArgumentException("Resource cannot be empty", "resource");
|
||||
if(action == null) throw new ArgumentNullException("action");
|
||||
if(action.Length == 0) throw new ArgumentException("Action cannot be empty", "action");
|
||||
if(subject == null) throw new ArgumentNullException("subject");
|
||||
if(subject.Length == 0) throw new ArgumentException("Subject cannot be empty", "subject");
|
||||
|
||||
this.resource = resource;
|
||||
this.action = action;
|
||||
this.subject = subject;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the controlled resource.
|
||||
/// </summary>
|
||||
public string Resource {
|
||||
get { return resource; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the controlled action on the resource.
|
||||
/// </summary>
|
||||
public string Action {
|
||||
get { return action; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the subject of the entry.
|
||||
/// </summary>
|
||||
public string Subject {
|
||||
get { return subject; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the entry.
|
||||
/// </summary>
|
||||
public Value Value {
|
||||
get { return value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string representation of the current object.
|
||||
/// </summary>
|
||||
/// <returns>The string representation.</returns>
|
||||
public override string ToString() {
|
||||
return resource + "->" + action + ": " + subject + " (" + value.ToString() + ")";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a hash code for the current object.
|
||||
/// </summary>
|
||||
/// <returns>The hash code.</returns>
|
||||
public override int GetHashCode() {
|
||||
return base.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this object equals another (by value).
|
||||
/// </summary>
|
||||
/// <param name="obj">The other object.</param>
|
||||
/// <returns><c>true</c> if this object equals <b>obj</b>, <c>false</c> otherwise.</returns>
|
||||
public override bool Equals(object obj) {
|
||||
AclEntry other = obj as AclEntry;
|
||||
if(other != null) return Equals(other);
|
||||
else return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this instance equals another (by value).
|
||||
/// </summary>
|
||||
/// <param name="other">The other instance.</param>
|
||||
/// <returns><c>true</c> if this instance equals <b>other</b>, <c>false</c> otherwise.</returns>
|
||||
public bool Equals(AclEntry other) {
|
||||
if(object.ReferenceEquals(other, null)) return false;
|
||||
else return resource == other.Resource &&
|
||||
action == other.Action && subject == other.Subject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether two instances of <see cref="T:AclEntry" /> are equal (by value).
|
||||
/// </summary>
|
||||
/// <param name="x">The first instance.</param>
|
||||
/// <param name="y">The second instance.</param>
|
||||
/// <returns><c>true</c> if <b>x</b> equals <b>y</b>, <c>false</c> otherwise.</returns>
|
||||
public static bool Equals(AclEntry x, AclEntry y) {
|
||||
if(object.ReferenceEquals(x, null) && !object.ReferenceEquals(x, null)) return false;
|
||||
if(!object.ReferenceEquals(x, null) && object.ReferenceEquals(x, null)) return false;
|
||||
if(object.ReferenceEquals(x, null) && object.ReferenceEquals(x, null)) return true;
|
||||
return x.Equals(y);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists legal ACL Entry values.
|
||||
/// </summary>
|
||||
public enum Value {
|
||||
/// <summary>
|
||||
/// Deny the action.
|
||||
/// </summary>
|
||||
Deny = 0,
|
||||
/// <summary>
|
||||
/// Grant the action.
|
||||
/// </summary>
|
||||
Grant = 1
|
||||
}
|
||||
|
||||
}
|
129
AclEngine/AclEvaluator.cs
Normal file
129
AclEngine/AclEvaluator.cs
Normal file
|
@ -0,0 +1,129 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace ScrewTurn.Wiki.AclEngine {
|
||||
|
||||
/// <summary>
|
||||
/// Implements tools for evaluating permissions.
|
||||
/// </summary>
|
||||
public static class AclEvaluator {
|
||||
|
||||
/// <summary>
|
||||
/// Decides whether a user, member of some groups, is authorized to perform an action on a resource.
|
||||
/// </summary>
|
||||
/// <param name="resource">The resource.</param>
|
||||
/// <param name="action">The action on the resource.</param>
|
||||
/// <param name="user">The user, in the form 'U.Name'.</param>
|
||||
/// <param name="groups">The groups the user is member of, in the form 'G.Name'.</param>
|
||||
/// <param name="entries">The available ACL entries for the resource.</param>
|
||||
/// <returns>The positive, negative, or indeterminate result.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="resource"/>, <paramref name="action"/>, <paramref name="user"/>, <paramref name="groups"/> or <paramref name="entries"/> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="resource"/>, <paramref name="action"/>, <paramref name="user"/> are empty, or if <paramref name="action"/> equals <see cref="AclEntry.FullControlAction"/>.</exception>
|
||||
public static Authorization AuthorizeAction(string resource, string action, string user, string[] groups, AclEntry[] entries) {
|
||||
if(resource == null) throw new ArgumentNullException("resource");
|
||||
if(resource.Length == 0) throw new ArgumentException("Resource cannot be empty", "resource");
|
||||
if(action == null) throw new ArgumentNullException("action");
|
||||
if(action.Length == 0) throw new ArgumentException("Action cannot be empty", "action");
|
||||
if(action == AclEntry.FullControlAction) throw new ArgumentException("Action cannot be the FullControl flag", "action");
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
if(user.Length == 0) throw new ArgumentException("User cannot be empty", "user");
|
||||
if(groups == null) throw new ArgumentNullException("groups");
|
||||
if(entries == null) throw new ArgumentNullException("entries");
|
||||
|
||||
// Simple ACL model
|
||||
// Sort entries so that FullControl ones are at the bottom
|
||||
// First look for an entry specific for the user
|
||||
// If not found, look for a group that denies the permission
|
||||
|
||||
AclEntry[] sortedEntries = new AclEntry[entries.Length];
|
||||
Array.Copy(entries, sortedEntries, entries.Length);
|
||||
|
||||
Array.Sort(sortedEntries, delegate(AclEntry x, AclEntry y) {
|
||||
return x.Action.CompareTo(y.Action);
|
||||
});
|
||||
Array.Reverse(sortedEntries);
|
||||
|
||||
foreach(AclEntry entry in sortedEntries) {
|
||||
if(entry.Resource == resource && (entry.Action == action || entry.Action == AclEntry.FullControlAction) && entry.Subject == user) {
|
||||
if(entry.Value == Value.Grant) return Authorization.Granted;
|
||||
else if(entry.Value == Value.Deny) return Authorization.Denied;
|
||||
else throw new NotSupportedException("Entry value not supported");
|
||||
}
|
||||
}
|
||||
|
||||
// For each group, a decision is made
|
||||
Dictionary<string, bool> groupFullControlGrant = new Dictionary<string, bool>();
|
||||
Dictionary<string, bool> groupExplicitGrant = new Dictionary<string, bool>();
|
||||
Dictionary<string, bool> groupFullControlDeny = new Dictionary<string, bool>();
|
||||
|
||||
foreach(string group in groups) {
|
||||
foreach(AclEntry entry in entries) {
|
||||
|
||||
if(entry.Resource == resource && entry.Subject == group) {
|
||||
if(!groupFullControlGrant.ContainsKey(group)) {
|
||||
groupFullControlGrant.Add(group, false);
|
||||
groupExplicitGrant.Add(group, false);
|
||||
groupFullControlDeny.Add(group, false);
|
||||
}
|
||||
|
||||
if(entry.Action == action) {
|
||||
// Explicit action
|
||||
if(entry.Value == Value.Grant) {
|
||||
// An explicit grant only wins if there are no other explicit deny
|
||||
groupExplicitGrant[group] = true;
|
||||
}
|
||||
else if(entry.Value == Value.Deny) {
|
||||
// An explicit deny wins over all other entries
|
||||
return Authorization.Denied;
|
||||
}
|
||||
}
|
||||
else if(entry.Action == AclEntry.FullControlAction) {
|
||||
// Full control, lower priority
|
||||
if(entry.Value == Value.Deny) {
|
||||
groupFullControlDeny[group] = true;
|
||||
}
|
||||
else if(entry.Value == Value.Grant) {
|
||||
groupFullControlGrant[group] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Any explicit grant found at this step wins, because all explicit deny have been processed previously
|
||||
bool tentativeGrant = false;
|
||||
bool tentativeDeny = false;
|
||||
foreach(string group in groupFullControlGrant.Keys) {
|
||||
if(groupExplicitGrant[group]) return Authorization.Granted;
|
||||
|
||||
if(groupFullControlGrant[group] && !groupFullControlDeny[group]) tentativeGrant = true;
|
||||
if(!groupFullControlGrant[group] && groupFullControlDeny[group]) tentativeDeny = true;
|
||||
}
|
||||
if(tentativeGrant && !tentativeDeny) return Authorization.Granted;
|
||||
else if(tentativeDeny) return Authorization.Denied;
|
||||
else return Authorization.Unknown;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists legal authorization values.
|
||||
/// </summary>
|
||||
public enum Authorization {
|
||||
/// <summary>
|
||||
/// Authorization granted.
|
||||
/// </summary>
|
||||
Granted,
|
||||
/// <summary>
|
||||
/// Authorization denied.
|
||||
/// </summary>
|
||||
Denied,
|
||||
/// <summary>
|
||||
/// No information available.
|
||||
/// </summary>
|
||||
Unknown
|
||||
}
|
||||
|
||||
}
|
287
AclEngine/AclManagerBase.cs
Normal file
287
AclEngine/AclManagerBase.cs
Normal file
|
@ -0,0 +1,287 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace ScrewTurn.Wiki.AclEngine {
|
||||
|
||||
/// <summary>
|
||||
/// Implements a base class for an ACL Manager.
|
||||
/// </summary>
|
||||
/// <remarks>All instance and static members are <b>thread-safe</b>.</remarks>
|
||||
public abstract class AclManagerBase : IAclManager {
|
||||
|
||||
private List<AclEntry> entries;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:AclManagerBase" /> abstract class.
|
||||
/// </summary>
|
||||
public AclManagerBase() {
|
||||
entries = new List<AclEntry>(100);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the invokation of <see cref="IAclManager.AclChanged" /> event.
|
||||
/// </summary>
|
||||
/// <param name="entries">The changed entries.</param>
|
||||
/// <param name="change">The change.</param>
|
||||
protected void OnAclChanged(AclEntry[] entries, Change change) {
|
||||
if(AclChanged != null) {
|
||||
AclChanged(this, new AclChangedEventArgs(entries, change));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores a new ACL entry.
|
||||
/// </summary>
|
||||
/// <param name="resource">The controlled resource.</param>
|
||||
/// <param name="action">The action on the controlled resource.</param>
|
||||
/// <param name="subject">The subject whose access to the resource/action is controlled.</param>
|
||||
/// <param name="value">The value of the entry.</param>
|
||||
/// <returns><c>true</c> if the entry is stored, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="resource"/>, <paramref name="action"/> or <paramref name="subject"/> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="resource"/>, <paramref name="action"/> or <paramref name="subject"/> are empty.</exception>
|
||||
public bool StoreEntry(string resource, string action, string subject, Value value) {
|
||||
if(resource == null) throw new ArgumentNullException("resource");
|
||||
if(resource.Length == 0) throw new ArgumentException("Resource cannot be empty", "resource");
|
||||
if(action == null) throw new ArgumentNullException("action");
|
||||
if(action.Length == 0) throw new ArgumentException("Action cannot be empty", "action");
|
||||
if(subject == null) throw new ArgumentNullException("subject");
|
||||
if(subject.Length == 0) throw new ArgumentException("Subject cannot be empty", "subject");
|
||||
|
||||
AclEntry result = new AclEntry(resource, action, subject, value);
|
||||
|
||||
lock(this) {
|
||||
int index = entries.FindIndex(delegate(AclEntry x) { return AclEntry.Equals(x, result); });
|
||||
if(index >= 0) {
|
||||
AclEntry removed = entries[index];
|
||||
entries.RemoveAt(index);
|
||||
OnAclChanged(new AclEntry[] { removed }, Change.EntryDeleted);
|
||||
}
|
||||
entries.Add(result);
|
||||
OnAclChanged(new AclEntry[] { result }, Change.EntryStored);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes an ACL entry.
|
||||
/// </summary>
|
||||
/// <param name="resource">The controlled resource.</param>
|
||||
/// <param name="action">The action on the controlled resource.</param>
|
||||
/// <param name="subject">The subject whose access to the resource/action is controlled.</param>
|
||||
/// <returns><c>true</c> if the entry is deleted, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="resource"/>, <paramref name="action"/> or <paramref name="subject"/> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="resource"/>, <paramref name="action"/> or <paramref name="subject"/> are empty.</exception>
|
||||
public bool DeleteEntry(string resource, string action, string subject) {
|
||||
if(resource == null) throw new ArgumentNullException("resource");
|
||||
if(resource.Length == 0) throw new ArgumentException("Resource cannot be empty", "resource");
|
||||
if(action == null) throw new ArgumentNullException("action");
|
||||
if(action.Length == 0) throw new ArgumentException("Action cannot be empty", "action");
|
||||
if(subject == null) throw new ArgumentNullException("subject");
|
||||
if(subject.Length == 0) throw new ArgumentException("Subject cannot be empty", "subject");
|
||||
|
||||
AclEntry result = new AclEntry(resource, action, subject, Value.Deny);
|
||||
|
||||
lock(this) {
|
||||
int index = entries.FindIndex(delegate(AclEntry x) { return AclEntry.Equals(x, result); });
|
||||
if(index >= 0) {
|
||||
AclEntry entry = entries[index];
|
||||
entries.RemoveAt(index);
|
||||
OnAclChanged(new AclEntry[] { entry }, Change.EntryDeleted);
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all the ACL entries for a resource.
|
||||
/// </summary>
|
||||
/// <param name="resource">The controlled resource.</param>
|
||||
/// <returns><c>true</c> if the entries are deleted, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="resource"/> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="resource"/> is empty.</exception>
|
||||
public bool DeleteEntriesForResource(string resource) {
|
||||
if(resource == null) throw new ArgumentNullException("resource");
|
||||
if(resource.Length == 0) throw new ArgumentException("Resource cannot be empty", "resource");
|
||||
|
||||
lock(this) {
|
||||
List<int> indexesToRemove = new List<int>(30);
|
||||
List<AclEntry> entriesToRemove = new List<AclEntry>(30);
|
||||
for(int i = 0; i < entries.Count; i++) {
|
||||
if(entries[i].Resource == resource) {
|
||||
indexesToRemove.Add(i);
|
||||
entriesToRemove.Add(entries[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if(indexesToRemove.Count > 0) {
|
||||
// Work in opposite direction to preserve smaller indexes
|
||||
for(int i = indexesToRemove.Count - 1; i >= 0; i--) {
|
||||
entries.RemoveAt(indexesToRemove[i]);
|
||||
}
|
||||
|
||||
OnAclChanged(entriesToRemove.ToArray(), Change.EntryDeleted);
|
||||
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all the ACL entries for a subject.
|
||||
/// </summary>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <returns><c>true</c> if the entries are deleted, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="subject"/> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="subject"/> is empty.</exception>
|
||||
public bool DeleteEntriesForSubject(string subject) {
|
||||
if(subject == null) throw new ArgumentNullException("subject");
|
||||
if(subject.Length == 0) throw new ArgumentException("Subject cannot be empty", "subject");
|
||||
|
||||
lock(this) {
|
||||
List<int> indexesToRemove = new List<int>(30);
|
||||
List<AclEntry> entriesToRemove = new List<AclEntry>(30);
|
||||
for(int i = 0; i < entries.Count; i++) {
|
||||
if(entries[i].Subject == subject) {
|
||||
indexesToRemove.Add(i);
|
||||
entriesToRemove.Add(entries[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if(indexesToRemove.Count > 0) {
|
||||
// Work in opposite direction to preserve smaller indexes
|
||||
for(int i = indexesToRemove.Count - 1; i >= 0; i--) {
|
||||
entries.RemoveAt(indexesToRemove[i]);
|
||||
}
|
||||
|
||||
OnAclChanged(entriesToRemove.ToArray(), Change.EntryDeleted);
|
||||
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renames a resource.
|
||||
/// </summary>
|
||||
/// <param name="resource">The resource.</param>
|
||||
/// <param name="newName">The new name of the resource.</param>
|
||||
/// <returns><c>true</c> if the resource is renamed, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="resource"/> or <paramref name="newName"/> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="resource"/> or <paramref name="newName"/> are empty.</exception>
|
||||
public bool RenameResource(string resource, string newName) {
|
||||
if(resource == null) throw new ArgumentNullException("resource");
|
||||
if(resource.Length == 0) throw new ArgumentException("Resource cannot be empty", "resource");
|
||||
|
||||
if(newName == null) throw new ArgumentNullException("newName");
|
||||
if(newName.Length == 0) throw new ArgumentException("New Name cannot be empty", "newName");
|
||||
|
||||
lock(this) {
|
||||
AclEntry[] entries = RetrieveEntriesForResource(resource);
|
||||
bool renamed = false;
|
||||
|
||||
foreach(AclEntry entry in entries) {
|
||||
bool deleted = DeleteEntry(entry.Resource, entry.Action, entry.Subject);
|
||||
if(deleted) {
|
||||
bool stored = StoreEntry(newName, entry.Action, entry.Subject, entry.Value);
|
||||
if(stored) renamed = true;
|
||||
else return false;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
return renamed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all the ACL entries for a resource.
|
||||
/// </summary>
|
||||
/// <returns>The entries.</returns>
|
||||
public AclEntry[] RetrieveAllEntries() {
|
||||
lock(this) {
|
||||
return entries.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all the ACL entries for a resource.
|
||||
/// </summary>
|
||||
/// <param name="resource">The resource.</param>
|
||||
/// <returns>The entries.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="resource"/> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="resource"/> is empty.</exception>
|
||||
public AclEntry[] RetrieveEntriesForResource(string resource) {
|
||||
if(resource == null) throw new ArgumentNullException("resource");
|
||||
if(resource.Length == 0) throw new ArgumentException("Resource cannot be empty", "resource");
|
||||
|
||||
lock(this) {
|
||||
List<AclEntry> result = new List<AclEntry>(10);
|
||||
|
||||
foreach(AclEntry e in entries) {
|
||||
if(e.Resource == resource) result.Add(e);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all the ACL entries for a subject.
|
||||
/// </summary>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <returns>The entries.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="subject"/> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="subject"/> is empty.</exception>
|
||||
public AclEntry[] RetrieveEntriesForSubject(string subject) {
|
||||
if(subject == null) throw new ArgumentNullException("subject");
|
||||
if(subject.Length == 0) throw new ArgumentException("Subject cannot be empty", "subject");
|
||||
|
||||
lock(this) {
|
||||
List<AclEntry> result = new List<AclEntry>(10);
|
||||
|
||||
foreach(AclEntry e in entries) {
|
||||
if(e.Subject == subject) result.Add(e);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the manager data.
|
||||
/// </summary>
|
||||
/// <param name="entries">The ACL entries.</param>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="entries"/> is <c>null</c>.</exception>
|
||||
public void InitializeData(AclEntry[] entries) {
|
||||
if(entries == null) throw new ArgumentNullException("entries");
|
||||
|
||||
lock(this) {
|
||||
this.entries = new List<AclEntry>(entries);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of ACL entries.
|
||||
/// </summary>
|
||||
public int TotalEntries {
|
||||
get {
|
||||
lock(this) {
|
||||
return entries.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when an ACL entry is stored or deleted.
|
||||
/// </summary>
|
||||
public event EventHandler<AclChangedEventArgs> AclChanged;
|
||||
|
||||
}
|
||||
|
||||
}
|
107
AclEngine/AclStorerBase.cs
Normal file
107
AclEngine/AclStorerBase.cs
Normal file
|
@ -0,0 +1,107 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace ScrewTurn.Wiki.AclEngine {
|
||||
|
||||
/// <summary>
|
||||
/// Implements a base class for an ACL Storer.
|
||||
/// </summary>
|
||||
public abstract class AclStorerBase : IDisposable {
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the object was disposed.
|
||||
/// </summary>
|
||||
protected bool disposed = false;
|
||||
|
||||
/// <summary>
|
||||
/// The instance of the ACL Manager to handle.
|
||||
/// </summary>
|
||||
protected IAclManager aclManager;
|
||||
|
||||
/// <summary>
|
||||
/// The event handler for the <see cref="IAclManager.AclChanged" /> event.
|
||||
/// </summary>
|
||||
protected EventHandler<AclChangedEventArgs> aclChangedHandler;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:AclStorerBase" /> abstract class.
|
||||
/// </summary>
|
||||
/// <param name="aclManager">The instance of the ACL Manager to handle.</param>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="aclManager"/> is <c>null</c>.</exception>
|
||||
public AclStorerBase(IAclManager aclManager) {
|
||||
if(aclManager == null) throw new ArgumentNullException("aclManager");
|
||||
|
||||
this.aclManager = aclManager;
|
||||
|
||||
aclChangedHandler = new EventHandler<AclChangedEventArgs>(aclManager_AclChanged);
|
||||
|
||||
this.aclManager.AclChanged += aclChangedHandler;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the <see cref="IAclManager.AclChanged" /> event.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The event arguments.</param>
|
||||
private void aclManager_AclChanged(object sender, AclChangedEventArgs e) {
|
||||
if(e.Change == Change.EntryDeleted) DeleteEntries(e.Entries);
|
||||
else if(e.Change == Change.EntryStored) StoreEntries(e.Entries);
|
||||
else throw new NotSupportedException("Change type not supported");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads data from storage.
|
||||
/// </summary>
|
||||
/// <returns>The loaded ACL entries.</returns>
|
||||
protected abstract AclEntry[] LoadDataInternal();
|
||||
|
||||
/// <summary>
|
||||
/// Deletes some entries.
|
||||
/// </summary>
|
||||
/// <param name="entries">The entries to delete.</param>
|
||||
protected abstract void DeleteEntries(AclEntry[] entries);
|
||||
|
||||
/// <summary>
|
||||
/// Stores some entries.
|
||||
/// </summary>
|
||||
/// <param name="entries">The entries to store.</param>
|
||||
protected abstract void StoreEntries(AclEntry[] entries);
|
||||
|
||||
/// <summary>
|
||||
/// Loads the data and injects it in the instance of <see cref="T:IAclManager" />.
|
||||
/// </summary>
|
||||
public void LoadData() {
|
||||
lock(this) {
|
||||
AclEntry[] entries = LoadDataInternal();
|
||||
aclManager.InitializeData(entries);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of the ACL Manager.
|
||||
/// </summary>
|
||||
public IAclManager AclManager {
|
||||
get {
|
||||
lock(this) {
|
||||
return aclManager;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the current object.
|
||||
/// </summary>
|
||||
public void Dispose() {
|
||||
lock(this) {
|
||||
if(!disposed) {
|
||||
disposed = true;
|
||||
aclManager.AclChanged -= aclChangedHandler;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
107
AclEngine/IAclManager.cs
Normal file
107
AclEngine/IAclManager.cs
Normal file
|
@ -0,0 +1,107 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace ScrewTurn.Wiki.AclEngine {
|
||||
|
||||
/// <summary>
|
||||
/// Defines an interface for an ACL manager.
|
||||
/// </summary>
|
||||
public interface IAclManager {
|
||||
|
||||
/// <summary>
|
||||
/// Stores a new ACL entry.
|
||||
/// </summary>
|
||||
/// <param name="resource">The controlled resource.</param>
|
||||
/// <param name="action">The action on the controlled resource.</param>
|
||||
/// <param name="subject">The subject whose access to the resource/action is controlled.</param>
|
||||
/// <param name="value">The value of the entry.</param>
|
||||
/// <returns><c>true</c> if the entry is stored, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="resource"/>, <paramref name="action"/> or <paramref name="subject"/> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="resource"/>, <paramref name="action"/> or <paramref name="subject"/> are empty.</exception>
|
||||
bool StoreEntry(string resource, string action, string subject, Value value);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes an ACL entry.
|
||||
/// </summary>
|
||||
/// <param name="resource">The controlled resource.</param>
|
||||
/// <param name="action">The action on the controlled resource.</param>
|
||||
/// <param name="subject">The subject whose access to the resource/action is controlled.</param>
|
||||
/// <returns><c>true</c> if the entry is deleted, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="resource"/>, <paramref name="action"/> or <paramref name="subject"/> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="resource"/>, <paramref name="action"/> or <paramref name="subject"/> are empty.</exception>
|
||||
bool DeleteEntry(string resource, string action, string subject);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all the ACL entries for a resource.
|
||||
/// </summary>
|
||||
/// <param name="resource">The controlled resource.</param>
|
||||
/// <returns><c>true</c> if the entries are deleted, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="resource"/> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="resource"/> is empty.</exception>
|
||||
bool DeleteEntriesForResource(string resource);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all the ACL entries for a subject.
|
||||
/// </summary>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <returns><c>true</c> if the entries are deleted, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="subject"/> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="subject"/> is empty.</exception>
|
||||
bool DeleteEntriesForSubject(string subject);
|
||||
|
||||
/// <summary>
|
||||
/// Renames a resource.
|
||||
/// </summary>
|
||||
/// <param name="resource">The resource.</param>
|
||||
/// <param name="newName">The new name of the resource.</param>
|
||||
/// <returns><c>true</c> if the resource is renamed, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="resource"/> or <paramref name="newName"/> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="resource"/> or <paramref name="newName"/> are empty.</exception>
|
||||
bool RenameResource(string resource, string newName);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all the ACL entries for a resource.
|
||||
/// </summary>
|
||||
/// <returns>The entries.</returns>
|
||||
AclEntry[] RetrieveAllEntries();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all the ACL entries for a resource.
|
||||
/// </summary>
|
||||
/// <param name="resource">The resource.</param>
|
||||
/// <returns>The entries.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="resource"/> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="resource"/> is empty.</exception>
|
||||
AclEntry[] RetrieveEntriesForResource(string resource);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all the ACL entries for a subject.
|
||||
/// </summary>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <returns>The entries.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="subject"/> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="subject"/> is empty.</exception>
|
||||
AclEntry[] RetrieveEntriesForSubject(string subject);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the manager data.
|
||||
/// </summary>
|
||||
/// <param name="entries">The ACL entries.</param>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="entries"/> is <c>null</c>.</exception>
|
||||
void InitializeData(AclEntry[] entries);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of ACL entries.
|
||||
/// </summary>
|
||||
int TotalEntries { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when an ACL entry is stored or deleted.
|
||||
/// </summary>
|
||||
event EventHandler<AclChangedEventArgs> AclChanged;
|
||||
|
||||
}
|
||||
|
||||
}
|
18
AclEngine/Properties/AssemblyInfo.cs
Normal file
18
AclEngine/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("ScrewTurn Wiki ACL Engine")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("3129a8a6-4955-47a3-9a88-3ea42a08b34b")]
|
20
AssemblyVersion.cs
Normal file
20
AssemblyVersion.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyCompany("Dario Solera")]
|
||||
[assembly: AssemblyProduct("ScrewTurn Wiki")]
|
||||
[assembly: AssemblyCopyright("Copyright © Dario Solera 2006-2009")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Revision and Build Numbers
|
||||
// by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("3.0.0.333")]
|
||||
[assembly: AssemblyFileVersion("3.0.0.333")]
|
22
Build - Readme.txt
Normal file
22
Build - Readme.txt
Normal file
|
@ -0,0 +1,22 @@
|
|||
|
||||
ScrewTurn Wiki is written in Microsoft Visual Studio 2008 Professional. The project is structured
|
||||
as a Visual Studio solution (ScrewTurnWiki.sln).
|
||||
|
||||
In order to compile the application you can either build the solution in Visual Studio, or
|
||||
follow the instructions included in the Build directory.
|
||||
|
||||
The CHM documentation file (Help directory) is built using Microsoft Sandcastle [1] and
|
||||
Sandcastle Help File Builder [2]. The documentation is not necessary to compile or run the application.
|
||||
|
||||
Note: if you are using a 64-bit Windows edition, Sandcastle as well as HTML Help Workshop
|
||||
(distributed with Visual Studio 2008) are installed by default in "C:\Program Files (x86)\".
|
||||
The SHFB project assumes that the installation directory is "C:\Program Files\".
|
||||
In order to fix this issue without re-installing the applications or modifying the project file,
|
||||
you can create a symbolic link using the "mklink" command-line tool (only supported in
|
||||
Windows Server 2003/2008 and Windows Vista and 7 (you may need to open an elevated command prompt):
|
||||
|
||||
mklink /D Sandcastle "C:\Program Files (x86)\Sandcastle"
|
||||
mklink /D "HTML Help Workshop" "C:\Program Files (x86)\HTML Help Workshop"
|
||||
|
||||
[1] http://www.codeplex.com/Sandcastle
|
||||
[2] http://www.codeplex.com/SHFB
|
2
Build/Build.bat
Normal file
2
Build/Build.bat
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
"%WINDIR%\Microsoft.NET\Framework\v3.5\MSBuild.exe" ScrewTurnWiki.msbuild
|
2
Build/BuildAndPackage.bat
Normal file
2
Build/BuildAndPackage.bat
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
"%WINDIR%\Microsoft.NET\Framework\v3.5\MSBuild.exe" ScrewTurnWiki.msbuild /t:Package
|
2
Build/BuildAndTest.bat
Normal file
2
Build/BuildAndTest.bat
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
"%WINDIR%\Microsoft.NET\Framework\v3.5\MSBuild.exe" ScrewTurnWiki.msbuild /t:Test /p:Configuration=Debug
|
98
Build/Install-SqlServer.txt
Normal file
98
Build/Install-SqlServer.txt
Normal file
|
@ -0,0 +1,98 @@
|
|||
|
||||
Installing or Updating ScrewTurn Wiki
|
||||
-------------------------------------
|
||||
|
||||
This document explains how to install or update ScrewTurn Wiki when using the
|
||||
SQL Server-based data providers.
|
||||
|
||||
INPUT_DIR refers to the WebApplication directory supplied along this file.
|
||||
TARGET_DIR refers to the directory the application will live in on your server.
|
||||
|
||||
This document assumes that you have at least basic knowledge about managing a Windows Server
|
||||
machine, IIS (Internet Information Services) and Microsoft SQL Server.
|
||||
|
||||
If you need help, you can ask questions in the project forum [1].
|
||||
|
||||
If you want to host ScrewTurn Wiki using a shared hosting service, or you don't have full
|
||||
administrative access to your server, please contact either the service's support or the
|
||||
server administrator.
|
||||
|
||||
Please also take a look at other installation packages that are available at the project
|
||||
website [2], which might better suit your needs.
|
||||
|
||||
|
||||
|
||||
Clean Installation
|
||||
------------------
|
||||
|
||||
Note: depending on the environment you are running, the following steps might require
|
||||
minor adjustments.
|
||||
|
||||
1. Create TARGET_DIR if it does not already exist; be sure that the directory is empty.
|
||||
|
||||
2. Copy all the files from INPUT_DIR into TARGET_DIR.
|
||||
|
||||
3. Open the "web.config" file with a text editor and set the MasterPassword field with a password
|
||||
that will be used for the built-in "admin" account, for example:
|
||||
<add key="MasterPassword" value="my_password_123" />
|
||||
|
||||
4. In the "web.config" file, complete the SQL Server connection string, specifying server address,
|
||||
database name and authentication information (either user/password or integrated authentication),
|
||||
for example:
|
||||
<add key="SettingsStorageProviderConfig" value="Data Source=(local);Initial Catalog=ScrewTurnWiki;User ID=wiki_user;Password=wiki_password_567;" />
|
||||
|
||||
5. Make sure that the "web.config" file specifies the correct Settings Storage Provider:
|
||||
<add key="SettingsStorageProvider" value="ScrewTurn.Wiki.Plugins.SqlServer.SqlServerSettingsStorageProvider, SqlServerProviders.dll" />
|
||||
|
||||
6. Create a database in SQL Server with the name specified in the connection string, making sure that
|
||||
the specified user can create, alter and drop tables as well as select, update, insert and delete rows.
|
||||
Make sure that the database collation is NOT case sensitive.
|
||||
|
||||
7. Setup a Web Site or Application in IIS, setting the root directory to TARGET_DIRECTORY.
|
||||
|
||||
8. Navigate to the Web Site or Application using a web browser and verify that everything works properly
|
||||
(the data providers, if the connection string is correct, will automatically create the required
|
||||
tables in the database).
|
||||
|
||||
|
||||
Updating From a Previous v3 Release
|
||||
-----------------------------------
|
||||
|
||||
If you upgrading from v2, please refer to our online documentation [3].
|
||||
|
||||
Note: depending on the environment you are running, the following steps might require
|
||||
minor adjustments. If you made modifications to the application, the following steps
|
||||
might cause issues and make the application unusable and/or unstable.
|
||||
|
||||
1. Navigate to TARGET_DIR and verify that the "public" directory only contains the "Plugins" directory.
|
||||
|
||||
2. Backup all the content of TARGET_DIR (seriously).
|
||||
Backup your database (seriously).
|
||||
|
||||
3. Rename "app_online.htm" to "app_offline.htm": this will take the application offline.
|
||||
|
||||
4. Delete all the content of TARGET_DIR, ***except***:
|
||||
- "web.config" file
|
||||
- "app_offline.htm" file.
|
||||
|
||||
5. Copy "SqlServerProviders.dll" from the distribution package into "TARGET_DIR\public\Plugins",
|
||||
replacing the existing file.
|
||||
|
||||
6. Select all files in INPUT_DIR, ***except***:
|
||||
- "web.config" file.
|
||||
|
||||
7. Copy all the selected files into TARGET_DIR.
|
||||
|
||||
8. Delete "app_offline.htm".
|
||||
|
||||
9. Navigate to the wiki using a web browser and verify that everything works properly,
|
||||
(the data providers, if the connection string is correct, will automatically update the required
|
||||
tables in the database).
|
||||
|
||||
10. Check for plugin updates in the administration interface.
|
||||
|
||||
|
||||
|
||||
[1] http://www.screwturn.eu/forum
|
||||
[2] http://www.screwturn.eu
|
||||
[3] http://www.screwturn.eu/Help.MainPage.ashx
|
84
Build/Install.txt
Normal file
84
Build/Install.txt
Normal file
|
@ -0,0 +1,84 @@
|
|||
|
||||
Installing or Updating ScrewTurn Wiki
|
||||
-------------------------------------
|
||||
|
||||
This document explains how to install or update ScrewTurn Wiki when using the built-in
|
||||
file-based data providers.
|
||||
|
||||
INPUT_DIR refers to the WebApplication directory supplied along this file.
|
||||
TARGET_DIR refers to the directory the application will live in on your server.
|
||||
|
||||
This document assumes that you have at least basic knowledge about managing a Windows Server
|
||||
machine and IIS (Internet Information Services).
|
||||
|
||||
If you need help, you can ask questions in the project forum [1].
|
||||
|
||||
If you want to host ScrewTurn Wiki using a shared hosting service, or you don't have full
|
||||
administrative access to your server, please contact either the service's support or the
|
||||
server administrator.
|
||||
|
||||
Please also take a look at other installation packages that are available at the project
|
||||
website [2], which might better suit your needs.
|
||||
|
||||
|
||||
|
||||
Clean Installation
|
||||
------------------
|
||||
|
||||
Note: depending on the environment you are running, the following steps might require
|
||||
minor adjustments.
|
||||
|
||||
1. Create TARGET_DIR if it does not already exist; be sure that the directory is empty.
|
||||
|
||||
2. Copy all the files from INPUT_DIR into TARGET_DIR.
|
||||
|
||||
3. Open the "web.config" file with a text editor and set the MasterPassword field with a password
|
||||
that will be used for the built-in "admin" account, for example:
|
||||
<add key="MasterPassword" value="my_password_123" />
|
||||
|
||||
4. Set the permissions of the "public" directory so that the ASP.NET Worker Process has "Modify"
|
||||
permissions on it (usually, the ASP.NET Worker Process runs as "NETWORK SERVICE" or "ASPNET",
|
||||
depending on the Operating System version).
|
||||
|
||||
5. Setup a Web Site or Application in IIS, setting the root directory to TARGET_DIRECTORY.
|
||||
|
||||
6. Navigate to the Web Site or Application using a web browser and verify that everything works properly.
|
||||
|
||||
|
||||
Updating From a Previous v3 Release
|
||||
-----------------------------------
|
||||
|
||||
If you upgrading from v2, please refer to our online documentation [3].
|
||||
|
||||
Note: depending on the environment you are running, the following steps might require
|
||||
minor adjustments. If you made modifications to the application, the following steps
|
||||
might cause issues and make the application unusable and/or unstable.
|
||||
|
||||
1. Navigate to TARGET_DIR.
|
||||
|
||||
2. Backup all the content of TARGET_DIR (seriously).
|
||||
|
||||
3. Rename "app_online.htm" to "app_offline.htm": this will take the application offline.
|
||||
|
||||
4. Delete all the content of TARGET_DIR, ***except***:
|
||||
- "public" directory
|
||||
- "web.config" file
|
||||
- "app_offline.htm" file.
|
||||
|
||||
5. Select all files in INPUT_DIR, ***except***:
|
||||
- "public" directory
|
||||
- "web.config" file.
|
||||
|
||||
6. Copy all the selected files into TARGET_DIR.
|
||||
|
||||
7. Delete "app_offline.htm".
|
||||
|
||||
8. Navigate to the wiki using a web browser and verify that everything works properly.
|
||||
|
||||
9. Check for plugin updates in the administration interface.
|
||||
|
||||
|
||||
|
||||
[1] http://www.screwturn.eu/forum
|
||||
[2] http://www.screwturn.eu
|
||||
[3] http://www.screwturn.eu/Help.MainPage.ashx
|
16
Build/Readme.txt
Normal file
16
Build/Readme.txt
Normal file
|
@ -0,0 +1,16 @@
|
|||
|
||||
In order to build ScrewTurn Wiki, you need Microsoft Visual Studio 2008 (any edition, including Express)
|
||||
installed on the machine.
|
||||
|
||||
ScrewTurnWiki.msbuild contains the build script, written for MSBuild 3.5.
|
||||
|
||||
To build the application more easily, use the Build.bad batch file, which compiles the application
|
||||
and the plugins.
|
||||
|
||||
BuildAndTest.bat compiles the application and runs all the unit tests. SQL Server 2005/2008 is required
|
||||
on the machine and it must allow the necessary access rights to the current Windows user.
|
||||
|
||||
BuildAndPackage.bat creates a ZIP archive containing the compiled application and the plugins,
|
||||
but it requires 7-zip [1] installed on the system.
|
||||
|
||||
[1] http://www.7-zip.org
|
108
Build/ScrewTurnWiki.msbuild
Normal file
108
Build/ScrewTurnWiki.msbuild
Normal file
|
@ -0,0 +1,108 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="3.5" DefaultTargets="Compile" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<PropertyGroup>
|
||||
<Configuration Condition="'$(Configuration)' == ''">Release</Configuration>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<SolutionRoot Include=".." />
|
||||
<SolutionFile Include="..\ScrewTurnWiki.sln" />
|
||||
|
||||
<BuildTemp Include=".\Temp\" />
|
||||
|
||||
<Artifacts Include=".\Artifacts\" />
|
||||
<WebAppOutput Include=".\Artifacts\WebApplication\" />
|
||||
<WebAppOutput_PublicDirectory Include=".\Artifacts\WebApplication\public\" />
|
||||
<WebAppOutput_SqlServer Include=".\Artifacts\WebApplication-SqlServer\" />
|
||||
<WebAppOutput_SqlServer_PublicDirectory Include=".\Artifacts\WebApplication-SqlServer\public\" />
|
||||
<WebAppOutput_SqlServer_PublicDirectory_Plugins Include=".\Artifacts\WebApplication-SqlServer\public\Plugins\" />
|
||||
<PluginsOutput Include=".\Artifacts\Plugins\" />
|
||||
<TestsOutput Include=".\Artifacts\Tests\" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="Clean">
|
||||
<RemoveDir Directories="@(Artifacts)" />
|
||||
<RemoveDir Directories="@(Temp)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="Init" DependsOnTargets="Clean">
|
||||
<MakeDir Directories="@(Artifacts)" />
|
||||
<MakeDir Directories="@(BuildTemp)" />
|
||||
<MakeDir Directories="@(WebAppOutput)" />
|
||||
<MakeDir Directories="@(PluginsOutput)" />
|
||||
<MakeDir Directories="@(TestsOutput)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="Compile" DependsOnTargets="Init">
|
||||
<MSBuild Projects="@(SolutionFile)" Properties="OutDir=%(BuildTemp.FullPath);Configuration=$(Configuration)" />
|
||||
|
||||
<!-- Web Application -->
|
||||
<CreateItem Include="Temp\_PublishedWebsites\WebApplication\**\*.*" Exclude="**\*.xml">
|
||||
<Output ItemName="BuiltWebApp" TaskParameter="Include" />
|
||||
</CreateItem>
|
||||
<Copy SourceFiles="@(BuiltWebApp)" DestinationFolder=".\Artifacts\WebApplication\%(RecursiveDir)" />
|
||||
|
||||
<!-- Web Application, SQL Server -->
|
||||
<Copy SourceFiles="@(BuiltWebApp)" DestinationFolder=".\Artifacts\WebApplication-SqlServer\%(RecursiveDir)" />
|
||||
|
||||
<!-- Release Web.config -->
|
||||
<Copy Condition="'$(Configuration)' == 'Release'"
|
||||
SourceFiles=".\Artifacts\WebApplication\Web.Release.config" DestinationFiles=".\Artifacts\WebApplication\Web.config" />
|
||||
<Delete Files=".\Artifacts\WebApplication\Web.Release.config" />
|
||||
<Delete Files=".\Artifacts\WebApplication\Web.SqlServer.Release.config" />
|
||||
|
||||
<!-- Release Web.config, SQL Server -->
|
||||
<Copy Condition="'$(Configuration)' == 'Release'"
|
||||
SourceFiles=".\Artifacts\WebApplication-SqlServer\Web.SqlServer.Release.config" DestinationFiles=".\Artifacts\WebApplication-SqlServer\Web.config" />
|
||||
<Delete Files=".\Artifacts\WebApplication-SqlServer\Web.Release.config" />
|
||||
<Delete Files=".\Artifacts\WebApplication-SqlServer\Web.SqlServer.Release.config" />
|
||||
|
||||
<!-- Web Application Public Directory -->
|
||||
<MakeDir Directories="@(WebAppOutput_PublicDirectory)" />
|
||||
|
||||
<!-- Web Application Public Directory and Plugins Directory, SQL Server -->
|
||||
<MakeDir Directories="@(WebAppOutput_SqlServer_PublicDirectory)" />
|
||||
<MakeDir Directories="@(WebAppOutput_SqlServer_PublicDirectory_Plugins)" />
|
||||
|
||||
<!-- Plugins Assemblies -->
|
||||
<CreateItem Include="Temp\PluginPack.*;Temp\SqlServerProviders.*">
|
||||
<Output ItemName="BuiltPlugins" TaskParameter="Include" />
|
||||
</CreateItem>
|
||||
<Copy SourceFiles="@(BuiltPlugins)" DestinationFolder=".\Artifacts\Plugins\" />
|
||||
|
||||
<!-- SqlServerProviders.dll for SQL Server -->
|
||||
<Copy SourceFiles=".\Artifacts\Plugins\SqlServerProviders.dll" DestinationFolder=".\Artifacts\WebApplication-SqlServer\public\Plugins\" />
|
||||
|
||||
<!-- Install.txt, Install-SqlServer.txt -->
|
||||
<Copy SourceFiles=".\Install.txt" DestinationFolder=".\Artifacts\" />
|
||||
<Copy SourceFiles=".\Install-SqlServer.txt" DestinationFolder=".\Artifacts\" />
|
||||
|
||||
<!-- Tests Assemblies -->
|
||||
<CreateItem Include="Temp\*.dll;Temp\*.pdb">
|
||||
<Output ItemName="BuiltTests" TaskParameter="Include" />
|
||||
</CreateItem>
|
||||
<Copy SourceFiles="@(BuiltTests)" DestinationFolder=".\Artifacts\Tests\" />
|
||||
|
||||
<!-- Delete Temp directory -->
|
||||
<RemoveDir Directories="@(BuildTemp)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="Test" DependsOnTargets="Compile">
|
||||
<!-- Requires SQL Server 2005/2008 installed on the system, and it must grant the necessary rights to the current Windows user -->
|
||||
|
||||
<Exec Command='..\References\Tools\NUnit\nunit-console.exe ".\Artifacts\Tests\ScrewTurn.Wiki.AclEngine.Tests.dll" ".\Artifacts\Tests\ScrewTurn.Wiki.Core.Tests.dll" ".\Artifacts\Tests\ScrewTurn.Wiki.Plugins.PluginPack.Tests.dll" ".\Artifacts\Tests\ScrewTurn.Wiki.Plugins.SqlCommon.Tests.dll" ".\Artifacts\Tests\ScrewTurn.Wiki.Plugins.SqlServer.Tests.dll" ".\Artifacts\Tests\ScrewTurn.Wiki.SearchEngine.Tests.dll" /xml=".\Artifacts\TestResult.xml" /labels' />
|
||||
</Target>
|
||||
|
||||
<Target Name="Package" DependsOnTargets="Compile">
|
||||
<!-- Requires 7-Zip installed on the system in the location %PROGRAMFILES% points to - use a symlink alternatively -->
|
||||
|
||||
<GetAssemblyIdentity AssemblyFiles=".\Artifacts\WebApplication\bin\ScrewTurn.Wiki.Core.dll">
|
||||
<Output TaskParameter="Assemblies" ItemName="MyAsm" />
|
||||
</GetAssemblyIdentity>
|
||||
|
||||
<Exec Command='"$(ProgramFiles)\7-Zip\7z.exe" a -tzip -mx=7 ".\Artifacts\ScrewTurnWiki-%(MyAsm.Version).zip" ".\Artifacts\WebApplication\" ".\Artifacts\Plugins\" ".\Artifacts\Install.txt"' />
|
||||
<Exec Command='"$(ProgramFiles)\7-Zip\7z.exe" a -tzip -mx=7 ".\Artifacts\ScrewTurnWiki-SqlServer-%(MyAsm.Version).zip" ".\Artifacts\WebApplication-SqlServer\" ".\Artifacts\Plugins\" ".\Artifacts\Install-SqlServer.txt"' />
|
||||
</Target>
|
||||
|
||||
</Project>
|
148
Core-Tests/AclStorerTests.cs
Normal file
148
Core-Tests/AclStorerTests.cs
Normal file
|
@ -0,0 +1,148 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Rhino.Mocks;
|
||||
using ScrewTurn.Wiki.AclEngine;
|
||||
|
||||
namespace ScrewTurn.Wiki.Tests {
|
||||
|
||||
[TestFixture]
|
||||
public class AclStorerTests {
|
||||
|
||||
private string testFile = Path.Combine(Environment.GetEnvironmentVariable("TEMP"), "__ACL_File.dat");
|
||||
|
||||
private MockRepository mocks = new MockRepository();
|
||||
|
||||
private IAclManager MockAclManager() {
|
||||
IAclManager manager = mocks.DynamicMock<AclManagerBase>();
|
||||
|
||||
return manager;
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown() {
|
||||
try {
|
||||
File.Delete(testFile);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Constructor() {
|
||||
IAclManager manager = MockAclManager();
|
||||
|
||||
AclStorer storer = new AclStorer(manager, testFile);
|
||||
Assert.AreSame(manager, storer.AclManager, "Wrong ACL Manager instance");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void Constructor_NullAclManager() {
|
||||
AclStorer storer = new AclStorer(null, testFile);
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void Constructor_InvalidFile(string f) {
|
||||
AclStorer storer = new AclStorer(MockAclManager(), f);
|
||||
}
|
||||
|
||||
private void AssertAclEntriesAreEqual(AclEntry expected, AclEntry actual) {
|
||||
Assert.AreEqual(expected.Resource, actual.Resource, "Wrong resource");
|
||||
Assert.AreEqual(expected.Action, actual.Action, "Wrong action");
|
||||
Assert.AreEqual(expected.Subject, actual.Subject, "Wrong subject");
|
||||
Assert.AreEqual(expected.Value, actual.Value, "Wrong value");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Store_LoadData() {
|
||||
IAclManager manager = MockAclManager();
|
||||
|
||||
AclStorer storer = new AclStorer(manager, testFile);
|
||||
|
||||
manager.StoreEntry("Res1", "Action1", "U.User", Value.Grant);
|
||||
manager.StoreEntry("Res2", "Action2", "G.Group", Value.Deny);
|
||||
|
||||
storer.Dispose();
|
||||
storer = null;
|
||||
|
||||
manager = MockAclManager();
|
||||
|
||||
storer = new AclStorer(manager, testFile);
|
||||
storer.LoadData();
|
||||
|
||||
Assert.AreEqual(2, manager.TotalEntries, "Wrong entry count");
|
||||
|
||||
AclEntry[] allEntries = manager.RetrieveAllEntries();
|
||||
Assert.AreEqual(2, allEntries.Length, "Wrong entry count");
|
||||
|
||||
Array.Sort(allEntries, delegate(AclEntry x, AclEntry y) { return x.Subject.CompareTo(y.Subject); });
|
||||
AssertAclEntriesAreEqual(new AclEntry("Res2", "Action2", "G.Group", Value.Deny), allEntries[0]);
|
||||
AssertAclEntriesAreEqual(new AclEntry("Res1", "Action1", "U.User", Value.Grant), allEntries[1]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Delete_LoadData() {
|
||||
IAclManager manager = MockAclManager();
|
||||
|
||||
AclStorer storer = new AclStorer(manager, testFile);
|
||||
|
||||
manager.StoreEntry("Res1", "Action1", "U.User", Value.Grant);
|
||||
manager.StoreEntry("Res2", "Action2", "G.Group", Value.Deny);
|
||||
manager.StoreEntry("Res3", "Action1", "U.User", Value.Grant);
|
||||
manager.StoreEntry("Res3", "Action2", "G.Group", Value.Deny);
|
||||
|
||||
manager.DeleteEntriesForResource("Res3");
|
||||
|
||||
storer.Dispose();
|
||||
storer = null;
|
||||
|
||||
manager = MockAclManager();
|
||||
|
||||
storer = new AclStorer(manager, testFile);
|
||||
storer.LoadData();
|
||||
|
||||
Assert.AreEqual(2, manager.TotalEntries, "Wrong entry count");
|
||||
|
||||
AclEntry[] allEntries = manager.RetrieveAllEntries();
|
||||
Assert.AreEqual(2, allEntries.Length, "Wrong entry count");
|
||||
|
||||
Array.Sort(allEntries, delegate(AclEntry x, AclEntry y) { return x.Subject.CompareTo(y.Subject); });
|
||||
AssertAclEntriesAreEqual(new AclEntry("Res2", "Action2", "G.Group", Value.Deny), allEntries[0]);
|
||||
AssertAclEntriesAreEqual(new AclEntry("Res1", "Action1", "U.User", Value.Grant), allEntries[1]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Overwrite_LoadData() {
|
||||
IAclManager manager = MockAclManager();
|
||||
|
||||
AclStorer storer = new AclStorer(manager, testFile);
|
||||
|
||||
manager.StoreEntry("Res1", "Action1", "U.User", Value.Grant);
|
||||
manager.StoreEntry("Res2", "Action2", "G.Group", Value.Grant);
|
||||
manager.StoreEntry("Res2", "Action2", "G.Group", Value.Deny); // Overwrite
|
||||
|
||||
storer.Dispose();
|
||||
storer = null;
|
||||
|
||||
manager = MockAclManager();
|
||||
|
||||
storer = new AclStorer(manager, testFile);
|
||||
storer.LoadData();
|
||||
|
||||
Assert.AreEqual(2, manager.TotalEntries, "Wrong entry count");
|
||||
|
||||
AclEntry[] allEntries = manager.RetrieveAllEntries();
|
||||
Assert.AreEqual(2, allEntries.Length, "Wrong entry count");
|
||||
|
||||
Array.Sort(allEntries, delegate(AclEntry x, AclEntry y) { return x.Subject.CompareTo(y.Subject); });
|
||||
AssertAclEntriesAreEqual(new AclEntry("Res2", "Action2", "G.Group", Value.Deny), allEntries[0]);
|
||||
AssertAclEntriesAreEqual(new AclEntry("Res1", "Action1", "U.User", Value.Grant), allEntries[1]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
1736
Core-Tests/AuthCheckerTests.cs
Normal file
1736
Core-Tests/AuthCheckerTests.cs
Normal file
File diff suppressed because it is too large
Load diff
1169
Core-Tests/AuthReaderTests.cs
Normal file
1169
Core-Tests/AuthReaderTests.cs
Normal file
File diff suppressed because it is too large
Load diff
66
Core-Tests/AuthToolsTests.cs
Normal file
66
Core-Tests/AuthToolsTests.cs
Normal file
|
@ -0,0 +1,66 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ScrewTurn.Wiki.Tests {
|
||||
|
||||
[TestFixture]
|
||||
public class AuthToolsTests {
|
||||
|
||||
[Test]
|
||||
public void Static_PrepareUsername() {
|
||||
Assert.AreEqual("U.User", AuthTools.PrepareUsername("User"), "Wrong result");
|
||||
Assert.AreEqual("U.U.User", AuthTools.PrepareUsername("U.User"), "Wrong result");
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void Static_PrepareUsername_InvalidUsername(string s) {
|
||||
AuthTools.PrepareUsername(s);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Static_PrepareGroups() {
|
||||
Assert.AreEqual(0, AuthTools.PrepareGroups(new string[0]).Length, "Wrong result length");
|
||||
|
||||
string[] input = new string[] { "Group", "G.Group" };
|
||||
string[] output = AuthTools.PrepareGroups(input);
|
||||
|
||||
Assert.AreEqual(input.Length, output.Length, "Wrong result length");
|
||||
for(int i = 0; i < input.Length; i++) {
|
||||
Assert.AreEqual("G." + input[i], output[i], "Wrong value");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void Static_PrepareGroups_NullGroups() {
|
||||
AuthTools.PrepareGroups(null);
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void Static_PrepareGroups_InvalidElement(string e) {
|
||||
AuthTools.PrepareGroups(new string[] { e });
|
||||
}
|
||||
|
||||
[TestCase(null, false, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", false, ExpectedException = typeof(ArgumentException))]
|
||||
[TestCase("G", false, ExpectedException = typeof(ArgumentException))]
|
||||
[TestCase("G.", true)]
|
||||
[TestCase("g.", true)]
|
||||
[TestCase("G.Blah", true)]
|
||||
[TestCase("g.Blah", true)]
|
||||
[TestCase("U.", false)]
|
||||
[TestCase("u.", false)]
|
||||
[TestCase("U.Blah", false)]
|
||||
[TestCase("u.Blah", false)]
|
||||
public void Static_IsGroup(string subject, bool result) {
|
||||
Assert.AreEqual(result, AuthTools.IsGroup(subject), "Wrong result");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
1629
Core-Tests/AuthWriterTests.cs
Normal file
1629
Core-Tests/AuthWriterTests.cs
Normal file
File diff suppressed because it is too large
Load diff
21
Core-Tests/CacheProviderTests.cs
Normal file
21
Core-Tests/CacheProviderTests.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Rhino.Mocks;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki.Tests {
|
||||
|
||||
public class CacheProviderTests : CacheProviderTestScaffolding {
|
||||
|
||||
public override ICacheProviderV30 GetProvider() {
|
||||
CacheProvider prov = new CacheProvider();
|
||||
prov.Init(MockHost(), "");
|
||||
return prov;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
106
Core-Tests/Core-Tests.csproj
Normal file
106
Core-Tests/Core-Tests.csproj
Normal file
|
@ -0,0 +1,106 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.30729</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{013B5DA5-76F9-4D7F-A174-4926BF51E24B}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>ScrewTurn.Wiki.Tests</RootNamespace>
|
||||
<AssemblyName>ScrewTurn.Wiki.Core.Tests</AssemblyName>
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="nunit.framework, Version=2.5.1.9189, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\References\Tools\NUnit\framework\nunit.framework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Rhino.Mocks, Version=3.5.0.1337, Culture=neutral, PublicKeyToken=0b3305902db7183f, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\References\Tools\Rhino.Mocks\Rhino.Mocks.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\AssemblyVersion.cs">
|
||||
<Link>AssemblyVersion.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\SearchEngine-Tests\TestsBase.cs">
|
||||
<Link>TestsBase.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="AclStorerTests.cs" />
|
||||
<Compile Include="AuthCheckerTests.cs" />
|
||||
<Compile Include="AuthReaderTests.cs" />
|
||||
<Compile Include="AuthToolsTests.cs" />
|
||||
<Compile Include="AuthWriterTests.cs" />
|
||||
<Compile Include="CacheProviderTests.cs" />
|
||||
<Compile Include="DataMigratorTests.cs" />
|
||||
<Compile Include="FilesStorageProviderTests.cs" />
|
||||
<Compile Include="FormatterTests.cs" />
|
||||
<Compile Include="IndexStorerTests.cs" />
|
||||
<Compile Include="PagesStorageProviderTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ProviderLoaderTests.cs" />
|
||||
<Compile Include="SettingsStorageProviderTests.cs" />
|
||||
<Compile Include="TestSettingsStorageProvider.cs" />
|
||||
<Compile Include="UsersStorageProviderTests.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AclEngine\AclEngine.csproj">
|
||||
<Project>{44B0F4C1-8CDC-4272-B2A2-C0AF689CEB81}</Project>
|
||||
<Name>AclEngine</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Core\Core.csproj">
|
||||
<Project>{C353A35C-86D0-4154-9500-4F88CAAB29C3}</Project>
|
||||
<Name>Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\PluginFramework\PluginFramework.csproj">
|
||||
<Project>{531A83D6-76F9-4014-91C5-295818E2D948}</Project>
|
||||
<Name>PluginFramework</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SearchEngine\SearchEngine.csproj">
|
||||
<Project>{2DF980A6-4742-49B1-A090-DE79314644D0}</Project>
|
||||
<Name>SearchEngine</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\TestScaffolding\TestScaffolding.csproj">
|
||||
<Project>{F865670A-DEDE-41B5-B426-48D73C3B5B1C}</Project>
|
||||
<Name>TestScaffolding</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
647
Core-Tests/DataMigratorTests.cs
Normal file
647
Core-Tests/DataMigratorTests.cs
Normal file
|
@ -0,0 +1,647 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Rhino.Mocks;
|
||||
using RMC = Rhino.Mocks.Constraints;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
using ScrewTurn.Wiki.AclEngine;
|
||||
|
||||
namespace ScrewTurn.Wiki.Tests {
|
||||
|
||||
[TestFixture]
|
||||
public class DataMigratorTests {
|
||||
|
||||
[Test]
|
||||
public void MigratePagesStorageProviderData() {
|
||||
MockRepository mocks = new MockRepository();
|
||||
|
||||
IPagesStorageProviderV30 source = mocks.StrictMock<IPagesStorageProviderV30>();
|
||||
IPagesStorageProviderV30 destination = mocks.StrictMock<IPagesStorageProviderV30>();
|
||||
|
||||
// Setup SOURCE -------------------------
|
||||
|
||||
// Setup snippets
|
||||
Snippet s1 = new Snippet("S1", "Blah1", source);
|
||||
Snippet s2 = new Snippet("S2", "Blah2", source);
|
||||
Expect.Call(source.GetSnippets()).Return(new Snippet[] { s1, s2 });
|
||||
|
||||
// Setup content templates
|
||||
ContentTemplate ct1 = new ContentTemplate("CT1", "Template 1", source);
|
||||
ContentTemplate ct2 = new ContentTemplate("CT2", "Template 2", source);
|
||||
Expect.Call(source.GetContentTemplates()).Return(new ContentTemplate[] { ct1, ct2 });
|
||||
|
||||
// Setup namespaces
|
||||
NamespaceInfo ns1 = new NamespaceInfo("NS1", source, null);
|
||||
NamespaceInfo ns2 = new NamespaceInfo("NS2", source, null);
|
||||
Expect.Call(source.GetNamespaces()).Return(new NamespaceInfo[] { ns1, ns2 });
|
||||
|
||||
// Setup pages
|
||||
PageInfo p1 = new PageInfo("Page", source, DateTime.Now);
|
||||
PageInfo p2 = new PageInfo(NameTools.GetFullName(ns1.Name, "Page"), source, DateTime.Now);
|
||||
PageInfo p3 = new PageInfo(NameTools.GetFullName(ns1.Name, "Page1"), source, DateTime.Now);
|
||||
Expect.Call(source.GetPages(null)).Return(new PageInfo[] { p1 });
|
||||
Expect.Call(source.GetPages(ns1)).Return(new PageInfo[] { p2, p3 });
|
||||
Expect.Call(source.GetPages(ns2)).Return(new PageInfo[0]);
|
||||
|
||||
// Set default page for NS1
|
||||
ns1.DefaultPage = p2;
|
||||
|
||||
// Setup categories/bindings
|
||||
CategoryInfo c1 = new CategoryInfo("Cat", source);
|
||||
c1.Pages = new string[] { p1.FullName };
|
||||
CategoryInfo c2 = new CategoryInfo(NameTools.GetFullName(ns1.Name, "Cat"), source);
|
||||
c2.Pages = new string[] { p2.FullName };
|
||||
CategoryInfo c3 = new CategoryInfo(NameTools.GetFullName(ns1.Name, "Cat1"), source);
|
||||
c3.Pages = new string[0];
|
||||
Expect.Call(source.GetCategories(null)).Return(new CategoryInfo[] { c1 });
|
||||
Expect.Call(source.GetCategories(ns1)).Return(new CategoryInfo[] { c2, c3 });
|
||||
Expect.Call(source.GetCategories(ns2)).Return(new CategoryInfo[0]);
|
||||
|
||||
// Setup drafts
|
||||
PageContent d1 = new PageContent(p1, "Draft", "NUnit", DateTime.Now, "Comm", "Cont", new string[] { "k1", "k2" }, "Descr");
|
||||
Expect.Call(source.GetDraft(p1)).Return(d1);
|
||||
Expect.Call(source.GetDraft(p2)).Return(null);
|
||||
Expect.Call(source.GetDraft(p3)).Return(null);
|
||||
|
||||
// Setup content
|
||||
PageContent ctn1 = new PageContent(p1, "Title1", "User1", DateTime.Now, "Comm1", "Cont1", null, "Descr1");
|
||||
PageContent ctn2 = new PageContent(p2, "Title2", "User2", DateTime.Now, "Comm2", "Cont2", null, "Descr2");
|
||||
PageContent ctn3 = new PageContent(p3, "Title3", "User3", DateTime.Now, "Comm3", "Cont3", null, "Descr3");
|
||||
Expect.Call(source.GetContent(p1)).Return(ctn1);
|
||||
Expect.Call(source.GetContent(p2)).Return(ctn2);
|
||||
Expect.Call(source.GetContent(p3)).Return(ctn3);
|
||||
|
||||
// Setup backups
|
||||
Expect.Call(source.GetBackups(p1)).Return(new int[] { 0, 1 });
|
||||
Expect.Call(source.GetBackups(p2)).Return(new int[] { 0 });
|
||||
Expect.Call(source.GetBackups(p3)).Return(new int[0]);
|
||||
PageContent bak1_0 = new PageContent(p1, "K1_0", "U1_0", DateTime.Now, "", "Cont", null, null);
|
||||
PageContent bak1_1 = new PageContent(p1, "K1_1", "U1_1", DateTime.Now, "", "Cont", null, null);
|
||||
PageContent bak2_0 = new PageContent(p2, "K2_0", "U2_0", DateTime.Now, "", "Cont", null, null);
|
||||
Expect.Call(source.GetBackupContent(p1, 0)).Return(bak1_0);
|
||||
Expect.Call(source.GetBackupContent(p1, 1)).Return(bak1_1);
|
||||
Expect.Call(source.GetBackupContent(p2, 0)).Return(bak2_0);
|
||||
|
||||
// Messages
|
||||
Message m1 = new Message(1, "User1", "Subject1", DateTime.Now, "Body1");
|
||||
m1.Replies = new Message[] { new Message(2, "User2", "Subject2", DateTime.Now, "Body2") };
|
||||
Message[] p1m = new Message[] { m1 };
|
||||
Message[] p2m = new Message[0];
|
||||
Message[] p3m = new Message[0];
|
||||
Expect.Call(source.GetMessages(p1)).Return(p1m);
|
||||
Expect.Call(source.GetMessages(p2)).Return(p2m);
|
||||
Expect.Call(source.GetMessages(p3)).Return(p3m);
|
||||
|
||||
// Setup navigation paths
|
||||
NavigationPath n1 = new NavigationPath("N1", source);
|
||||
n1.Pages = new string[] { p1.FullName };
|
||||
NavigationPath n2 = new NavigationPath(NameTools.GetFullName(ns1.Name, "N1"), source);
|
||||
n2.Pages = new string[] { p2.FullName, p3.FullName };
|
||||
Expect.Call(source.GetNavigationPaths(null)).Return(new NavigationPath[] { n1 });
|
||||
Expect.Call(source.GetNavigationPaths(ns1)).Return(new NavigationPath[] { n2 });
|
||||
Expect.Call(source.GetNavigationPaths(ns2)).Return(new NavigationPath[0]);
|
||||
|
||||
// Setup DESTINATION --------------------------
|
||||
|
||||
// Snippets
|
||||
Expect.Call(destination.AddSnippet(s1.Name, s1.Content)).Return(new Snippet(s1.Name, s1.Content, destination));
|
||||
Expect.Call(source.RemoveSnippet(s1.Name)).Return(true);
|
||||
Expect.Call(destination.AddSnippet(s2.Name, s2.Content)).Return(new Snippet(s2.Name, s2.Content, destination));
|
||||
Expect.Call(source.RemoveSnippet(s2.Name)).Return(true);
|
||||
|
||||
// Content templates
|
||||
Expect.Call(destination.AddContentTemplate(ct1.Name, ct1.Content)).Return(new ContentTemplate(ct1.Name, ct1.Name, destination));
|
||||
Expect.Call(source.RemoveContentTemplate(ct1.Name)).Return(true);
|
||||
Expect.Call(destination.AddContentTemplate(ct2.Name, ct2.Content)).Return(new ContentTemplate(ct2.Name, ct2.Name, destination));
|
||||
Expect.Call(source.RemoveContentTemplate(ct2.Name)).Return(true);
|
||||
|
||||
// Namespaces
|
||||
NamespaceInfo ns1Out = new NamespaceInfo(ns1.Name, destination, null);
|
||||
NamespaceInfo ns2Out = new NamespaceInfo(ns2.Name, destination, null);
|
||||
Expect.Call(destination.AddNamespace(ns1.Name)).Return(ns1Out);
|
||||
Expect.Call(source.RemoveNamespace(ns1)).Return(true);
|
||||
Expect.Call(destination.AddNamespace(ns2.Name)).Return(ns2Out);
|
||||
Expect.Call(source.RemoveNamespace(ns2)).Return(true);
|
||||
|
||||
// Pages/drafts/content/backups/messages
|
||||
PageInfo p1Out = new PageInfo(p1.FullName, destination, p1.CreationDateTime);
|
||||
Expect.Call(destination.AddPage(null, p1.FullName, p1.CreationDateTime)).Return(p1Out);
|
||||
Expect.Call(destination.ModifyPage(p1Out, ctn1.Title, ctn1.User, ctn1.LastModified, ctn1.Comment, ctn1.Content, ctn1.Keywords, ctn1.Description, SaveMode.Normal)).Return(true);
|
||||
Expect.Call(destination.ModifyPage(p1Out, d1.Title, d1.User, d1.LastModified, d1.Comment, d1.Content, d1.Keywords, d1.Description, SaveMode.Draft)).Return(true);
|
||||
Expect.Call(destination.SetBackupContent(bak1_0, 0)).Return(true);
|
||||
Expect.Call(destination.SetBackupContent(bak1_1, 1)).Return(true);
|
||||
Expect.Call(destination.BulkStoreMessages(p1Out, p1m)).Return(true);
|
||||
Expect.Call(source.RemovePage(p1)).Return(true);
|
||||
|
||||
PageInfo p2Out = new PageInfo(p2.FullName, destination, p2.CreationDateTime);
|
||||
Expect.Call(destination.AddPage(NameTools.GetNamespace(p2.FullName), NameTools.GetLocalName(p2.FullName),
|
||||
p2.CreationDateTime)).Return(p2Out);
|
||||
Expect.Call(destination.ModifyPage(p2Out, ctn2.Title, ctn2.User, ctn2.LastModified, ctn2.Comment, ctn2.Content, ctn2.Keywords, ctn2.Description, SaveMode.Normal)).Return(true);
|
||||
Expect.Call(destination.SetBackupContent(bak2_0, 0)).Return(true);
|
||||
Expect.Call(destination.BulkStoreMessages(p2Out, p2m)).Return(true);
|
||||
Expect.Call(source.RemovePage(p2)).Return(true);
|
||||
|
||||
PageInfo p3Out = new PageInfo(p3.FullName, destination, p3.CreationDateTime);
|
||||
Expect.Call(destination.AddPage(NameTools.GetNamespace(p3.FullName), NameTools.GetLocalName(p3.FullName),
|
||||
p3.CreationDateTime)).Return(p3Out);
|
||||
Expect.Call(destination.ModifyPage(p3Out, ctn3.Title, ctn3.User, ctn3.LastModified, ctn3.Comment, ctn3.Content, ctn3.Keywords, ctn3.Description, SaveMode.Normal)).Return(true);
|
||||
Expect.Call(destination.BulkStoreMessages(p3Out, p3m)).Return(true);
|
||||
Expect.Call(source.RemovePage(p3)).Return(true);
|
||||
|
||||
// Categories/bindings
|
||||
CategoryInfo c1Out = new CategoryInfo(c1.FullName, destination);
|
||||
CategoryInfo c2Out = new CategoryInfo(c2.FullName, destination);
|
||||
CategoryInfo c3Out = new CategoryInfo(c3.FullName, destination);
|
||||
Expect.Call(destination.AddCategory(null, c1.FullName)).Return(c1Out);
|
||||
Expect.Call(destination.AddCategory(NameTools.GetNamespace(c2.FullName), NameTools.GetLocalName(c2.FullName))).Return(c2Out);
|
||||
Expect.Call(destination.AddCategory(NameTools.GetNamespace(c3.FullName), NameTools.GetLocalName(c3.FullName))).Return(c3Out);
|
||||
Expect.Call(destination.RebindPage(p1Out, new string[] { c1.FullName })).Return(true);
|
||||
Expect.Call(destination.RebindPage(p2Out, new string[] { c2.FullName })).Return(true);
|
||||
Expect.Call(destination.RebindPage(p3Out, new string[0])).Return(true);
|
||||
Expect.Call(source.RemoveCategory(c1)).Return(true);
|
||||
Expect.Call(source.RemoveCategory(c2)).Return(true);
|
||||
Expect.Call(source.RemoveCategory(c3)).Return(true);
|
||||
|
||||
// Navigation paths
|
||||
NavigationPath n1Out = new NavigationPath(n1.FullName, destination);
|
||||
n1Out.Pages = n1.Pages;
|
||||
NavigationPath n2Out = new NavigationPath(n2.FullName, destination);
|
||||
n2Out.Pages = n2.Pages;
|
||||
|
||||
Expect.Call(destination.AddNavigationPath(null, n1.FullName, new PageInfo[] { p1 })).Return(n1Out).Constraints(
|
||||
RMC.Is.Null(), RMC.Is.Equal(n1.FullName),
|
||||
RMC.Is.Matching<PageInfo[]>(delegate(PageInfo[] array) {
|
||||
return array[0].FullName == p1.FullName;
|
||||
}));
|
||||
|
||||
Expect.Call(destination.AddNavigationPath(NameTools.GetNamespace(n2.FullName), NameTools.GetLocalName(n2.FullName), new PageInfo[] { p2, p3 })).Return(n2Out).Constraints(
|
||||
RMC.Is.Equal(NameTools.GetNamespace(n2.FullName)), RMC.Is.Equal(NameTools.GetLocalName(n2.FullName)),
|
||||
RMC.Is.Matching<PageInfo[]>(delegate(PageInfo[] array) {
|
||||
return array[0].FullName == p2.FullName && array[1].FullName == p3.FullName;
|
||||
}));
|
||||
|
||||
Expect.Call(source.RemoveNavigationPath(n1)).Return(true);
|
||||
Expect.Call(source.RemoveNavigationPath(n2)).Return(true);
|
||||
|
||||
Expect.Call(destination.SetNamespaceDefaultPage(ns1Out, p2Out)).Return(ns1Out);
|
||||
Expect.Call(destination.SetNamespaceDefaultPage(ns2Out, null)).Return(ns2Out);
|
||||
|
||||
// Used for navigation paths
|
||||
Expect.Call(destination.GetPages(null)).Return(new PageInfo[] { p1Out });
|
||||
Expect.Call(destination.GetPages(ns1Out)).Return(new PageInfo[] { p2Out, p3Out });
|
||||
Expect.Call(destination.GetPages(ns2Out)).Return(new PageInfo[0]);
|
||||
|
||||
mocks.Replay(source);
|
||||
mocks.Replay(destination);
|
||||
|
||||
DataMigrator.MigratePagesStorageProviderData(source, destination);
|
||||
|
||||
mocks.Verify(source);
|
||||
mocks.Verify(destination);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MigrateUsersStorageProviderData() {
|
||||
MockRepository mocks = new MockRepository();
|
||||
|
||||
IUsersStorageProviderV30 source = mocks.StrictMock<IUsersStorageProviderV30>();
|
||||
IUsersStorageProviderV30 destination = mocks.StrictMock<IUsersStorageProviderV30>();
|
||||
|
||||
// Setup SOURCE --------------------
|
||||
|
||||
// User groups
|
||||
UserGroup g1 = new UserGroup("G1", "G1", source);
|
||||
UserGroup g2 = new UserGroup("G2", "G2", source);
|
||||
Expect.Call(source.GetUserGroups()).Return(new UserGroup[] { g1, g2 });
|
||||
|
||||
// Users
|
||||
UserInfo u1 = new UserInfo("U1", "U1", "u1@users.com", true, DateTime.Now, source);
|
||||
UserInfo u2 = new UserInfo("U2", "U2", "u2@users.com", true, DateTime.Now, source);
|
||||
UserInfo u3 = new UserInfo("U3", "U3", "u3@users.com", true, DateTime.Now, source);
|
||||
Expect.Call(source.GetUsers()).Return(new UserInfo[] { u1, u2, u3 });
|
||||
|
||||
// Membership
|
||||
g1.Users = new string[] { u1.Username, u2.Username };
|
||||
g2.Users = new string[] { u2.Username, u3.Username };
|
||||
u1.Groups = new string[] { g1.Name };
|
||||
u2.Groups = new string[] { g1.Name, g2.Name };
|
||||
u3.Groups = new string[] { g2.Name };
|
||||
|
||||
// User data
|
||||
IDictionary<string, string> u1Data = new Dictionary<string, string>() {
|
||||
{ "Key1", "Value1" },
|
||||
{ "Key2", "Value2" }
|
||||
};
|
||||
Expect.Call(source.RetrieveAllUserData(u1)).Return(u1Data);
|
||||
Expect.Call(source.RetrieveAllUserData(u2)).Return(new Dictionary<string, string>());
|
||||
Expect.Call(source.RetrieveAllUserData(u3)).Return(new Dictionary<string, string>());
|
||||
|
||||
// Setup DESTINATION ------------------
|
||||
|
||||
// User groups
|
||||
UserGroup g1Out = new UserGroup(g1.Name, g1.Description, destination);
|
||||
UserGroup g2Out = new UserGroup(g2.Name, g2.Description, destination);
|
||||
Expect.Call(destination.AddUserGroup(g1.Name, g1.Description)).Return(g1Out);
|
||||
Expect.Call(destination.AddUserGroup(g2.Name, g2.Description)).Return(g2Out);
|
||||
|
||||
// Users
|
||||
UserInfo u1Out = new UserInfo(u1.Username, u1.DisplayName, u1.Email, u1.Active, u1.DateTime, destination);
|
||||
UserInfo u2Out = new UserInfo(u2.Username, u2.DisplayName, u2.Email, u2.Active, u2.DateTime, destination);
|
||||
UserInfo u3Out = new UserInfo(u3.Username, u3.DisplayName, u3.Email, u3.Active, u3.DateTime, destination);
|
||||
Expect.Call(destination.AddUser(u1.Username, u1.DisplayName, null, u1.Email, u1.Active, u1.DateTime)).Return(u1Out).Constraints(
|
||||
RMC.Is.Equal(u1.Username), RMC.Is.Equal(u1.DisplayName), RMC.Is.Anything(), RMC.Is.Equal(u1.Email), RMC.Is.Equal(u1.Active), RMC.Is.Equal(u1.DateTime));
|
||||
Expect.Call(destination.AddUser(u2.Username, u2.DisplayName, null, u2.Email, u2.Active, u2.DateTime)).Return(u2Out).Constraints(
|
||||
RMC.Is.Equal(u2.Username), RMC.Is.Equal(u2.DisplayName), RMC.Is.Anything(), RMC.Is.Equal(u2.Email), RMC.Is.Equal(u2.Active), RMC.Is.Equal(u2.DateTime));
|
||||
Expect.Call(destination.AddUser(u3.Username, u3.DisplayName, null, u3.Email, u3.Active, u3.DateTime)).Return(u3Out).Constraints(
|
||||
RMC.Is.Equal(u3.Username), RMC.Is.Equal(u3.DisplayName), RMC.Is.Anything(), RMC.Is.Equal(u3.Email), RMC.Is.Equal(u3.Active), RMC.Is.Equal(u3.DateTime));
|
||||
|
||||
// Membership
|
||||
Expect.Call(destination.SetUserMembership(u1Out, u1.Groups)).Return(u1Out);
|
||||
Expect.Call(destination.SetUserMembership(u2Out, u2.Groups)).Return(u2Out);
|
||||
Expect.Call(destination.SetUserMembership(u3Out, u3.Groups)).Return(u3Out);
|
||||
|
||||
// User data
|
||||
Expect.Call(destination.StoreUserData(u1Out, "Key1", "Value1")).Return(true);
|
||||
Expect.Call(destination.StoreUserData(u1Out, "Key2", "Value2")).Return(true);
|
||||
|
||||
// Delete source data
|
||||
Expect.Call(source.RemoveUser(u1)).Return(true);
|
||||
Expect.Call(source.RemoveUser(u2)).Return(true);
|
||||
Expect.Call(source.RemoveUser(u3)).Return(true);
|
||||
Expect.Call(source.RemoveUserGroup(g1)).Return(true);
|
||||
Expect.Call(source.RemoveUserGroup(g2)).Return(true);
|
||||
|
||||
mocks.Replay(source);
|
||||
mocks.Replay(destination);
|
||||
|
||||
DataMigrator.MigrateUsersStorageProviderData(source, destination, false);
|
||||
|
||||
mocks.Verify(source);
|
||||
mocks.Verify(destination);
|
||||
}
|
||||
|
||||
private delegate bool RetrieveFile(string file, Stream stream, bool count);
|
||||
private delegate bool RetrieveAttachment(PageInfo page, string file, Stream stream, bool count);
|
||||
private delegate bool StoreFile(string file, Stream stream, bool overwrite);
|
||||
private delegate bool StoreAttachment(PageInfo page, string file, Stream stream, bool overwrite);
|
||||
|
||||
[Test]
|
||||
public void MigrateFilesStorageProviderData() {
|
||||
MockRepository mocks = new MockRepository();
|
||||
|
||||
IFilesStorageProviderV30 source = mocks.StrictMock<IFilesStorageProviderV30>();
|
||||
IFilesStorageProviderV30 destination = mocks.StrictMock<IFilesStorageProviderV30>();
|
||||
ISettingsStorageProviderV30 settingsProvider = mocks.StrictMock<ISettingsStorageProviderV30>();
|
||||
IAclManager aclManager = mocks.StrictMock<IAclManager>();
|
||||
Expect.Call(settingsProvider.AclManager).Return(aclManager).Repeat.Any();
|
||||
|
||||
// Setup SOURCE -----------------
|
||||
|
||||
// Directories
|
||||
Expect.Call(source.ListDirectories("/")).Return(new string[] { "/Dir1/", "/Dir2/" });
|
||||
Expect.Call(source.ListDirectories("/Dir1/")).Return(new string[] { "/Dir1/Sub/" });
|
||||
Expect.Call(source.ListDirectories("/Dir2/")).Return(new string[0]);
|
||||
Expect.Call(source.ListDirectories("/Dir1/Sub/")).Return(new string[0]);
|
||||
|
||||
// Settings (permissions)
|
||||
Expect.Call(aclManager.RenameResource(
|
||||
Actions.ForDirectories.ResourceMasterPrefix + AuthTools.GetDirectoryName(source, "/"),
|
||||
Actions.ForDirectories.ResourceMasterPrefix + AuthTools.GetDirectoryName(destination, "/"))).Return(true);
|
||||
Expect.Call(aclManager.RenameResource(
|
||||
Actions.ForDirectories.ResourceMasterPrefix + AuthTools.GetDirectoryName(source, "/Dir1/"),
|
||||
Actions.ForDirectories.ResourceMasterPrefix + AuthTools.GetDirectoryName(destination, "/Dir1/"))).Return(true);
|
||||
Expect.Call(aclManager.RenameResource(
|
||||
Actions.ForDirectories.ResourceMasterPrefix + AuthTools.GetDirectoryName(source, "/Dir2/"),
|
||||
Actions.ForDirectories.ResourceMasterPrefix + AuthTools.GetDirectoryName(destination, "/Dir2/"))).Return(true);
|
||||
Expect.Call(aclManager.RenameResource(
|
||||
Actions.ForDirectories.ResourceMasterPrefix + AuthTools.GetDirectoryName(source, "/Dir1/Sub/"),
|
||||
Actions.ForDirectories.ResourceMasterPrefix + AuthTools.GetDirectoryName(destination, "/Dir1/Sub/"))).Return(true);
|
||||
|
||||
// Filenames
|
||||
Expect.Call(source.ListFiles("/")).Return(new string[] { "/File1.txt", "/File2.txt" });
|
||||
Expect.Call(source.ListFiles("/Dir1/")).Return(new string[] { "/Dir1/File.txt" });
|
||||
Expect.Call(source.ListFiles("/Dir2/")).Return(new string[0]);
|
||||
Expect.Call(source.ListFiles("/Dir1/Sub/")).Return(new string[] { "/Dir1/Sub/File.txt" });
|
||||
|
||||
// File content
|
||||
Expect.Call(source.RetrieveFile("/File1.txt", null, false)).Constraints(
|
||||
RMC.Is.Equal("/File1.txt"), RMC.Is.TypeOf<Stream>(), RMC.Is.Equal(false)).Do(
|
||||
new RetrieveFile(
|
||||
delegate(string file, Stream stream, bool count) {
|
||||
byte[] stuff = Encoding.Unicode.GetBytes("content1");
|
||||
stream.Write(stuff, 0, stuff.Length);
|
||||
return true;
|
||||
}));
|
||||
|
||||
Expect.Call(source.RetrieveFile("/File2.txt", null, false)).Constraints(
|
||||
RMC.Is.Equal("/File2.txt"), RMC.Is.TypeOf<Stream>(), RMC.Is.Equal(false)).Do(
|
||||
new RetrieveFile(
|
||||
delegate(string file, Stream stream, bool count) {
|
||||
byte[] stuff = Encoding.Unicode.GetBytes("content2");
|
||||
stream.Write(stuff, 0, stuff.Length);
|
||||
return true;
|
||||
}));
|
||||
|
||||
Expect.Call(source.RetrieveFile("/Dir1/File.txt", null, false)).Constraints(
|
||||
RMC.Is.Equal("/Dir1/File.txt"), RMC.Is.TypeOf<Stream>(), RMC.Is.Equal(false)).Do(
|
||||
new RetrieveFile(
|
||||
delegate(string file, Stream stream, bool count) {
|
||||
byte[] stuff = Encoding.Unicode.GetBytes("content3");
|
||||
stream.Write(stuff, 0, stuff.Length);
|
||||
return true;
|
||||
}));
|
||||
|
||||
Expect.Call(source.RetrieveFile("/Dir1/Sub/File.txt", null, false)).Constraints(
|
||||
RMC.Is.Equal("/Dir1/Sub/File.txt"), RMC.Is.TypeOf<Stream>(), RMC.Is.Equal(false)).Do(
|
||||
new RetrieveFile(
|
||||
delegate(string file, Stream stream, bool count) {
|
||||
byte[] stuff = Encoding.Unicode.GetBytes("content4");
|
||||
stream.Write(stuff, 0, stuff.Length);
|
||||
return true;
|
||||
}));
|
||||
|
||||
// File details
|
||||
Expect.Call(source.GetFileDetails("/File1.txt")).Return(new FileDetails(8, DateTime.Now, 52));
|
||||
Expect.Call(source.GetFileDetails("/File2.txt")).Return(new FileDetails(8, DateTime.Now, 0));
|
||||
Expect.Call(source.GetFileDetails("/Dir1/File.txt")).Return(new FileDetails(8, DateTime.Now, 21));
|
||||
Expect.Call(source.GetFileDetails("/Dir1/Sub/File.txt")).Return(new FileDetails(8, DateTime.Now, 123));
|
||||
|
||||
// Page attachments
|
||||
Expect.Call(source.GetPagesWithAttachments()).Return(new string[] { "MainPage", "Sub.Page", "Sub.Another" });
|
||||
Expect.Call(source.ListPageAttachments(null)).Constraints(RMC.Is.Matching(delegate(PageInfo p) { return p.FullName == "MainPage"; })).Return(new string[] { "Attachment.txt" });
|
||||
Expect.Call(source.ListPageAttachments(null)).Constraints(RMC.Is.Matching(delegate(PageInfo p) { return p.FullName == "Sub.Page"; })).Return(new string[] { "Attachment2.txt" });
|
||||
Expect.Call(source.ListPageAttachments(null)).Constraints(RMC.Is.Matching(delegate(PageInfo p) { return p.FullName == "Sub.Another"; })).Return(new string[0]);
|
||||
|
||||
// Page attachment content
|
||||
Expect.Call(source.RetrievePageAttachment(null, "Attachment.txt", null, false)).Constraints(
|
||||
RMC.Is.Matching(delegate(PageInfo p) { return p.FullName == "MainPage"; }), RMC.Is.Equal("Attachment.txt"), RMC.Is.TypeOf<Stream>(), RMC.Is.Equal(false)).Do(
|
||||
new RetrieveAttachment(
|
||||
delegate(PageInfo page, string name, Stream stream, bool count) {
|
||||
byte[] stuff = Encoding.Unicode.GetBytes("content5");
|
||||
stream.Write(stuff, 0, stuff.Length);
|
||||
return true;
|
||||
}));
|
||||
|
||||
Expect.Call(source.RetrievePageAttachment(null, "Attachment2.txt", null, false)).Constraints(
|
||||
RMC.Is.Matching(delegate(PageInfo p) { return p.FullName == "Sub.Page"; }), RMC.Is.Equal("Attachment2.txt"), RMC.Is.TypeOf<Stream>(), RMC.Is.Equal(false)).Do(
|
||||
new RetrieveAttachment(
|
||||
delegate(PageInfo page, string name, Stream stream, bool count) {
|
||||
byte[] stuff = Encoding.Unicode.GetBytes("content6");
|
||||
stream.Write(stuff, 0, stuff.Length);
|
||||
return true;
|
||||
}));
|
||||
|
||||
// Attachment details
|
||||
Expect.Call(source.GetPageAttachmentDetails(null, "Attachment.txt")).Constraints(
|
||||
RMC.Is.Matching(delegate(PageInfo p) { return p.FullName == "MainPage"; }), RMC.Is.Equal("Attachment.txt")).Return(new FileDetails(8, DateTime.Now, 8));
|
||||
Expect.Call(source.GetPageAttachmentDetails(null, "Attachment2.txt")).Constraints(
|
||||
RMC.Is.Matching(delegate(PageInfo p) { return p.FullName == "Sub.Page"; }), RMC.Is.Equal("Attachment2.txt")).Return(new FileDetails(8, DateTime.Now, 29));
|
||||
|
||||
// Setup DESTINATION ------------------------
|
||||
|
||||
// Directories
|
||||
Expect.Call(destination.CreateDirectory("/", "Dir1")).Return(true);
|
||||
Expect.Call(destination.CreateDirectory("/", "Dir2")).Return(true);
|
||||
Expect.Call(destination.CreateDirectory("/Dir1/", "Sub")).Return(true);
|
||||
|
||||
// Files
|
||||
Expect.Call(destination.StoreFile("/File1.txt", null, false)).Constraints(
|
||||
RMC.Is.Equal("/File1.txt"), RMC.Is.TypeOf<Stream>(), RMC.Is.Equal(false)).Do(new StoreFile(
|
||||
delegate(string name, Stream stream, bool overwrite) {
|
||||
byte[] buff = new byte[512];
|
||||
int read = stream.Read(buff, 0, (int)stream.Length);
|
||||
Assert.AreEqual("content1", Encoding.Unicode.GetString(buff, 0, read), "Wrong data");
|
||||
return true;
|
||||
}));
|
||||
|
||||
Expect.Call(destination.StoreFile("/File2.txt", null, false)).Constraints(
|
||||
RMC.Is.Equal("/File2.txt"), RMC.Is.TypeOf<Stream>(), RMC.Is.Equal(false)).Do(new StoreFile(
|
||||
delegate(string name, Stream stream, bool overwrite) {
|
||||
byte[] buff = new byte[512];
|
||||
int read = stream.Read(buff, 0, (int)stream.Length);
|
||||
Assert.AreEqual("content2", Encoding.Unicode.GetString(buff, 0, read), "Wrong data");
|
||||
return true;
|
||||
}));
|
||||
|
||||
Expect.Call(destination.StoreFile("/Dir1/File.txt", null, false)).Constraints(
|
||||
RMC.Is.Equal("/Dir1/File.txt"), RMC.Is.TypeOf<Stream>(), RMC.Is.Equal(false)).Do(new StoreFile(
|
||||
delegate(string name, Stream stream, bool overwrite) {
|
||||
byte[] buff = new byte[512];
|
||||
int read = stream.Read(buff, 0, (int)stream.Length);
|
||||
Assert.AreEqual("content3", Encoding.Unicode.GetString(buff, 0, read), "Wrong data");
|
||||
return true;
|
||||
}));
|
||||
|
||||
Expect.Call(destination.StoreFile("/Dir1/Sub/File.txt", null, false)).Constraints(
|
||||
RMC.Is.Equal("/Dir1/Sub/File.txt"), RMC.Is.TypeOf<Stream>(), RMC.Is.Equal(false)).Do(new StoreFile(
|
||||
delegate(string name, Stream stream, bool overwrite) {
|
||||
byte[] buff = new byte[512];
|
||||
int read = stream.Read(buff, 0, (int)stream.Length);
|
||||
Assert.AreEqual("content4", Encoding.Unicode.GetString(buff, 0, read), "Wrong data");
|
||||
return true;
|
||||
}));
|
||||
|
||||
// File retrieval count
|
||||
destination.SetFileRetrievalCount("/File1.txt", 52);
|
||||
LastCall.On(destination).Repeat.Once();
|
||||
destination.SetFileRetrievalCount("/File2.txt", 0);
|
||||
LastCall.On(destination).Repeat.Once();
|
||||
destination.SetFileRetrievalCount("/Dir1/File.txt", 21);
|
||||
LastCall.On(destination).Repeat.Once();
|
||||
destination.SetFileRetrievalCount("/Dir1/Sub/File.txt", 123);
|
||||
LastCall.On(destination).Repeat.Once();
|
||||
|
||||
// Page attachments
|
||||
Expect.Call(destination.StorePageAttachment(null, "Attachment.txt", null, false)).Constraints(
|
||||
RMC.Is.Matching(delegate(PageInfo p) { return p.FullName == "MainPage"; }), RMC.Is.Equal("Attachment.txt"), RMC.Is.TypeOf<Stream>(), RMC.Is.Equal(false)).Do(new StoreAttachment(
|
||||
delegate(PageInfo page, string name, Stream stream, bool overwrite) {
|
||||
byte[] buff = new byte[512];
|
||||
int read = stream.Read(buff, 0, (int)stream.Length);
|
||||
Assert.AreEqual("content5", Encoding.Unicode.GetString(buff, 0, read), "Wrong data");
|
||||
return true;
|
||||
}));
|
||||
|
||||
Expect.Call(destination.StorePageAttachment(null, "Attachment2.txt", null, false)).Constraints(
|
||||
RMC.Is.Matching(delegate(PageInfo p) { return p.FullName == "Sub.Page"; }), RMC.Is.Equal("Attachment2.txt"), RMC.Is.TypeOf<Stream>(), RMC.Is.Equal(false)).Do(new StoreAttachment(
|
||||
delegate(PageInfo page, string name, Stream stream, bool overwrite) {
|
||||
byte[] buff = new byte[512];
|
||||
int read = stream.Read(buff, 0, (int)stream.Length);
|
||||
Assert.AreEqual("content6", Encoding.Unicode.GetString(buff, 0, read), "Wrong data");
|
||||
return true;
|
||||
}));
|
||||
|
||||
// Attachment retrieval count
|
||||
destination.SetPageAttachmentRetrievalCount(null, "Attachment.txt", 8);
|
||||
LastCall.On(destination).Constraints(RMC.Is.Matching(delegate(PageInfo p) { return p.FullName == "MainPage"; }), RMC.Is.Equal("Attachment.txt"), RMC.Is.Equal(8)).Repeat.Once();
|
||||
destination.SetPageAttachmentRetrievalCount(null, "Attachment2.txt", 29);
|
||||
LastCall.On(destination).Constraints(RMC.Is.Matching(delegate(PageInfo p) { return p.FullName == "Sub.Page"; }), RMC.Is.Equal("Attachment2.txt"), RMC.Is.Equal(29)).Repeat.Once();
|
||||
|
||||
// Delete source content
|
||||
Expect.Call(source.DeleteFile("/File1.txt")).Return(true);
|
||||
Expect.Call(source.DeleteFile("/File2.txt")).Return(true);
|
||||
Expect.Call(source.DeleteDirectory("/Dir1/")).Return(true);
|
||||
Expect.Call(source.DeleteDirectory("/Dir2/")).Return(true);
|
||||
|
||||
Expect.Call(source.DeletePageAttachment(null, "Attachment.aspx")).Constraints(RMC.Is.Matching(delegate(PageInfo p) { return p.FullName == "MainPage"; }), RMC.Is.Equal("Attachment.txt")).Return(true);
|
||||
Expect.Call(source.DeletePageAttachment(null, "Attachment2.aspx")).Constraints(RMC.Is.Matching(delegate(PageInfo p) { return p.FullName == "Sub.Page"; }), RMC.Is.Equal("Attachment2.txt")).Return(true);
|
||||
|
||||
mocks.Replay(source);
|
||||
mocks.Replay(destination);
|
||||
mocks.Replay(settingsProvider);
|
||||
mocks.Replay(aclManager);
|
||||
|
||||
DataMigrator.MigrateFilesStorageProviderData(source, destination, settingsProvider);
|
||||
|
||||
mocks.Verify(source);
|
||||
mocks.Verify(destination);
|
||||
mocks.Verify(settingsProvider);
|
||||
mocks.Verify(aclManager);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CopySettingsStorageProviderData() {
|
||||
MockRepository mocks = new MockRepository();
|
||||
|
||||
ISettingsStorageProviderV30 source = mocks.StrictMock<ISettingsStorageProviderV30>();
|
||||
ISettingsStorageProviderV30 destination = mocks.StrictMock<ISettingsStorageProviderV30>();
|
||||
IAclManager sourceAclManager = mocks.StrictMock<IAclManager>();
|
||||
IAclManager destinationAclManager = mocks.StrictMock<IAclManager>();
|
||||
|
||||
// Setup SOURCE ---------------------
|
||||
|
||||
// Settings
|
||||
Dictionary<string, string> settings = new Dictionary<string, string>() {
|
||||
{ "Set1", "Value1" },
|
||||
{ "Set2", "Value2" }
|
||||
};
|
||||
Expect.Call(source.GetAllSettings()).Return(settings);
|
||||
|
||||
// Meta-data (global)
|
||||
Expect.Call(source.GetMetaDataItem(MetaDataItem.AccountActivationMessage, null)).Return("AAM");
|
||||
Expect.Call(source.GetMetaDataItem(MetaDataItem.PasswordResetProcedureMessage, null)).Return("PRM");
|
||||
Expect.Call(source.GetMetaDataItem(MetaDataItem.LoginNotice, null)).Return("");
|
||||
Expect.Call(source.GetMetaDataItem(MetaDataItem.PageChangeMessage, null)).Return("PCM");
|
||||
Expect.Call(source.GetMetaDataItem(MetaDataItem.DiscussionChangeMessage, null)).Return("DCM");
|
||||
|
||||
// Meta-data (root)
|
||||
Expect.Call(source.GetMetaDataItem(MetaDataItem.EditNotice, null)).Return("");
|
||||
Expect.Call(source.GetMetaDataItem(MetaDataItem.Footer, null)).Return("FOOT");
|
||||
Expect.Call(source.GetMetaDataItem(MetaDataItem.Header, null)).Return("HEADER");
|
||||
Expect.Call(source.GetMetaDataItem(MetaDataItem.HtmlHead, null)).Return("HTML");
|
||||
Expect.Call(source.GetMetaDataItem(MetaDataItem.PageFooter, null)).Return("P_FOOT");
|
||||
Expect.Call(source.GetMetaDataItem(MetaDataItem.PageHeader, null)).Return("P_HEADER");
|
||||
Expect.Call(source.GetMetaDataItem(MetaDataItem.Sidebar, null)).Return("SIDEBAR");
|
||||
|
||||
// Meta-data ("NS" namespace)
|
||||
Expect.Call(source.GetMetaDataItem(MetaDataItem.EditNotice, "NS")).Return("NS_EDIT");
|
||||
Expect.Call(source.GetMetaDataItem(MetaDataItem.Footer, "NS")).Return("NS_FOOT");
|
||||
Expect.Call(source.GetMetaDataItem(MetaDataItem.Header, "NS")).Return("NS_HEADER");
|
||||
Expect.Call(source.GetMetaDataItem(MetaDataItem.HtmlHead, "NS")).Return("NS_HTML");
|
||||
Expect.Call(source.GetMetaDataItem(MetaDataItem.PageFooter, "NS")).Return("NS_P_FOOT");
|
||||
Expect.Call(source.GetMetaDataItem(MetaDataItem.PageHeader, "NS")).Return("NS_P_HEADER");
|
||||
Expect.Call(source.GetMetaDataItem(MetaDataItem.Sidebar, "NS")).Return("NS_SIDEBAR");
|
||||
|
||||
// Plugin assemblies
|
||||
byte[] asm1 = new byte[] { 1, 2, 3, 4, 5 };
|
||||
byte[] asm2 = new byte[] { 6, 7, 8, 9, 10, 11, 12 };
|
||||
Expect.Call(source.ListPluginAssemblies()).Return(new string[] { "Plugins1.dll", "Plugins2.dll" });
|
||||
Expect.Call(source.RetrievePluginAssembly("Plugins1.dll")).Return(asm1);
|
||||
Expect.Call(source.RetrievePluginAssembly("Plugins2.dll")).Return(asm2);
|
||||
|
||||
// Plugin status
|
||||
Expect.Call(source.GetPluginStatus("Test1.Plugin1")).Return(true);
|
||||
Expect.Call(source.GetPluginStatus("Test2.Plugin2")).Return(false);
|
||||
|
||||
// Plugin config
|
||||
Expect.Call(source.GetPluginConfiguration("Test1.Plugin1")).Return("Config1");
|
||||
Expect.Call(source.GetPluginConfiguration("Test2.Plugin2")).Return("");
|
||||
|
||||
// Outgoing links
|
||||
Dictionary<string, string[]> outgoingLinks = new Dictionary<string, string[]>() {
|
||||
{ "Page1", new string[] { "Page2", "Page3" } },
|
||||
{ "Page2", new string[] { "Page3" } },
|
||||
{ "Page3", new string[] { "Page4", "Page3" } }
|
||||
};
|
||||
Expect.Call(source.GetAllOutgoingLinks()).Return(outgoingLinks);
|
||||
|
||||
// ACLs
|
||||
Expect.Call(source.AclManager).Return(sourceAclManager);
|
||||
AclEntry[] entries = new AclEntry[] {
|
||||
new AclEntry("Res1", "Act1", "Subj1", Value.Grant),
|
||||
new AclEntry("Res2", "Act2", "Subj2", Value.Deny)
|
||||
};
|
||||
Expect.Call(sourceAclManager.RetrieveAllEntries()).Return(entries);
|
||||
|
||||
// Setup DESTINATION -----------------
|
||||
|
||||
// Settings
|
||||
destination.BeginBulkUpdate();
|
||||
LastCall.On(destination).Repeat.Once();
|
||||
foreach(KeyValuePair<string, string> pair in settings) {
|
||||
Expect.Call(destination.SetSetting(pair.Key, pair.Value)).Return(true);
|
||||
}
|
||||
destination.EndBulkUpdate();
|
||||
LastCall.On(destination).Repeat.Once();
|
||||
|
||||
// Meta-data (global)
|
||||
Expect.Call(destination.SetMetaDataItem(MetaDataItem.AccountActivationMessage, null, "AAM")).Return(true);
|
||||
Expect.Call(destination.SetMetaDataItem(MetaDataItem.PasswordResetProcedureMessage, null, "PRM")).Return(true);
|
||||
Expect.Call(destination.SetMetaDataItem(MetaDataItem.LoginNotice, null, "")).Return(true);
|
||||
Expect.Call(destination.SetMetaDataItem(MetaDataItem.PageChangeMessage, null, "PCM")).Return(true);
|
||||
Expect.Call(destination.SetMetaDataItem(MetaDataItem.DiscussionChangeMessage, null, "DCM")).Return(true);
|
||||
|
||||
// Meta-data (root)
|
||||
Expect.Call(destination.SetMetaDataItem(MetaDataItem.EditNotice, null, "")).Return(true);
|
||||
Expect.Call(destination.SetMetaDataItem(MetaDataItem.Footer, null, "FOOT")).Return(true);
|
||||
Expect.Call(destination.SetMetaDataItem(MetaDataItem.Header, null, "HEADER")).Return(true);
|
||||
Expect.Call(destination.SetMetaDataItem(MetaDataItem.HtmlHead, null, "HTML")).Return(true);
|
||||
Expect.Call(destination.SetMetaDataItem(MetaDataItem.PageFooter, null, "P_FOOT")).Return(true);
|
||||
Expect.Call(destination.SetMetaDataItem(MetaDataItem.PageHeader, null, "P_HEADER")).Return(true);
|
||||
Expect.Call(destination.SetMetaDataItem(MetaDataItem.Sidebar, null, "SIDEBAR")).Return(true);
|
||||
|
||||
// Meta-data ("NS" namespace)
|
||||
Expect.Call(destination.SetMetaDataItem(MetaDataItem.EditNotice, "NS", "NS_EDIT")).Return(true);
|
||||
Expect.Call(destination.SetMetaDataItem(MetaDataItem.Footer, "NS", "NS_FOOT")).Return(true);
|
||||
Expect.Call(destination.SetMetaDataItem(MetaDataItem.Header, "NS", "NS_HEADER")).Return(true);
|
||||
Expect.Call(destination.SetMetaDataItem(MetaDataItem.HtmlHead, "NS", "NS_HTML")).Return(true);
|
||||
Expect.Call(destination.SetMetaDataItem(MetaDataItem.PageFooter, "NS", "NS_P_FOOT")).Return(true);
|
||||
Expect.Call(destination.SetMetaDataItem(MetaDataItem.PageHeader, "NS", "NS_P_HEADER")).Return(true);
|
||||
Expect.Call(destination.SetMetaDataItem(MetaDataItem.Sidebar, "NS", "NS_SIDEBAR")).Return(true);
|
||||
|
||||
// Plugin assemblies
|
||||
Expect.Call(destination.StorePluginAssembly("Plugins1.dll", asm1)).Return(true);
|
||||
Expect.Call(destination.StorePluginAssembly("Plugins2.dll", asm2)).Return(true);
|
||||
|
||||
// Plugin status
|
||||
Expect.Call(destination.SetPluginStatus("Test1.Plugin1", true)).Return(true);
|
||||
Expect.Call(destination.SetPluginStatus("Test2.Plugin2", false)).Return(true);
|
||||
|
||||
// Plugin config
|
||||
Expect.Call(destination.SetPluginConfiguration("Test1.Plugin1", "Config1")).Return(true);
|
||||
Expect.Call(destination.SetPluginConfiguration("Test2.Plugin2", "")).Return(true);
|
||||
|
||||
// Outgoing links
|
||||
foreach(KeyValuePair<string, string[]> pair in outgoingLinks) {
|
||||
Expect.Call(destination.StoreOutgoingLinks(pair.Key, pair.Value)).Return(true);
|
||||
}
|
||||
|
||||
// ACLs
|
||||
Expect.Call(destination.AclManager).Return(destinationAclManager).Repeat.Any();
|
||||
foreach(AclEntry e in entries) {
|
||||
Expect.Call(destinationAclManager.StoreEntry(e.Resource, e.Action, e.Subject, e.Value)).Return(true);
|
||||
}
|
||||
|
||||
mocks.ReplayAll();
|
||||
|
||||
DataMigrator.CopySettingsStorageProviderData(source, destination,
|
||||
new string[] { "NS" }, new string[] { "Test1.Plugin1", "Test2.Plugin2" });
|
||||
|
||||
mocks.VerifyAll();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
30
Core-Tests/FilesStorageProviderTests.cs
Normal file
30
Core-Tests/FilesStorageProviderTests.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Rhino.Mocks;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki.Tests {
|
||||
|
||||
public class FilesStorageProviderTests : FilesStorageProviderTestScaffolding {
|
||||
|
||||
public override IFilesStorageProviderV30 GetProvider() {
|
||||
FilesStorageProvider prov = new FilesStorageProvider();
|
||||
prov.Init(MockHost(), "");
|
||||
return prov;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Init() {
|
||||
IFilesStorageProviderV30 prov = GetProvider();
|
||||
prov.Init(MockHost(), "");
|
||||
|
||||
Assert.IsNotNull(prov.Information, "Information should not be null");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
167
Core-Tests/FormatterTests.cs
Normal file
167
Core-Tests/FormatterTests.cs
Normal file
|
@ -0,0 +1,167 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Rhino.Mocks;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki.Tests {
|
||||
|
||||
[TestFixture]
|
||||
public class FormatterTests {
|
||||
|
||||
private MockRepository mocks;
|
||||
|
||||
[Test]
|
||||
public void Format() {
|
||||
FormattingContext context = FormattingContext.PageContent;
|
||||
PageInfo currentPage = null;
|
||||
string[] linkedPages = null;
|
||||
|
||||
string output = Formatter.Format(Input, false, context, currentPage, out linkedPages, false);
|
||||
|
||||
// Ignore \r characters
|
||||
Assert.AreEqual(ExpectedOutput.Replace("\r", ""), output.Replace("\r", ""), "Formatter output is different from expected output");
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() {
|
||||
mocks = new MockRepository();
|
||||
|
||||
ISettingsStorageProviderV30 settingsProvider = mocks.StrictMock<ISettingsStorageProviderV30>();
|
||||
Expect.Call(settingsProvider.GetSetting("ProcessSingleLineBreaks")).Return("false").Repeat.Any();
|
||||
|
||||
Collectors.SettingsProvider = settingsProvider;
|
||||
|
||||
IPagesStorageProviderV30 pagesProvider = mocks.StrictMock<IPagesStorageProviderV30>();
|
||||
Collectors.PagesProviderCollector = new ProviderCollector<IPagesStorageProviderV30>();
|
||||
Collectors.PagesProviderCollector.AddProvider(pagesProvider);
|
||||
|
||||
Expect.Call(settingsProvider.GetSetting("DefaultPagesProvider")).Return(pagesProvider.GetType().FullName).Repeat.Any();
|
||||
|
||||
PageInfo page1 = new PageInfo("page1", pagesProvider, DateTime.Now);
|
||||
PageContent page1Content = new PageContent(page1, "Page 1", "User", DateTime.Now, "Comment", "Content", null, null);
|
||||
Expect.Call(pagesProvider.GetPage("page1")).Return(page1).Repeat.Any();
|
||||
Expect.Call(pagesProvider.GetContent(page1)).Return(page1Content).Repeat.Any();
|
||||
|
||||
Expect.Call(pagesProvider.GetPage("page2")).Return(null).Repeat.Any();
|
||||
|
||||
//Pages.Instance = new Pages();
|
||||
|
||||
Host.Instance = new Host();
|
||||
|
||||
Expect.Call(settingsProvider.GetSetting("CacheSize")).Return("100").Repeat.Any();
|
||||
Expect.Call(settingsProvider.GetSetting("CacheCutSize")).Return("20").Repeat.Any();
|
||||
|
||||
Expect.Call(settingsProvider.GetSetting("DefaultCacheProvider")).Return(typeof(CacheProvider).FullName).Repeat.Any();
|
||||
|
||||
// Cache needs setting to init
|
||||
mocks.Replay(settingsProvider);
|
||||
|
||||
ICacheProviderV30 cacheProvider = new CacheProvider();
|
||||
cacheProvider.Init(Host.Instance, "");
|
||||
Collectors.CacheProviderCollector = new ProviderCollector<ICacheProviderV30>();
|
||||
Collectors.CacheProviderCollector.AddProvider(cacheProvider);
|
||||
|
||||
mocks.Replay(pagesProvider);
|
||||
|
||||
Collectors.FormatterProviderCollector = new ProviderCollector<IFormatterProviderV30>();
|
||||
|
||||
//System.Web.UI.HtmlTextWriter writer = new System.Web.UI.HtmlTextWriter(new System.IO.StreamWriter(new System.IO.MemoryStream()));
|
||||
//System.Web.Hosting.SimpleWorkerRequest request = new System.Web.Hosting.SimpleWorkerRequest("Default.aspx", "?Page=MainPage", writer);
|
||||
System.Web.HttpContext.Current = new System.Web.HttpContext(new DummyRequest());
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown() {
|
||||
mocks.VerifyAll();
|
||||
}
|
||||
|
||||
private const string Input =
|
||||
@"'''bold''' ''italic'' __underlined__ --striked--
|
||||
[page1] [page2|title]
|
||||
|
||||
@@* item 1
|
||||
* item 2
|
||||
second line@@
|
||||
|
||||
{|
|
||||
| cell || other cell
|
||||
|}";
|
||||
|
||||
private const string ExpectedOutput =
|
||||
@"<b>bold</b> <i>italic</i> <u>underlined</u> <strike>striked</strike>
|
||||
<a class=""pagelink"" href=""page1.ashx"" title=""Page 1"">page1</a> <a class=""unknownlink"" href=""page2.ashx"" title=""page2"">title</a><br /><br /><pre>* item 1
|
||||
* item 2
|
||||
second line</pre><br /><table><tr><td>cell</td><td>other cell</td></tr></table>
|
||||
";
|
||||
|
||||
}
|
||||
|
||||
public class DummyRequest : System.Web.HttpWorkerRequest {
|
||||
|
||||
public override void EndOfRequest() {
|
||||
}
|
||||
|
||||
public override void FlushResponse(bool finalFlush) {
|
||||
}
|
||||
|
||||
public override string GetHttpVerbName() {
|
||||
return "GET";
|
||||
}
|
||||
|
||||
public override string GetHttpVersion() {
|
||||
return "1.1";
|
||||
}
|
||||
|
||||
public override string GetLocalAddress() {
|
||||
return "http://localhost/";
|
||||
}
|
||||
|
||||
public override int GetLocalPort() {
|
||||
return 80;
|
||||
}
|
||||
|
||||
public override string GetQueryString() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public override string GetRawUrl() {
|
||||
return "http://localhost/Default.aspx";
|
||||
}
|
||||
|
||||
public override string GetRemoteAddress() {
|
||||
return "127.0.0.1";
|
||||
}
|
||||
|
||||
public override int GetRemotePort() {
|
||||
return 45695;
|
||||
}
|
||||
|
||||
public override string GetUriPath() {
|
||||
return "/";
|
||||
}
|
||||
|
||||
public override void SendKnownResponseHeader(int index, string value) {
|
||||
}
|
||||
|
||||
public override void SendResponseFromFile(IntPtr handle, long offset, long length) {
|
||||
}
|
||||
|
||||
public override void SendResponseFromFile(string filename, long offset, long length) {
|
||||
}
|
||||
|
||||
public override void SendResponseFromMemory(byte[] data, int length) {
|
||||
}
|
||||
|
||||
public override void SendStatus(int statusCode, string statusDescription) {
|
||||
}
|
||||
|
||||
public override void SendUnknownResponseHeader(string name, string value) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
237
Core-Tests/IndexStorerTests.cs
Normal file
237
Core-Tests/IndexStorerTests.cs
Normal file
|
@ -0,0 +1,237 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.SearchEngine.Tests;
|
||||
using NUnit.Framework;
|
||||
using ScrewTurn.Wiki.SearchEngine;
|
||||
|
||||
namespace ScrewTurn.Wiki.Tests {
|
||||
|
||||
[TestFixture]
|
||||
public class IndexStorerTests : TestsBase {
|
||||
|
||||
private string documentsFile = Path.Combine(Environment.GetEnvironmentVariable("TEMP"), "__SE_Documents.dat");
|
||||
private string wordsFile = Path.Combine(Environment.GetEnvironmentVariable("TEMP"), "__SE_Words.dat");
|
||||
private string mappingsFile = Path.Combine(Environment.GetEnvironmentVariable("TEMP"), "__SE_Mappings.dat");
|
||||
|
||||
[TearDown]
|
||||
public void RemoveFiles() {
|
||||
try {
|
||||
File.Delete(documentsFile);
|
||||
}
|
||||
catch { }
|
||||
try {
|
||||
File.Delete(wordsFile);
|
||||
}
|
||||
catch { }
|
||||
try {
|
||||
File.Delete(mappingsFile);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Constructor() {
|
||||
IInMemoryIndex index = MockInMemoryIndex();
|
||||
IndexStorer storer = new IndexStorer(documentsFile, wordsFile, mappingsFile, index);
|
||||
Assert.AreEqual(index, storer.Index, "Wrong index");
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void Constructor_InvalidDocumentsFile(string file) {
|
||||
IndexStorer storer = new IndexStorer(file, wordsFile, mappingsFile, MockInMemoryIndex());
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void Constructor_InvalidWordsFile(string file) {
|
||||
IndexStorer storer = new IndexStorer(documentsFile, file, mappingsFile, MockInMemoryIndex());
|
||||
}
|
||||
|
||||
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
|
||||
[TestCase("", ExpectedException = typeof(ArgumentException))]
|
||||
public void Constructor_InvalidMappingsFile(string file) {
|
||||
IndexStorer storer = new IndexStorer(documentsFile, wordsFile, file, MockInMemoryIndex());
|
||||
}
|
||||
|
||||
[Test]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void Constructor_NullIndex() {
|
||||
IndexStorer storer = new IndexStorer(documentsFile, wordsFile, mappingsFile, null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Size() {
|
||||
IInMemoryIndex index = MockInMemoryIndex();
|
||||
index.SetBuildDocumentDelegate(delegate(DumpedDocument d) { return null; });
|
||||
|
||||
IndexStorer storer = new IndexStorer(documentsFile, wordsFile, mappingsFile, index);
|
||||
storer.LoadIndex();
|
||||
|
||||
Assert.IsTrue(storer.Size > 0, "Size should be greater than zero");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LoadIndexAndModifications() {
|
||||
IInMemoryIndex index = MockInMemoryIndex();
|
||||
IDocument doc1 = MockDocument("doc1", "Document", "doc", DateTime.Now);
|
||||
IDocument doc2 = MockDocument2("doc2", "Article", "doc", DateTime.Now);
|
||||
|
||||
BuildDocument buildDoc = new BuildDocument(delegate(DumpedDocument d) {
|
||||
return d.Name == doc1.Name ? doc1 : doc2;
|
||||
});
|
||||
|
||||
index.SetBuildDocumentDelegate(buildDoc);
|
||||
|
||||
IndexStorer storer = new IndexStorer(documentsFile, wordsFile, mappingsFile, index);
|
||||
storer.LoadIndex();
|
||||
|
||||
index.StoreDocument(doc1, null, "", null);
|
||||
index.StoreDocument(doc2, null, "", null);
|
||||
|
||||
Assert.AreEqual(2, index.TotalDocuments, "Wrong document count");
|
||||
Assert.AreEqual(12, index.TotalWords, "Wrong word count");
|
||||
Assert.AreEqual(12, index.TotalOccurrences, "Wrong occurrence count");
|
||||
|
||||
storer.Dispose();
|
||||
storer = null;
|
||||
|
||||
index = MockInMemoryIndex();
|
||||
index.SetBuildDocumentDelegate(buildDoc);
|
||||
|
||||
storer = new IndexStorer(documentsFile, wordsFile, mappingsFile, index);
|
||||
storer.LoadIndex();
|
||||
|
||||
Assert.AreEqual(2, index.TotalDocuments, "Wrong document count");
|
||||
Assert.AreEqual(12, index.TotalWords, "Wrong word count");
|
||||
Assert.AreEqual(12, index.TotalOccurrences, "Wrong occurrence count");
|
||||
|
||||
SearchResultCollection res = index.Search(new SearchParameters("document content article"));
|
||||
Assert.AreEqual(2, res.Count, "Wrong result count");
|
||||
Assert.AreEqual(2, res[0].Matches.Count, "Wrong matches count");
|
||||
Assert.AreEqual(1, res[1].Matches.Count, "Wrong matches count");
|
||||
|
||||
Assert.AreEqual("document", res[0].Matches[0].Text, "Wrong match text");
|
||||
Assert.AreEqual(0, res[0].Matches[0].FirstCharIndex, "Wrong match first char index");
|
||||
Assert.AreEqual(0, res[0].Matches[0].WordIndex, "Wrong match word index");
|
||||
Assert.AreEqual(WordLocation.Title, res[0].Matches[0].Location, "Wrong match location");
|
||||
|
||||
Assert.AreEqual("content", res[0].Matches[1].Text, "Wrong match text");
|
||||
Assert.AreEqual(13, res[0].Matches[1].FirstCharIndex, "Wrong match first char index");
|
||||
Assert.AreEqual(3, res[0].Matches[1].WordIndex, "Wrong match word index");
|
||||
Assert.AreEqual(WordLocation.Content, res[0].Matches[1].Location, "Wrong match location");
|
||||
|
||||
Assert.AreEqual("article", res[1].Matches[0].Text, "Wrong match text");
|
||||
Assert.AreEqual(0, res[1].Matches[0].FirstCharIndex, "Wrong match first char index");
|
||||
Assert.AreEqual(0, res[1].Matches[0].WordIndex, "Wrong match word index");
|
||||
Assert.AreEqual(WordLocation.Title, res[1].Matches[0].Location, "Wrong match location");
|
||||
|
||||
index.RemoveDocument(doc1, null);
|
||||
|
||||
storer.Dispose();
|
||||
storer = null;
|
||||
|
||||
index = MockInMemoryIndex();
|
||||
index.SetBuildDocumentDelegate(buildDoc);
|
||||
|
||||
storer = new IndexStorer(documentsFile, wordsFile, mappingsFile, index);
|
||||
storer.LoadIndex();
|
||||
|
||||
Assert.AreEqual(1, index.TotalDocuments, "Wrong document count");
|
||||
Assert.AreEqual(7, index.TotalWords, "Wrong word count");
|
||||
Assert.AreEqual(7, index.TotalOccurrences, "Wrong occurrence count");
|
||||
|
||||
res = index.Search(new SearchParameters("document content article"));
|
||||
Assert.AreEqual(1, res.Count, "Wrong result count");
|
||||
Assert.AreEqual(1, res[0].Matches.Count, "Wrong matches count");
|
||||
|
||||
Assert.AreEqual("article", res[0].Matches[0].Text, "Wrong match text");
|
||||
Assert.AreEqual(0, res[0].Matches[0].FirstCharIndex, "Wrong match first char index");
|
||||
Assert.AreEqual(0, res[0].Matches[0].WordIndex, "Wrong match word index");
|
||||
Assert.AreEqual(WordLocation.Title, res[0].Matches[0].Location, "Wrong match location");
|
||||
|
||||
index.Clear(null);
|
||||
|
||||
storer.Dispose();
|
||||
storer = null;
|
||||
|
||||
index = MockInMemoryIndex();
|
||||
index.SetBuildDocumentDelegate(buildDoc);
|
||||
|
||||
storer = new IndexStorer(documentsFile, wordsFile, mappingsFile, index);
|
||||
storer.LoadIndex();
|
||||
|
||||
Assert.AreEqual(0, index.TotalDocuments, "Wrong document count");
|
||||
Assert.AreEqual(0, index.TotalWords, "Wrong word count");
|
||||
Assert.AreEqual(0, index.TotalOccurrences, "Wrong occurrence count");
|
||||
Assert.AreEqual(0, index.Search(new SearchParameters("document")).Count, "Wrong result count");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DataCorrupted() {
|
||||
IInMemoryIndex index = MockInMemoryIndex();
|
||||
IDocument doc1 = MockDocument("doc1", "Document", "doc", DateTime.Now);
|
||||
IDocument doc2 = MockDocument2("doc2", "Article", "doc", DateTime.Now);
|
||||
index.SetBuildDocumentDelegate(delegate(DumpedDocument d) { return d.Name == doc1.Name ? doc1 : doc2; });
|
||||
|
||||
IndexStorer storer = new IndexStorer(documentsFile, wordsFile, mappingsFile, index);
|
||||
storer.LoadIndex();
|
||||
|
||||
index.StoreDocument(doc1, null, "", null);
|
||||
index.StoreDocument(doc2, null, "", null);
|
||||
|
||||
Assert.AreEqual(2, index.TotalDocuments, "Wrong document count");
|
||||
Assert.AreEqual(12, index.TotalWords, "Wrong word count");
|
||||
Assert.AreEqual(12, index.TotalOccurrences, "Wrong occurrence count");
|
||||
|
||||
storer.Dispose();
|
||||
storer = null;
|
||||
|
||||
File.Create(documentsFile).Close();
|
||||
|
||||
index = MockInMemoryIndex();
|
||||
|
||||
storer = new IndexStorer(documentsFile, wordsFile, mappingsFile, index);
|
||||
storer.LoadIndex();
|
||||
|
||||
Assert.IsTrue(storer.DataCorrupted, "DataCorrupted should be true");
|
||||
Assert.AreEqual(0, index.TotalDocuments, "Wrong document count");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ReplaceDocument() {
|
||||
// This test checks that IndexStorer properly discards the old document when a new copy is stored,
|
||||
// even when the title and date/time are different
|
||||
|
||||
IInMemoryIndex index = MockInMemoryIndex();
|
||||
DateTime dt1 = DateTime.Now.AddDays(-1);
|
||||
IDocument doc1 = MockDocument("doc1", "Document", "doc", dt1);
|
||||
IDocument doc2 = MockDocument2("doc1", "Article", "doc", DateTime.Now);
|
||||
index.SetBuildDocumentDelegate(delegate(DumpedDocument d) { return d.DateTime == dt1 ? doc1 : doc2; });
|
||||
|
||||
IndexStorer storer = new IndexStorer(documentsFile, wordsFile, mappingsFile, index);
|
||||
storer.LoadIndex();
|
||||
|
||||
index.StoreDocument(doc1, null, "", null);
|
||||
Assert.AreEqual(1, index.TotalDocuments, "Wrong document count");
|
||||
index.StoreDocument(doc2, null, "", null);
|
||||
Assert.AreEqual(1, index.TotalDocuments, "Wrong document count");
|
||||
|
||||
storer.Dispose();
|
||||
storer = null;
|
||||
|
||||
index = MockInMemoryIndex();
|
||||
index.SetBuildDocumentDelegate(delegate(DumpedDocument d) { return d.DateTime == dt1 ? doc1 : doc2; });
|
||||
|
||||
storer = new IndexStorer(documentsFile, wordsFile, mappingsFile, index);
|
||||
storer.LoadIndex();
|
||||
|
||||
Assert.AreEqual(1, index.TotalDocuments, "Wrong document count");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
163
Core-Tests/PagesStorageProviderTests.cs
Normal file
163
Core-Tests/PagesStorageProviderTests.cs
Normal file
|
@ -0,0 +1,163 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
using NUnit.Framework;
|
||||
using Rhino.Mocks;
|
||||
|
||||
namespace ScrewTurn.Wiki.Tests {
|
||||
|
||||
public class PagesStorageProviderTests : PagesStorageProviderTestScaffolding {
|
||||
|
||||
public override IPagesStorageProviderV30 GetProvider() {
|
||||
PagesStorageProvider prov = new PagesStorageProvider();
|
||||
prov.Init(MockHost(), "");
|
||||
return prov;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Init() {
|
||||
IPagesStorageProviderV30 prov = GetProvider();
|
||||
prov.Init(MockHost(), "");
|
||||
|
||||
Assert.IsNotNull(prov.Information, "Information should not be null");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Init_Upgrade() {
|
||||
string testDir = Path.Combine(Environment.GetEnvironmentVariable("TEMP"), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(testDir);
|
||||
|
||||
MockRepository mocks = new MockRepository();
|
||||
IHostV30 host = mocks.DynamicMock<IHostV30>();
|
||||
Expect.Call(host.GetSettingValue(SettingName.PublicDirectory)).Return(testDir).Repeat.AtLeastOnce();
|
||||
|
||||
Expect.Call(host.UpgradePageStatusToAcl(null, 'L')).IgnoreArguments().Repeat.Twice().Return(true);
|
||||
|
||||
mocks.Replay(host);
|
||||
|
||||
string file = Path.Combine(host.GetSettingValue(SettingName.PublicDirectory), "Pages.cs");
|
||||
string categoriesFile = Path.Combine(host.GetSettingValue(SettingName.PublicDirectory), "Categories.cs");
|
||||
string navPathsFile = Path.Combine(host.GetSettingValue(SettingName.PublicDirectory), "NavigationPaths.cs");
|
||||
string directory = Path.Combine(host.GetSettingValue(SettingName.PublicDirectory), "Pages");
|
||||
string messagesDirectory = Path.Combine(host.GetSettingValue(SettingName.PublicDirectory), "Messages");
|
||||
Directory.CreateDirectory(directory);
|
||||
Directory.CreateDirectory(messagesDirectory);
|
||||
|
||||
// Structure (Keywords and Description are new in v3)
|
||||
// Page Title
|
||||
// Username|DateTime[|Comment] --- Comment is optional
|
||||
// ##PAGE##
|
||||
// Content...
|
||||
|
||||
File.WriteAllText(Path.Combine(directory, "Page1.cs"), "Title1\r\nSYSTEM|2008/10/30 20:20:20|Comment\r\n##PAGE##\r\nContent...");
|
||||
File.WriteAllText(Path.Combine(directory, "Page2.cs"), "Title2\r\nSYSTEM|2008/10/30 20:20:20\r\n##PAGE\r\nContent. [[Page.3]] [Page.3|Link to update].");
|
||||
File.WriteAllText(Path.Combine(directory, "Page.3.cs"), "Title3\r\nSYSTEM|2008/10/30 20:20:20|Comment\r\n##PAGE\r\nContent...");
|
||||
|
||||
// ID|Username|Subject|DateTime|ParentID|Body
|
||||
File.WriteAllText(Path.Combine(messagesDirectory, "Page.3.cs"), "0|User|Hello|2008/10/30 21:21:21|-1|Blah\r\n");
|
||||
|
||||
// Structure
|
||||
// [Namespace.]PageName|PageFile|Status|DateTime
|
||||
File.WriteAllText(file, "Page1|Page1.cs|NORMAL|2008/10/30 20:20:20\r\nPage2|Page2.cs|PUBLIC\r\nPage.3|Page.3.cs|LOCKED");
|
||||
|
||||
File.WriteAllText(categoriesFile, "Cat1|Page.3\r\nCat.2|Page1|Page2\r\n");
|
||||
|
||||
File.WriteAllText(navPathsFile, "Path1|Page1|Page.3\r\nPath2|Page2\r\n");
|
||||
|
||||
PagesStorageProvider prov = new PagesStorageProvider();
|
||||
prov.Init(host, "");
|
||||
|
||||
PageInfo[] pages = prov.GetPages(null);
|
||||
|
||||
Assert.AreEqual(3, pages.Length, "Wrong page count");
|
||||
Assert.AreEqual("Page1", pages[0].FullName, "Wrong name");
|
||||
Assert.AreEqual("Page2", pages[1].FullName, "Wrong name");
|
||||
Assert.AreEqual("Page_3", pages[2].FullName, "Wrong name");
|
||||
//Assert.IsFalse(prov.GetContent(pages[1]).Content.Contains("Page.3"), "Content should not contain 'Page.3'");
|
||||
//Assert.IsTrue(prov.GetContent(pages[1]).Content.Contains("Page_3"), "Content should contain 'Page_3'");
|
||||
|
||||
Message[] messages = prov.GetMessages(pages[2]);
|
||||
Assert.AreEqual(1, messages.Length, "Wrong message count");
|
||||
Assert.AreEqual("Hello", messages[0].Subject, "Wrong subject");
|
||||
|
||||
CategoryInfo[] categories = prov.GetCategories(null);
|
||||
|
||||
Assert.AreEqual(2, categories.Length, "Wrong category count");
|
||||
Assert.AreEqual("Cat1", categories[0].FullName, "Wrong name");
|
||||
Assert.AreEqual(1, categories[0].Pages.Length, "Wrong page count");
|
||||
Assert.AreEqual("Page_3", categories[0].Pages[0], "Wrong page");
|
||||
Assert.AreEqual("Cat_2", categories[1].FullName, "Wrong name");
|
||||
Assert.AreEqual(2, categories[1].Pages.Length, "Wrong page count");
|
||||
Assert.AreEqual("Page1", categories[1].Pages[0], "Wrong page");
|
||||
Assert.AreEqual("Page2", categories[1].Pages[1], "Wrong page");
|
||||
|
||||
NavigationPath[] navPaths = prov.GetNavigationPaths(null);
|
||||
|
||||
Assert.AreEqual(2, navPaths.Length, "Wrong nav path count");
|
||||
Assert.AreEqual("Path1", navPaths[0].FullName, "Wrong name");
|
||||
Assert.AreEqual(2, navPaths[0].Pages.Length, "Wrong page count");
|
||||
Assert.AreEqual("Page1", navPaths[0].Pages[0], "Wrong page");
|
||||
Assert.AreEqual("Page_3", navPaths[0].Pages[1], "Wrong page");
|
||||
Assert.AreEqual(1, navPaths[1].Pages.Length, "Wrong page count");
|
||||
Assert.AreEqual("Page2", navPaths[1].Pages[0], "Wrong page");
|
||||
|
||||
mocks.Verify(host);
|
||||
|
||||
// Simulate another startup - upgrade not needed anymore
|
||||
|
||||
mocks.BackToRecord(host);
|
||||
Expect.Call(host.GetSettingValue(SettingName.PublicDirectory)).Return(testDir).Repeat.AtLeastOnce();
|
||||
Expect.Call(host.UpgradePageStatusToAcl(null, 'L')).IgnoreArguments().Repeat.Times(0).Return(false);
|
||||
|
||||
mocks.Replay(host);
|
||||
|
||||
prov = new PagesStorageProvider();
|
||||
prov.Init(host, "");
|
||||
|
||||
mocks.Verify(host);
|
||||
|
||||
Directory.Delete(testDir, true);
|
||||
}
|
||||
|
||||
/*[Test]
|
||||
public void Init_UpgradeCategories() {
|
||||
string testDir = Path.Combine(Environment.GetEnvironmentVariable("TEMP"), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(testDir);
|
||||
|
||||
MockRepository mocks = new MockRepository();
|
||||
IHost host = mocks.DynamicMock<IHost>();
|
||||
Expect.Call(host.GetSettingValue(SettingName.PublicDirectory)).Return(testDir).Repeat.AtLeastOnce();
|
||||
|
||||
mocks.Replay(host);
|
||||
|
||||
string file = Path.Combine(host.GetSettingValue(SettingName.PublicDirectory), "Categories.cs");
|
||||
string filePages = Path.Combine(host.GetSettingValue(SettingName.PublicDirectory), "Pages.cs");
|
||||
string directory = Path.Combine(host.GetSettingValue(SettingName.PublicDirectory), "Pages");
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
File.WriteAllText(file, "Cat1|Page1\r\nCat.2|Page2|Page3\r\n");
|
||||
|
||||
File.WriteAllText(Path.Combine(directory, "Page1.cs"), "Title1\r\nSYSTEM|2008/10/30 20:20:20|Comment\r\n##PAGE##\r\nContent...");
|
||||
File.WriteAllText(Path.Combine(directory, "Page2.cs"), "Title2\r\nSYSTEM|2008/10/30 20:20:20\r\n##PAGE\r\nContent...");
|
||||
File.WriteAllText(Path.Combine(directory, "Page3.cs"), "Title3\r\nSYSTEM|2008/10/30 20:20:20|Comment\r\n##PAGE\r\nContent...");
|
||||
|
||||
File.WriteAllText(filePages, "Page1|Page1.cs|NORMAL|2008/10/30 20:20:20\r\nPage2|Page2.cs\r\nPage3|Page3.cs|LOCKED");
|
||||
|
||||
PagesStorageProvider prov = new PagesStorageProvider();
|
||||
prov.Init(host, "");
|
||||
|
||||
CategoryInfo[] categories = prov.GetCategories(null);
|
||||
Assert.AreEqual("Cat1", categories[0].FullName, "Wrong name");
|
||||
Assert.AreEqual("Cat_2", categories[1].FullName, "Wrong name");
|
||||
|
||||
Assert.AreEqual(2, categories.Length, "Wrong page count");
|
||||
|
||||
mocks.Verify(host);
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
}
|
18
Core-Tests/Properties/AssemblyInfo.cs
Normal file
18
Core-Tests/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("ScrewTurn Wiki Core Tests")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("c0deeef4-19d7-4b11-9255-f5d7e9ddd16e")]
|
30
Core-Tests/ProviderLoaderTests.cs
Normal file
30
Core-Tests/ProviderLoaderTests.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki.Tests {
|
||||
|
||||
[TestFixture]
|
||||
public class ProviderLoaderTests {
|
||||
|
||||
[TestCase(null, typeof(SettingsStorageProvider))]
|
||||
[TestCase("", typeof(SettingsStorageProvider))]
|
||||
[TestCase("default", typeof(SettingsStorageProvider))]
|
||||
[TestCase("DEfaulT", typeof(SettingsStorageProvider))]
|
||||
[TestCase("ScrewTurn.Wiki.SettingsStorageProvider, ScrewTurn.Wiki.Core", typeof(SettingsStorageProvider))]
|
||||
[TestCase("ScrewTurn.Wiki.SettingsStorageProvider, ScrewTurn.Wiki.Core.dll", typeof(SettingsStorageProvider))]
|
||||
[TestCase("ScrewTurn.Wiki.Tests.TestSettingsStorageProvider, ScrewTurn.Wiki.Core.Tests.dll", typeof(TestSettingsStorageProvider))]
|
||||
[TestCase("glglglglglglg, gfgfgfgfggf.dll", typeof(string), ExpectedException = typeof(ArgumentException))]
|
||||
public void Static_LoadSettingsStorageProvider(string p, Type type) {
|
||||
ISettingsStorageProviderV30 prov = ProviderLoader.LoadSettingsStorageProvider(p);
|
||||
Assert.IsNotNull(prov, "Provider should not be null");
|
||||
// type == prov.GetType() seems to fail due to reflection
|
||||
Assert.AreEqual(type.ToString(), prov.GetType().FullName, "Wrong return type");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
30
Core-Tests/SettingsStorageProviderTests.cs
Normal file
30
Core-Tests/SettingsStorageProviderTests.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
using NUnit.Framework;
|
||||
using Rhino.Mocks;
|
||||
|
||||
namespace ScrewTurn.Wiki.Tests {
|
||||
|
||||
public class SettingsStorageProviderTests : SettingsStorageProviderTestScaffolding {
|
||||
|
||||
public override ISettingsStorageProviderV30 GetProvider() {
|
||||
SettingsStorageProvider prov = new SettingsStorageProvider();
|
||||
prov.Init(MockHost(), "");
|
||||
return prov;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Init() {
|
||||
ISettingsStorageProviderV30 prov = GetProvider();
|
||||
prov.Init(MockHost(), "");
|
||||
|
||||
Assert.IsNotNull(prov.Information, "Information should not be null");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
147
Core-Tests/TestSettingsStorageProvider.cs
Normal file
147
Core-Tests/TestSettingsStorageProvider.cs
Normal file
|
@ -0,0 +1,147 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
using ScrewTurn.Wiki.AclEngine;
|
||||
|
||||
namespace ScrewTurn.Wiki.Tests {
|
||||
|
||||
/// <summary>
|
||||
/// Implements a dummy Settings Storage Provider to use for testing.
|
||||
/// </summary>
|
||||
public class TestSettingsStorageProvider : ISettingsStorageProviderV30 {
|
||||
|
||||
public string GetSetting(string name) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool SetSetting(string name, string value) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void BeginBulkUpdate() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void EndBulkUpdate() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void LogEntry(string message, EntryType entryType, string user) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public LogEntry[] GetLogEntries() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ClearLog() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void CutLog(int size) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public int LogSize {
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public string GetMetaDataItem(MetaDataItem item, string tag) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool SetMetaDataItem(MetaDataItem item, string tag, string content) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public RecentChange[] GetRecentChanges() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool AddRecentChange(string page, string title, string messageSubject, DateTime dateTime, string user, ScrewTurn.Wiki.PluginFramework.Change change, string descr) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Init(IHostV30 host, string config) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Shutdown() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ComponentInformation Information {
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public string ConfigHelpHtml {
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public string[] ListPluginAssemblies() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool StorePluginAssembly(string filename, byte[] assembly) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public byte[] RetrievePluginAssembly(string filename) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool DeletePluginAssembly(string filename) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool SetPluginStatus(string typeName, bool enabled) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool GetPluginStatus(string typeName) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool SetPluginConfiguration(string typeName, string config) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public string GetPluginConfiguration(string typeName) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IAclManager AclManager {
|
||||
get {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public bool StoreOutgoingLinks(string page, string[] outgoingLinks) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public string[] GetOutgoingLinks(string page) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IDictionary<string, string[]> GetAllOutgoingLinks() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool DeleteOutgoingLinks(string page) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool UpdateOutgoingLinksForRename(string oldName, string newName) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IDictionary<string, string> GetAllSettings() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
75
Core-Tests/UsersStorageProviderTests.cs
Normal file
75
Core-Tests/UsersStorageProviderTests.cs
Normal file
|
@ -0,0 +1,75 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Rhino.Mocks;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki.Tests {
|
||||
|
||||
public class UsersStorageProviderTests : UsersStorageProviderTestScaffolding {
|
||||
|
||||
public override IUsersStorageProviderV30 GetProvider() {
|
||||
UsersStorageProvider prov = new UsersStorageProvider();
|
||||
prov.Init(MockHost(), "");
|
||||
return prov;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Init() {
|
||||
IUsersStorageProviderV30 prov = GetProvider();
|
||||
prov.Init(MockHost(), "");
|
||||
|
||||
Assert.IsNotNull(prov.Information, "Information should not be null");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Init_Upgrade() {
|
||||
string testDir = Path.Combine(Environment.GetEnvironmentVariable("TEMP"), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(testDir);
|
||||
|
||||
MockRepository mocks = new MockRepository();
|
||||
IHostV30 host = mocks.DynamicMock<IHostV30>();
|
||||
Expect.Call(host.GetSettingValue(SettingName.PublicDirectory)).Return(testDir).Repeat.AtLeastOnce();
|
||||
Expect.Call(host.GetSettingValue(SettingName.AdministratorsGroup)).Return("Administrators").Repeat.Once();
|
||||
Expect.Call(host.GetSettingValue(SettingName.UsersGroup)).Return("Users").Repeat.Once();
|
||||
|
||||
Expect.Call(host.UpgradeSecurityFlagsToGroupsAcl(null, null)).IgnoreArguments().Repeat.Times(1).Return(true);
|
||||
|
||||
mocks.Replay(host);
|
||||
|
||||
string file = Path.Combine(host.GetSettingValue(SettingName.PublicDirectory), "Users.cs");
|
||||
|
||||
File.WriteAllText(file, "user|PASSHASH|user@users.com|Inactive|2008/10/31 15:15:15|USER\r\nsuperuser|SUPERPASSHASH|superuser@users.com|Active|2008/10/31 15:15:16|ADMIN");
|
||||
|
||||
UsersStorageProvider prov = new UsersStorageProvider();
|
||||
prov.Init(host, "");
|
||||
|
||||
UserInfo[] users = prov.GetUsers();
|
||||
|
||||
Assert.AreEqual(2, users.Length, "Wrong user count");
|
||||
|
||||
Assert.AreEqual("superuser", users[0].Username, "Wrong username");
|
||||
Assert.AreEqual("superuser@users.com", users[0].Email, "Wrong email");
|
||||
Assert.IsTrue(users[0].Active, "User should be active");
|
||||
Assert.AreEqual("2008/10/31 15:15:16", users[0].DateTime.ToString("yyyy'/'MM'/'dd' 'HH':'mm':'ss"), "Wrong date/time");
|
||||
Assert.AreEqual(1, users[0].Groups.Length, "Wrong user count");
|
||||
Assert.AreEqual("Administrators", users[0].Groups[0], "Wrong group");
|
||||
|
||||
Assert.AreEqual("user", users[1].Username, "Wrong username");
|
||||
Assert.AreEqual("user@users.com", users[1].Email, "Wrong email");
|
||||
Assert.IsFalse(users[1].Active, "User should be inactive");
|
||||
Assert.AreEqual("2008/10/31 15:15:15", users[1].DateTime.ToString("yyyy'/'MM'/'dd' 'HH':'mm':'ss"), "Wrong date/time");
|
||||
Assert.AreEqual(1, users[1].Groups.Length, "Wrong user count");
|
||||
Assert.AreEqual("Users", users[1].Groups[0], "Wrong group");
|
||||
|
||||
mocks.Verify(host);
|
||||
|
||||
Directory.Delete(testDir, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
116
Core/AclStorer.cs
Normal file
116
Core/AclStorer.cs
Normal file
|
@ -0,0 +1,116 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.AclEngine;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Implements a file-based ACL Storer.
|
||||
/// </summary>
|
||||
public class AclStorer : AclStorerBase {
|
||||
|
||||
private string file;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:AclStorer" /> class.
|
||||
/// </summary>
|
||||
/// <param name="aclManager">The instance of the ACL Manager to handle.</param>
|
||||
/// <param name="file">The storage file.</param>
|
||||
public AclStorer(IAclManager aclManager, string file)
|
||||
: base(aclManager) {
|
||||
|
||||
if(file == null) throw new ArgumentNullException("file");
|
||||
if(file.Length == 0) throw new ArgumentException("File cannot be empty", "file");
|
||||
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads data from storage.
|
||||
/// </summary>
|
||||
/// <returns>The loaded ACL entries.</returns>
|
||||
protected override AclEntry[] LoadDataInternal() {
|
||||
lock(this) {
|
||||
if(!File.Exists(file)) {
|
||||
File.Create(file).Close();
|
||||
return new AclEntry[0];
|
||||
}
|
||||
|
||||
// Format
|
||||
// Resource|Action|Subject|(1|0)
|
||||
|
||||
string[] lines = File.ReadAllLines(file);
|
||||
|
||||
AclEntry[] result = new AclEntry[lines.Length];
|
||||
|
||||
string[] fields;
|
||||
for(int i = 0; i < lines.Length; i++) {
|
||||
fields = lines[i].Split('|');
|
||||
|
||||
result[i] = new AclEntry(fields[0], fields[1], fields[2], (fields[3] == "1" ? Value.Grant : Value.Deny));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps a <see cref="T:AclEntry" /> into a string.
|
||||
/// </summary>
|
||||
/// <param name="entry">The entry to dump.</param>
|
||||
/// <returns>The resulting string.</returns>
|
||||
private static string DumpAclEntry(AclEntry entry) {
|
||||
return string.Format("{0}|{1}|{2}|{3}", entry.Resource, entry.Action, entry.Subject, (entry.Value == Value.Grant ? "1" : "0"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes some entries.
|
||||
/// </summary>
|
||||
/// <param name="entries">The entries to delete.</param>
|
||||
protected override void DeleteEntries(AclEntry[] entries) {
|
||||
lock(this) {
|
||||
AclEntry[] allEntries = LoadDataInternal();
|
||||
|
||||
StringBuilder sb = new StringBuilder(10000);
|
||||
foreach(AclEntry originalEntry in allEntries) {
|
||||
// If the current entry is not contained in the entries array, then preserve it
|
||||
bool delete = false;
|
||||
foreach(AclEntry entryToDelete in entries) {
|
||||
if(AclEntry.Equals(originalEntry, entryToDelete)) {
|
||||
delete = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!delete) {
|
||||
sb.Append(DumpAclEntry(originalEntry));
|
||||
sb.Append("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
File.WriteAllText(file, sb.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores some entries.
|
||||
/// </summary>
|
||||
/// <param name="entries">The entries to store.</param>
|
||||
protected override void StoreEntries(AclEntry[] entries) {
|
||||
lock(this) {
|
||||
StringBuilder sb = new StringBuilder(100);
|
||||
foreach(AclEntry entry in entries) {
|
||||
sb.Append(DumpAclEntry(entry));
|
||||
sb.Append("\r\n");
|
||||
}
|
||||
|
||||
File.AppendAllText(file, sb.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
242
Core/AuthChecker.cs
Normal file
242
Core/AuthChecker.cs
Normal file
|
@ -0,0 +1,242 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
using ScrewTurn.Wiki.AclEngine;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Utility class for checking permissions and authorizations.
|
||||
/// </summary>
|
||||
/// <remarks>All the methods in this class implement a security bypass for the <i>admin</i> user.</remarks>
|
||||
public static class AuthChecker {
|
||||
|
||||
/// <summary>
|
||||
/// Gets the settings storage provider.
|
||||
/// </summary>
|
||||
private static ISettingsStorageProviderV30 SettingsProvider {
|
||||
get { return Collectors.SettingsProvider; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether an action is allowed for the global resources.
|
||||
/// </summary>
|
||||
/// <param name="action">The action the user is attempting to perform.</param>
|
||||
/// <param name="currentUser">The current user.</param>
|
||||
/// <param name="groups">The groups the user is member of.</param>
|
||||
/// <returns><c>true</c> if the action is allowed.</returns>
|
||||
public static bool CheckActionForGlobals(string action, string currentUser, string[] groups) {
|
||||
if(action == null) throw new ArgumentNullException("action");
|
||||
if(action.Length == 0) throw new ArgumentException("Action cannot be empty", "action");
|
||||
if(!AuthTools.IsValidAction(action, Actions.ForGlobals.All)) throw new ArgumentException("Invalid action", "action");
|
||||
|
||||
if(currentUser == null) throw new ArgumentNullException("currentUser");
|
||||
if(currentUser.Length == 0) throw new ArgumentException("Current User cannot be empty", "currentUser");
|
||||
|
||||
if(groups == null) throw new ArgumentNullException("groups");
|
||||
|
||||
if(currentUser == "admin") return true;
|
||||
|
||||
AclEntry[] entries = SettingsProvider.AclManager.RetrieveEntriesForResource(Actions.ForGlobals.ResourceMasterPrefix);
|
||||
Authorization auth = AclEvaluator.AuthorizeAction(Actions.ForGlobals.ResourceMasterPrefix, action,
|
||||
AuthTools.PrepareUsername(currentUser), AuthTools.PrepareGroups(groups), entries);
|
||||
|
||||
return auth == Authorization.Granted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether an action is allowed for a namespace.
|
||||
/// </summary>
|
||||
/// <param name="nspace">The current namespace (<c>null</c> for the root).</param>
|
||||
/// <param name="action">The action the user is attempting to perform.</param>
|
||||
/// <param name="currentUser">The current user.</param>
|
||||
/// <param name="groups">The groups the user is member of.</param>
|
||||
/// <returns><c>true</c> if the action is allowed, <c>false</c> otherwise.</returns>
|
||||
public static bool CheckActionForNamespace(NamespaceInfo nspace, string action, string currentUser, string[] groups) {
|
||||
if(action == null) throw new ArgumentNullException("action");
|
||||
if(action.Length == 0) throw new ArgumentException("Action cannot be empty", "action");
|
||||
if(!AuthTools.IsValidAction(action, Actions.ForNamespaces.All)) throw new ArgumentException("Invalid action", "action");
|
||||
|
||||
if(currentUser == null) throw new ArgumentNullException("currentUser");
|
||||
if(currentUser.Length == 0) throw new ArgumentException("Current User cannot be empty", "currentUser");
|
||||
|
||||
if(groups == null) throw new ArgumentNullException("groups");
|
||||
|
||||
if(currentUser == "admin") return true;
|
||||
|
||||
string namespaceName = nspace != null ? nspace.Name : "";
|
||||
|
||||
AclEntry[] entries = SettingsProvider.AclManager.RetrieveEntriesForResource(
|
||||
Actions.ForNamespaces.ResourceMasterPrefix + namespaceName);
|
||||
|
||||
Authorization auth = AclEvaluator.AuthorizeAction(Actions.ForNamespaces.ResourceMasterPrefix + namespaceName,
|
||||
action, AuthTools.PrepareUsername(currentUser), AuthTools.PrepareGroups(groups), entries);
|
||||
|
||||
if(auth != Authorization.Unknown) return auth == Authorization.Granted;
|
||||
|
||||
// Try local escalators
|
||||
string[] localEscalators = null;
|
||||
if(Actions.ForNamespaces.LocalEscalators.TryGetValue(action, out localEscalators)) {
|
||||
foreach(string localAction in localEscalators) {
|
||||
bool authorized = CheckActionForNamespace(nspace, localAction, currentUser, groups);
|
||||
if(authorized) return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Try root escalation
|
||||
if(nspace != null) {
|
||||
bool authorized = CheckActionForNamespace(null, action, currentUser, groups);
|
||||
if(authorized) return true;
|
||||
}
|
||||
|
||||
// Try global escalators
|
||||
string[] globalEscalators = null;
|
||||
if(Actions.ForNamespaces.GlobalEscalators.TryGetValue(action, out globalEscalators)) {
|
||||
foreach(string globalAction in globalEscalators) {
|
||||
bool authorized = CheckActionForGlobals(globalAction, currentUser, groups);
|
||||
if(authorized) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether an action is allowed for a page.
|
||||
/// </summary>
|
||||
/// <param name="page">The current page.</param>
|
||||
/// <param name="action">The action the user is attempting to perform.</param>
|
||||
/// <param name="currentUser">The current user.</param>
|
||||
/// <param name="groups">The groups the user is member of.</param>
|
||||
/// <returns><c>true</c> if the action is allowed, <c>false</c> otherwise.</returns>
|
||||
public static bool CheckActionForPage(PageInfo page, string action, string currentUser, string[] groups) {
|
||||
if(page == null) throw new ArgumentNullException("page");
|
||||
|
||||
if(action == null) throw new ArgumentNullException("action");
|
||||
if(action.Length == 0) throw new ArgumentException("Action cannot be empty", "action");
|
||||
if(!AuthTools.IsValidAction(action, Actions.ForPages.All)) throw new ArgumentException("Invalid action", "action");
|
||||
|
||||
if(currentUser == null) throw new ArgumentNullException("currentUser");
|
||||
if(currentUser.Length == 0) throw new ArgumentException("Current User cannot be empty", "currentUser");
|
||||
|
||||
if(groups == null) throw new ArgumentNullException("groups");
|
||||
|
||||
if(currentUser == "admin") return true;
|
||||
|
||||
AclEntry[] entries = SettingsProvider.AclManager.RetrieveEntriesForResource(Actions.ForPages.ResourceMasterPrefix + page.FullName);
|
||||
Authorization auth = AclEvaluator.AuthorizeAction(Actions.ForPages.ResourceMasterPrefix + page.FullName, action,
|
||||
AuthTools.PrepareUsername(currentUser), AuthTools.PrepareGroups(groups), entries);
|
||||
|
||||
if(auth != Authorization.Unknown) return auth == Authorization.Granted;
|
||||
|
||||
// Try local escalators
|
||||
string[] localEscalators = null;
|
||||
if(Actions.ForPages.LocalEscalators.TryGetValue(action, out localEscalators)) {
|
||||
foreach(string localAction in localEscalators) {
|
||||
bool authorized = CheckActionForPage(page, localAction, currentUser, groups);
|
||||
if(authorized) return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Try namespace escalators
|
||||
string[] namespaceEscalators = null;
|
||||
string nsName = NameTools.GetNamespace(page.FullName);
|
||||
NamespaceInfo ns = string.IsNullOrEmpty(nsName) ? null : new NamespaceInfo(nsName, null, null);
|
||||
if(Actions.ForPages.NamespaceEscalators.TryGetValue(action, out namespaceEscalators)) {
|
||||
foreach(string namespaceAction in namespaceEscalators) {
|
||||
bool authorized = CheckActionForNamespace(ns, namespaceAction, currentUser, groups);
|
||||
if(authorized) return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Try global escalators
|
||||
string[] globalEscalators = null;
|
||||
if(Actions.ForPages.GlobalEscalators.TryGetValue(action, out globalEscalators)) {
|
||||
foreach(string globalAction in globalEscalators) {
|
||||
bool authorized = CheckActionForGlobals(globalAction, currentUser, groups);
|
||||
if(authorized) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether an action is allowed for a directory.
|
||||
/// </summary>
|
||||
/// <param name="provider">The provider that manages the directory.</param>
|
||||
/// <param name="directory">The full path of the directory.</param>
|
||||
/// <param name="action">The action the user is attempting to perform.</param>
|
||||
/// <param name="currentUser">The current user.</param>
|
||||
/// <param name="groups">The groups the user is member of.</param>
|
||||
/// <returns><c>true</c> if the action is allowed, <c>false</c> otherwise.</returns>
|
||||
public static bool CheckActionForDirectory(IFilesStorageProviderV30 provider, string directory, string action, string currentUser, string[] groups) {
|
||||
if(provider == null) throw new ArgumentNullException("provider");
|
||||
|
||||
if(directory == null) throw new ArgumentNullException("directory");
|
||||
if(directory.Length == 0) throw new ArgumentException("Directory cannot be empty", "directory");
|
||||
|
||||
if(action == null) throw new ArgumentNullException("action");
|
||||
if(action.Length == 0) throw new ArgumentException("Action cannot be empty", "action");
|
||||
if(!AuthTools.IsValidAction(action, Actions.ForDirectories.All)) throw new ArgumentException("Invalid action", "action");
|
||||
|
||||
if(currentUser == null) throw new ArgumentNullException("currentUser");
|
||||
if(currentUser.Length == 0) throw new ArgumentException("Current User cannot be empty", "currentUser");
|
||||
|
||||
if(groups == null) throw new ArgumentNullException("groups");
|
||||
|
||||
if(currentUser == "admin") return true;
|
||||
|
||||
string resourceName = Actions.ForDirectories.ResourceMasterPrefix + AuthTools.GetDirectoryName(provider, directory);
|
||||
|
||||
AclEntry[] entries = SettingsProvider.AclManager.RetrieveEntriesForResource(resourceName);
|
||||
|
||||
Authorization auth = AclEvaluator.AuthorizeAction(resourceName, action,
|
||||
AuthTools.PrepareUsername(currentUser), AuthTools.PrepareGroups(groups), entries);
|
||||
|
||||
if(auth != Authorization.Unknown) return auth == Authorization.Granted;
|
||||
|
||||
// Try local escalators
|
||||
string[] localEscalators = null;
|
||||
if(Actions.ForDirectories.LocalEscalators.TryGetValue(action, out localEscalators)) {
|
||||
foreach(string localAction in localEscalators) {
|
||||
bool authorized = CheckActionForDirectory(provider, directory, localAction, currentUser, groups);
|
||||
if(authorized) return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Try directory escalation (extract parent directory and check its permissions)
|
||||
// Path manipulation keeps the format used by the caller (leading and trailing slashes are preserved if appropriate)
|
||||
string trimmedDirectory = directory.Trim('/');
|
||||
if(trimmedDirectory.Length > 0) {
|
||||
int slashIndex = trimmedDirectory.LastIndexOf('/');
|
||||
string parentDir = "";
|
||||
if(slashIndex > 0) {
|
||||
// Navigate one level up, using the same slash format as the current one
|
||||
parentDir = (directory.StartsWith("/") ? "/" : "") +
|
||||
trimmedDirectory.Substring(0, slashIndex) + (directory.EndsWith("/") ? "/" : "");
|
||||
}
|
||||
else {
|
||||
// This is the root
|
||||
parentDir = directory.StartsWith("/") ? "/" : "";
|
||||
}
|
||||
bool authorized = CheckActionForDirectory(provider, parentDir, action, currentUser, groups);
|
||||
if(authorized) return true;
|
||||
}
|
||||
|
||||
// Try global escalators
|
||||
string[] globalEscalators = null;
|
||||
if(Actions.ForDirectories.GlobalEscalators.TryGetValue(action, out globalEscalators)) {
|
||||
foreach(string globalAction in globalEscalators) {
|
||||
bool authorized = CheckActionForGlobals(globalAction, currentUser, groups);
|
||||
if(authorized) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
481
Core/AuthReader.cs
Normal file
481
Core/AuthReader.cs
Normal file
|
@ -0,0 +1,481 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.AclEngine;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Utility class for reading permissions and authorizations.
|
||||
/// </summary>
|
||||
public static class AuthReader {
|
||||
|
||||
/// <summary>
|
||||
/// Gets the settings storage provider.
|
||||
/// </summary>
|
||||
private static ISettingsStorageProviderV30 SettingsProvider {
|
||||
get { return Collectors.SettingsProvider; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for global resources that are granted to a group.
|
||||
/// </summary>
|
||||
/// <param name="group">The user group.</param>
|
||||
/// <returns>The granted actions.</returns>
|
||||
public static string[] RetrieveGrantsForGlobals(UserGroup group) {
|
||||
if(group == null) throw new ArgumentNullException("group");
|
||||
|
||||
return RetrieveGrantsForGlobals(AuthTools.PrepareGroup(group.Name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for global resources that are granted to a user.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>The granted actions.</returns>
|
||||
public static string[] RetrieveGrantsForGlobals(UserInfo user) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
|
||||
return RetrieveGrantsForGlobals(AuthTools.PrepareUsername(user.Username));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for global resources that are granted to a subject.
|
||||
/// </summary>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <returns>The granted actions.</returns>
|
||||
private static string[] RetrieveGrantsForGlobals(string subject) {
|
||||
AclEntry[] entries = SettingsProvider.AclManager.RetrieveEntriesForSubject(subject);
|
||||
|
||||
List<string> result = new List<string>(entries.Length);
|
||||
foreach(AclEntry entry in entries) {
|
||||
if(entry.Value == Value.Grant && entry.Resource == Actions.ForGlobals.ResourceMasterPrefix) {
|
||||
result.Add(entry.Action);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for global resources that are denied to a group.
|
||||
/// </summary>
|
||||
/// <param name="group">The user group.</param>
|
||||
/// <returns>The denied actions.</returns>
|
||||
public static string[] RetrieveDenialsForGlobals(UserGroup group) {
|
||||
if(group == null) throw new ArgumentNullException("group");
|
||||
|
||||
return RetrieveDenialsForGlobals(AuthTools.PrepareGroup(group.Name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for global resources that are denied to a user.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>The denied actions.</returns>
|
||||
public static string[] RetrieveDenialsForGlobals(UserInfo user) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
|
||||
return RetrieveDenialsForGlobals(AuthTools.PrepareUsername(user.Username));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for global resources that are denied to a subject.
|
||||
/// </summary>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <returns>The denied actions.</returns>
|
||||
private static string[] RetrieveDenialsForGlobals(string subject) {
|
||||
AclEntry[] entries = SettingsProvider.AclManager.RetrieveEntriesForSubject(subject);
|
||||
|
||||
List<string> result = new List<string>(entries.Length);
|
||||
foreach(AclEntry entry in entries) {
|
||||
if(entry.Value == Value.Deny && entry.Resource == Actions.ForGlobals.ResourceMasterPrefix) {
|
||||
result.Add(entry.Action);
|
||||
}
|
||||
}
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the subjects that have ACL entries set for a namespace.
|
||||
/// </summary>
|
||||
/// <param name="nspace">The namespace (<c>null</c> for the root).</param>
|
||||
/// <returns>The subjects.</returns>
|
||||
public static SubjectInfo[] RetrieveSubjectsForNamespace(NamespaceInfo nspace) {
|
||||
string resourceName = Actions.ForNamespaces.ResourceMasterPrefix;
|
||||
if(nspace != null) resourceName += nspace.Name;
|
||||
|
||||
AclEntry[] entries = SettingsProvider.AclManager.RetrieveEntriesForResource(resourceName);
|
||||
|
||||
List<SubjectInfo> result = new List<SubjectInfo>(entries.Length);
|
||||
|
||||
for(int i = 0; i < entries.Length; i++) {
|
||||
SubjectType type = AuthTools.IsGroup(entries[i].Subject) ? SubjectType.Group : SubjectType.User;
|
||||
|
||||
// Remove the subject qualifier ('U.' or 'G.')
|
||||
string name = entries[i].Subject.Substring(2);
|
||||
|
||||
if(result.Find(delegate(SubjectInfo x) { return x.Name == name && x.Type == type; }) == null) {
|
||||
result.Add(new SubjectInfo(name, type));
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for a namespace that are granted to a group.
|
||||
/// </summary>
|
||||
/// <param name="group">The user group.</param>
|
||||
/// <param name="nspace">The namespace (<c>null</c> for the root).</param>
|
||||
/// <returns>The granted actions.</returns>
|
||||
public static string[] RetrieveGrantsForNamespace(UserGroup group, NamespaceInfo nspace) {
|
||||
if(group == null) throw new ArgumentNullException("group");
|
||||
|
||||
return RetrieveGrantsForNamespace(AuthTools.PrepareGroup(group.Name), nspace);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for a namespace that are granted to a user.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="nspace">The namespace (<c>null</c> for the root).</param>
|
||||
/// <returns>The granted actions.</returns>
|
||||
public static string[] RetrieveGrantsForNamespace(UserInfo user, NamespaceInfo nspace) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
|
||||
return RetrieveGrantsForNamespace(AuthTools.PrepareUsername(user.Username), nspace);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for a namespace that are granted to a subject.
|
||||
/// </summary>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <param name="nspace">The namespace (<c>null</c> for the root).</param>
|
||||
/// <returns>The granted actions.</returns>
|
||||
private static string[] RetrieveGrantsForNamespace(string subject, NamespaceInfo nspace) {
|
||||
string resourceName = Actions.ForNamespaces.ResourceMasterPrefix;
|
||||
if(nspace != null) resourceName += nspace.Name;
|
||||
|
||||
AclEntry[] entries = SettingsProvider.AclManager.RetrieveEntriesForSubject(subject);
|
||||
|
||||
List<string> result = new List<string>(entries.Length);
|
||||
|
||||
foreach(AclEntry entry in entries) {
|
||||
if(entry.Value == Value.Grant && entry.Resource == resourceName) {
|
||||
result.Add(entry.Action);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for a namespace that are denied to a group.
|
||||
/// </summary>
|
||||
/// <param name="group">The user group.</param>
|
||||
/// <param name="nspace">The namespace (<c>null</c> for the root).</param>
|
||||
/// <returns>The denied actions.</returns>
|
||||
public static string[] RetrieveDenialsForNamespace(UserGroup group, NamespaceInfo nspace) {
|
||||
if(group == null) throw new ArgumentNullException("group");
|
||||
|
||||
return RetrieveDenialsForNamespace(AuthTools.PrepareGroup(group.Name), nspace);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for a namespace that are denied to a user.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="nspace">The namespace (<c>null</c> for the root).</param>
|
||||
/// <returns>The denied actions.</returns>
|
||||
public static string[] RetrieveDenialsForNamespace(UserInfo user, NamespaceInfo nspace) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
|
||||
return RetrieveDenialsForNamespace(AuthTools.PrepareUsername(user.Username), nspace);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for a namespace that are denied to a subject.
|
||||
/// </summary>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <param name="nspace">The namespace (<c>null</c> for the root).</param>
|
||||
/// <returns>The denied actions.</returns>
|
||||
private static string[] RetrieveDenialsForNamespace(string subject, NamespaceInfo nspace) {
|
||||
string resourceName = Actions.ForNamespaces.ResourceMasterPrefix;
|
||||
if(nspace != null) resourceName += nspace.Name;
|
||||
|
||||
AclEntry[] entries = SettingsProvider.AclManager.RetrieveEntriesForSubject(subject);
|
||||
|
||||
List<string> result = new List<string>(entries.Length);
|
||||
|
||||
foreach(AclEntry entry in entries) {
|
||||
if(entry.Value == Value.Deny && entry.Resource == resourceName) {
|
||||
result.Add(entry.Action);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the subjects that have ACL entries set for a page.
|
||||
/// </summary>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <returns>The subjects.</returns>
|
||||
public static SubjectInfo[] RetrieveSubjectsForPage(PageInfo page) {
|
||||
if(page == null) throw new ArgumentNullException("page");
|
||||
|
||||
AclEntry[] entries = SettingsProvider.AclManager.RetrieveEntriesForResource(Actions.ForPages.ResourceMasterPrefix + page.FullName);
|
||||
|
||||
List<SubjectInfo> result = new List<SubjectInfo>(entries.Length);
|
||||
|
||||
for(int i = 0; i < entries.Length; i++) {
|
||||
SubjectType type = AuthTools.IsGroup(entries[i].Subject) ? SubjectType.Group : SubjectType.User;
|
||||
|
||||
// Remove the subject qualifier ('U.' or 'G.')
|
||||
string name = entries[i].Subject.Substring(2);
|
||||
|
||||
if(result.Find(delegate(SubjectInfo x) { return x.Name == name && x.Type == type; }) == null) {
|
||||
result.Add(new SubjectInfo(name, type));
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for a page that are granted to a group.
|
||||
/// </summary>
|
||||
/// <param name="group">The user group.</param>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <returns>The granted actions.</returns>
|
||||
public static string[] RetrieveGrantsForPage(UserGroup group, PageInfo page) {
|
||||
if(group == null) throw new ArgumentNullException("group");
|
||||
|
||||
return RetrieveGrantsForPage(AuthTools.PrepareGroup(group.Name), page);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for a page that are granted to a user.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <returns>The granted actions.</returns>
|
||||
public static string[] RetrieveGrantsForPage(UserInfo user, PageInfo page) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
|
||||
return RetrieveGrantsForPage(AuthTools.PrepareUsername(user.Username), page);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for a page that are granted to a subject.
|
||||
/// </summary>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <returns>The granted actions.</returns>
|
||||
private static string[] RetrieveGrantsForPage(string subject, PageInfo page) {
|
||||
if(page == null) throw new ArgumentNullException("page");
|
||||
|
||||
string resourceName = Actions.ForPages.ResourceMasterPrefix + page.FullName;
|
||||
|
||||
AclEntry[] entries = SettingsProvider.AclManager.RetrieveEntriesForSubject(subject);
|
||||
|
||||
List<string> result = new List<string>(entries.Length);
|
||||
|
||||
foreach(AclEntry entry in entries) {
|
||||
if(entry.Value == Value.Grant && entry.Resource == resourceName) {
|
||||
result.Add(entry.Action);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for a page that are denied to a group.
|
||||
/// </summary>
|
||||
/// <param name="group">The user group.</param>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <returns>The granted actions.</returns>
|
||||
public static string[] RetrieveDenialsForPage(UserGroup group, PageInfo page) {
|
||||
if(group == null) throw new ArgumentNullException("group");
|
||||
|
||||
return RetrieveDenialsForPage(AuthTools.PrepareGroup(group.Name), page);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for a page that are denied to a user.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <returns>The granted actions.</returns>
|
||||
public static string[] RetrieveDenialsForPage(UserInfo user, PageInfo page) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
|
||||
return RetrieveDenialsForPage(AuthTools.PrepareUsername(user.Username), page);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for a page that are denied to a subject.
|
||||
/// </summary>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <returns>The granted actions.</returns>
|
||||
private static string[] RetrieveDenialsForPage(string subject, PageInfo page) {
|
||||
if(page == null) throw new ArgumentNullException("page");
|
||||
|
||||
string resourceName = Actions.ForPages.ResourceMasterPrefix + page.FullName;
|
||||
|
||||
AclEntry[] entries = SettingsProvider.AclManager.RetrieveEntriesForSubject(subject);
|
||||
|
||||
List<string> result = new List<string>(entries.Length);
|
||||
|
||||
foreach(AclEntry entry in entries) {
|
||||
if(entry.Value == Value.Deny && entry.Resource == resourceName) {
|
||||
result.Add(entry.Action);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the subjects that have ACL entries set for a directory.
|
||||
/// </summary>
|
||||
/// <param name="provider">The provider.</param>
|
||||
/// <param name="directory">The directory.</param>
|
||||
/// <returns>The subjects.</returns>
|
||||
public static SubjectInfo[] RetrieveSubjectsForDirectory(IFilesStorageProviderV30 provider, string directory) {
|
||||
if(provider == null) throw new ArgumentNullException("provider");
|
||||
if(directory == null) throw new ArgumentNullException("directory");
|
||||
if(directory.Length == 0) throw new ArgumentException("Directory cannot be empty", "directory");
|
||||
|
||||
AclEntry[] entries = SettingsProvider.AclManager.RetrieveEntriesForResource(Actions.ForDirectories.ResourceMasterPrefix + AuthTools.GetDirectoryName(provider, directory));
|
||||
|
||||
List<SubjectInfo> result = new List<SubjectInfo>(entries.Length);
|
||||
|
||||
for(int i = 0; i < entries.Length; i++) {
|
||||
SubjectType type = AuthTools.IsGroup(entries[i].Subject) ? SubjectType.Group : SubjectType.User;
|
||||
|
||||
// Remove the subject qualifier ('U.' or 'G.')
|
||||
string name = entries[i].Subject.Substring(2);
|
||||
|
||||
if(result.Find(delegate(SubjectInfo x) { return x.Name == name && x.Type == type; }) == null) {
|
||||
result.Add(new SubjectInfo(name, type));
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for a directory that are granted to a group.
|
||||
/// </summary>
|
||||
/// <param name="group">The user group.</param>
|
||||
/// <param name="provider">The provider.</param>
|
||||
/// <param name="directory">The directory.</param>
|
||||
/// <returns>The granted actions.</returns>
|
||||
public static string[] RetrieveGrantsForDirectory(UserGroup group, IFilesStorageProviderV30 provider, string directory) {
|
||||
if(group == null) throw new ArgumentNullException("group");
|
||||
|
||||
return RetrieveGrantsForDirectory(AuthTools.PrepareGroup(group.Name), provider, directory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for a directory that are granted to a user.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="provider">The provider.</param>
|
||||
/// <param name="directory">The directory.</param>
|
||||
/// <returns>The granted actions.</returns>
|
||||
public static string[] RetrieveGrantsForDirectory(UserInfo user, IFilesStorageProviderV30 provider, string directory) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
|
||||
return RetrieveGrantsForDirectory(AuthTools.PrepareUsername(user.Username), provider, directory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for a directory that are granted to a subject.
|
||||
/// </summary>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <param name="provider">The provider.</param>
|
||||
/// <param name="directory">The directory.</param>
|
||||
/// <returns>The granted actions.</returns>
|
||||
private static string[] RetrieveGrantsForDirectory(string subject, IFilesStorageProviderV30 provider, string directory) {
|
||||
if(provider == null) throw new ArgumentNullException("provider");
|
||||
if(directory == null) throw new ArgumentNullException("directory");
|
||||
if(directory.Length == 0) throw new ArgumentException("Directory cannot be empty", "directory");
|
||||
|
||||
string resourceName = Actions.ForDirectories.ResourceMasterPrefix + AuthTools.GetDirectoryName(provider, directory);
|
||||
|
||||
AclEntry[] entries = SettingsProvider.AclManager.RetrieveEntriesForSubject(subject);
|
||||
|
||||
List<string> result = new List<string>(entries.Length);
|
||||
|
||||
foreach(AclEntry entry in entries) {
|
||||
if(entry.Value == Value.Grant && entry.Resource == resourceName) {
|
||||
result.Add(entry.Action);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for a directory that are denied to a group.
|
||||
/// </summary>
|
||||
/// <param name="group">The user group.</param>
|
||||
/// <param name="provider">The provider.</param>
|
||||
/// <param name="directory">The directory.</param>
|
||||
/// <returns>The denied actions.</returns>
|
||||
public static string[] RetrieveDenialsForDirectory(UserGroup group, IFilesStorageProviderV30 provider, string directory) {
|
||||
if(group == null) throw new ArgumentNullException("group");
|
||||
|
||||
return RetrieveDenialsForDirectory(AuthTools.PrepareGroup(group.Name), provider, directory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for a directory that are denied to a user.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="provider">The provider.</param>
|
||||
/// <param name="directory">The directory.</param>
|
||||
/// <returns>The denied actions.</returns>
|
||||
public static string[] RetrieveDenialsForDirectory(UserInfo user, IFilesStorageProviderV30 provider, string directory) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
|
||||
return RetrieveDenialsForDirectory(AuthTools.PrepareUsername(user.Username), provider, directory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the actions for a directory that are denied to a subject.
|
||||
/// </summary>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <param name="provider">The provider.</param>
|
||||
/// <param name="directory">The directory.</param>
|
||||
/// <returns>The denied actions.</returns>
|
||||
private static string[] RetrieveDenialsForDirectory(string subject, IFilesStorageProviderV30 provider, string directory) {
|
||||
if(provider == null) throw new ArgumentNullException("provider");
|
||||
if(directory == null) throw new ArgumentNullException("directory");
|
||||
if(directory.Length == 0) throw new ArgumentException("Directory cannot be empty", "directory");
|
||||
|
||||
string resourceName = Actions.ForDirectories.ResourceMasterPrefix + AuthTools.GetDirectoryName(provider, directory);
|
||||
|
||||
AclEntry[] entries = SettingsProvider.AclManager.RetrieveEntriesForSubject(subject);
|
||||
|
||||
List<string> result = new List<string>(entries.Length);
|
||||
|
||||
foreach(AclEntry entry in entries) {
|
||||
if(entry.Value == Value.Deny && entry.Resource == resourceName) {
|
||||
result.Add(entry.Action);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
26
Core/AuthStatus.cs
Normal file
26
Core/AuthStatus.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Lists legal values for authorizations.
|
||||
/// </summary>
|
||||
public enum AuthStatus {
|
||||
/// <summary>
|
||||
/// Grant the action.
|
||||
/// </summary>
|
||||
Grant,
|
||||
/// <summary>
|
||||
/// Deny the action.
|
||||
/// </summary>
|
||||
Deny,
|
||||
/// <summary>
|
||||
/// Delete the permission entry.
|
||||
/// </summary>
|
||||
Delete
|
||||
}
|
||||
|
||||
}
|
95
Core/AuthTools.cs
Normal file
95
Core/AuthTools.cs
Normal file
|
@ -0,0 +1,95 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Implements tools supporting athorization management.
|
||||
/// </summary>
|
||||
public static class AuthTools {
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether an action is valid.
|
||||
/// </summary>
|
||||
/// <param name="action">The action to validate.</param>
|
||||
/// <param name="validActions">The list of valid actions.</param>
|
||||
/// <returns><c>true</c> if the action is valid, <c>false</c> otherwise.</returns>
|
||||
public static bool IsValidAction(string action, string[] validActions) {
|
||||
return Array.Find(validActions, delegate(string s) { return s == action; }) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a subject is a group.
|
||||
/// </summary>
|
||||
/// <param name="subject">The subject to test.</param>
|
||||
/// <returns><c>true</c> if the subject is a group, <c>false</c> if it is a user.</returns>
|
||||
public static bool IsGroup(string subject) {
|
||||
if(subject == null) throw new ArgumentNullException("subject");
|
||||
if(subject.Length < 2) throw new ArgumentException("Subject must contain at least 2 characters", "subject");
|
||||
|
||||
return subject.ToUpperInvariant().StartsWith("G.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepends the proper string to a username.
|
||||
/// </summary>
|
||||
/// <param name="username">The username.</param>
|
||||
/// <returns>The resulting username.</returns>
|
||||
public static string PrepareUsername(string username) {
|
||||
if(username == null) throw new ArgumentNullException("username");
|
||||
if(username.Length == 0) throw new ArgumentException("Username cannot be empty", "username");
|
||||
|
||||
return "U." + username;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepends the proper string to each group name in an array.
|
||||
/// </summary>
|
||||
/// <param name="groups">The group array.</param>
|
||||
/// <returns>The resulting group array.</returns>
|
||||
public static string[] PrepareGroups(string[] groups) {
|
||||
if(groups == null) throw new ArgumentNullException("groups");
|
||||
|
||||
if(groups.Length == 0) return groups;
|
||||
|
||||
string[] result = new string[groups.Length];
|
||||
|
||||
for(int i = 0; i < groups.Length; i++) {
|
||||
if(groups[i] == null) throw new ArgumentNullException("groups");
|
||||
if(groups[i].Length == 0) throw new ArgumentException("Groups cannot contain empty elements", "groups");
|
||||
|
||||
result[i] = PrepareGroup(groups[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepends the proper string to the group name.
|
||||
/// </summary>
|
||||
/// <param name="group">The group name.</param>
|
||||
/// <returns>The result string.</returns>
|
||||
public static string PrepareGroup(string group) {
|
||||
return "G." + group;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the proper full name for a directory.
|
||||
/// </summary>
|
||||
/// <param name="prov">The provider.</param>
|
||||
/// <param name="name">The directory name.</param>
|
||||
/// <returns>The full name (<b>not</b> prepended with <see cref="Actions.ForDirectories.ResourceMasterPrefix" />.</returns>
|
||||
public static string GetDirectoryName(IFilesStorageProviderV30 prov, string name) {
|
||||
if(prov == null) throw new ArgumentNullException("prov");
|
||||
if(name == null) throw new ArgumentNullException("name");
|
||||
if(name.Length == 0) throw new ArgumentException("Name cannot be empty", "name");
|
||||
|
||||
return "(" + prov.GetType().FullName + ")" + name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
668
Core/AuthWriter.cs
Normal file
668
Core/AuthWriter.cs
Normal file
|
@ -0,0 +1,668 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
using ScrewTurn.Wiki.AclEngine;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Utility class for writing permissions and authorizations.
|
||||
/// </summary>
|
||||
public static class AuthWriter {
|
||||
|
||||
private const string Delete = "DELETE";
|
||||
private const string Set = "SET-";
|
||||
|
||||
private const string MessageDeleteSuccess = "Deleted ACL Entry: ";
|
||||
private const string MessageDeleteFailure = "Deletion failed for ACL Entry: ";
|
||||
private const string MessageSetSuccess = "Set ACL Entry: ";
|
||||
private const string MessageSetFailure = "Setting failed for ACL Entry: ";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the settings storage provider.
|
||||
/// </summary>
|
||||
private static ISettingsStorageProviderV30 SettingsProvider {
|
||||
get { return Collectors.SettingsProvider; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a permission for a global resource.
|
||||
/// </summary>
|
||||
/// <param name="status">The authorization status.</param>
|
||||
/// <param name="action">The action of which to modify the authorization status.</param>
|
||||
/// <param name="group">The group subject of the authorization change.</param>
|
||||
/// <returns><c>true</c> if the authorization status is changed, <c>false</c> otherwise.</returns>
|
||||
public static bool SetPermissionForGlobals(AuthStatus status, string action, UserGroup group) {
|
||||
if(group == null) throw new ArgumentNullException("group");
|
||||
|
||||
return SetPermissionForGlobals(status, action, AuthTools.PrepareGroup(group.Name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a permission for a global resource.
|
||||
/// </summary>
|
||||
/// <param name="status">The authorization status.</param>
|
||||
/// <param name="action">The action of which to modify the authorization status.</param>
|
||||
/// <param name="user">The user subject of the authorization change.</param>
|
||||
/// <returns><c>true</c> if the authorization status is changed, <c>false</c> otherwise.</returns>
|
||||
public static bool SetPermissionForGlobals(AuthStatus status, string action, UserInfo user) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
|
||||
return SetPermissionForGlobals(status, action, AuthTools.PrepareUsername(user.Username));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a permission for a global resource.
|
||||
/// </summary>
|
||||
/// <param name="status">The authorization status.</param>
|
||||
/// <param name="action">The action of which to modify the authorization status.</param>
|
||||
/// <param name="subject">The subject of the authorization change.</param>
|
||||
/// <returns><c>true</c> if the authorization status is changed, <c>false</c> otherwise.</returns>
|
||||
private static bool SetPermissionForGlobals(AuthStatus status, string action, string subject) {
|
||||
if(action == null) throw new ArgumentNullException("action");
|
||||
if(action.Length == 0) throw new ArgumentException("Action cannot be empty", "action");
|
||||
if(action != Actions.FullControl && !AuthTools.IsValidAction(action, Actions.ForGlobals.All)) {
|
||||
throw new ArgumentException("Invalid action", "action");
|
||||
}
|
||||
|
||||
if(status == AuthStatus.Delete) {
|
||||
bool done = SettingsProvider.AclManager.DeleteEntry(Actions.ForGlobals.ResourceMasterPrefix, action, subject);
|
||||
|
||||
if(done) {
|
||||
Log.LogEntry(MessageDeleteSuccess + GetLogMessage(Actions.ForGlobals.ResourceMasterPrefix, "",
|
||||
action, subject, Delete), EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
else {
|
||||
Log.LogEntry(MessageDeleteFailure + GetLogMessage(Actions.ForGlobals.ResourceMasterPrefix, "",
|
||||
action, subject, Delete), EntryType.Error, Log.SystemUsername);
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
else {
|
||||
bool done = SettingsProvider.AclManager.StoreEntry(Actions.ForGlobals.ResourceMasterPrefix,
|
||||
action, subject, status == AuthStatus.Grant ? Value.Grant : Value.Deny);
|
||||
|
||||
if(done) {
|
||||
Log.LogEntry(MessageSetSuccess + GetLogMessage(Actions.ForGlobals.ResourceMasterPrefix, "",
|
||||
action, subject, Set + status.ToString()), EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
else {
|
||||
Log.LogEntry(MessageSetFailure + GetLogMessage(Actions.ForGlobals.ResourceMasterPrefix, "",
|
||||
action, subject, Set + status.ToString()), EntryType.Error, Log.SystemUsername);
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a permission for a namespace.
|
||||
/// </summary>
|
||||
/// <param name="status">The authorization status.</param>
|
||||
/// <param name="nspace">The namespace (<c>null</c> for the root).</param>
|
||||
/// <param name="action">The action of which to modify the authorization status.</param>
|
||||
/// <param name="group">The group subject of the authorization change.</param>
|
||||
/// <returns><c>true</c> if the authorization status is changed, <c>false</c> otherwise.</returns>
|
||||
public static bool SetPermissionForNamespace(AuthStatus status, NamespaceInfo nspace, string action, UserGroup group) {
|
||||
if(group == null) throw new ArgumentNullException("group");
|
||||
|
||||
return SetPermissionForNamespace(status, nspace, action, AuthTools.PrepareGroup(group.Name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a permission for a namespace.
|
||||
/// </summary>
|
||||
/// <param name="status">The authorization status.</param>
|
||||
/// <param name="nspace">The namespace (<c>null</c> for the root).</param>
|
||||
/// <param name="action">The action of which to modify the authorization status.</param>
|
||||
/// <param name="user">The user subject of the authorization change.</param>
|
||||
/// <returns><c>true</c> if the authorization status is changed, <c>false</c> otherwise.</returns>
|
||||
public static bool SetPermissionForNamespace(AuthStatus status, NamespaceInfo nspace, string action, UserInfo user) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
|
||||
return SetPermissionForNamespace(status, nspace, action, AuthTools.PrepareUsername(user.Username));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a permission for a namespace.
|
||||
/// </summary>
|
||||
/// <param name="status">The authorization status.</param>
|
||||
/// <param name="nspace">The namespace (<c>null</c> for the root).</param>
|
||||
/// <param name="action">The action of which to modify the authorization status.</param>
|
||||
/// <param name="subject">The subject of the authorization change.</param>
|
||||
/// <returns><c>true</c> if the authorization status is changed, <c>false</c> otherwise.</returns>
|
||||
private static bool SetPermissionForNamespace(AuthStatus status, NamespaceInfo nspace, string action, string subject) {
|
||||
if(action == null) throw new ArgumentNullException("action");
|
||||
if(action.Length == 0) throw new ArgumentException("Action cannot be empty", "action");
|
||||
if(action != Actions.FullControl && !AuthTools.IsValidAction(action, Actions.ForNamespaces.All)) {
|
||||
throw new ArgumentException("Invalid action", "action");
|
||||
}
|
||||
|
||||
string namespaceName = nspace != null ? nspace.Name : "";
|
||||
|
||||
if(status == AuthStatus.Delete) {
|
||||
bool done = SettingsProvider.AclManager.DeleteEntry(Actions.ForNamespaces.ResourceMasterPrefix + namespaceName,
|
||||
action, subject);
|
||||
|
||||
if(done) {
|
||||
Log.LogEntry(MessageDeleteSuccess + GetLogMessage(Actions.ForNamespaces.ResourceMasterPrefix, namespaceName,
|
||||
action, subject, Delete), EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
else {
|
||||
Log.LogEntry(MessageDeleteFailure + GetLogMessage(Actions.ForNamespaces.ResourceMasterPrefix, namespaceName,
|
||||
action, subject, Delete), EntryType.Error, Log.SystemUsername);
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
else {
|
||||
bool done = SettingsProvider.AclManager.StoreEntry(Actions.ForNamespaces.ResourceMasterPrefix + namespaceName,
|
||||
action, subject, status == AuthStatus.Grant ? Value.Grant : Value.Deny);
|
||||
|
||||
if(done) {
|
||||
Log.LogEntry(MessageSetSuccess + GetLogMessage(Actions.ForNamespaces.ResourceMasterPrefix, namespaceName,
|
||||
action, subject, Set + status.ToString()), EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
else {
|
||||
Log.LogEntry(MessageSetFailure + GetLogMessage(Actions.ForNamespaces.ResourceMasterPrefix, namespaceName,
|
||||
action, subject, Set + status.ToString()), EntryType.Error, Log.SystemUsername);
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a permission for a directory.
|
||||
/// </summary>
|
||||
/// <param name="status">The authorization status.</param>
|
||||
/// <param name="provider">The provider that handles the directory.</param>
|
||||
/// <param name="directory">The directory.</param>
|
||||
/// <param name="action">The action of which to modify the authorization status.</param>
|
||||
/// <param name="group">The group subject of the authorization change.</param>
|
||||
/// <returns><c>true</c> if the authorization status is changed, <c>false</c> otherwise.</returns>
|
||||
public static bool SetPermissionForDirectory(AuthStatus status, IFilesStorageProviderV30 provider, string directory, string action, UserGroup group) {
|
||||
if(group == null) throw new ArgumentNullException("group");
|
||||
|
||||
return SetPermissionForDirectory(status, provider, directory, action, AuthTools.PrepareGroup(group.Name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a permission for a directory.
|
||||
/// </summary>
|
||||
/// <param name="status">The authorization status.</param>
|
||||
/// <param name="provider">The provider that handles the directory.</param>
|
||||
/// <param name="directory">The directory.</param>
|
||||
/// <param name="action">The action of which to modify the authorization status.</param>
|
||||
/// <param name="user">The user subject of the authorization change.</param>
|
||||
/// <returns><c>true</c> if the authorization status is changed, <c>false</c> otherwise.</returns>
|
||||
public static bool SetPermissionForDirectory(AuthStatus status, IFilesStorageProviderV30 provider, string directory, string action, UserInfo user) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
|
||||
return SetPermissionForDirectory(status, provider, directory, action, AuthTools.PrepareUsername(user.Username));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a permission for a directory.
|
||||
/// </summary>
|
||||
/// <param name="status">The authorization status.</param>
|
||||
/// <param name="provider">The provider that handles the directory.</param>
|
||||
/// <param name="directory">The directory.</param>
|
||||
/// <param name="action">The action of which to modify the authorization status.</param>
|
||||
/// <param name="subject">The subject of the authorization change.</param>
|
||||
/// <returns><c>true</c> if the authorization status is changed, <c>false</c> otherwise.</returns>
|
||||
private static bool SetPermissionForDirectory(AuthStatus status, IFilesStorageProviderV30 provider, string directory, string action, string subject) {
|
||||
if(provider == null) throw new ArgumentNullException("provider");
|
||||
if(directory == null) throw new ArgumentNullException("directory");
|
||||
if(directory.Length == 0) throw new ArgumentException("Directory cannot be empty", "directory");
|
||||
if(action == null) throw new ArgumentNullException("action");
|
||||
if(action.Length == 0) throw new ArgumentException("Action cannot be empty", "action");
|
||||
if(action != Actions.FullControl && !AuthTools.IsValidAction(action, Actions.ForDirectories.All)) {
|
||||
throw new ArgumentException("Invalid action", "action");
|
||||
}
|
||||
|
||||
string directoryName = AuthTools.GetDirectoryName(provider, directory);
|
||||
|
||||
if(status == AuthStatus.Delete) {
|
||||
bool done = SettingsProvider.AclManager.DeleteEntry(Actions.ForDirectories.ResourceMasterPrefix + directoryName,
|
||||
action, subject);
|
||||
|
||||
if(done) {
|
||||
Log.LogEntry(MessageDeleteSuccess + GetLogMessage(Actions.ForDirectories.ResourceMasterPrefix, directoryName,
|
||||
action, subject, Delete), EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
else {
|
||||
Log.LogEntry(MessageDeleteFailure + GetLogMessage(Actions.ForDirectories.ResourceMasterPrefix, directoryName,
|
||||
action, subject, Delete), EntryType.Error, Log.SystemUsername);
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
else {
|
||||
bool done = SettingsProvider.AclManager.StoreEntry(Actions.ForDirectories.ResourceMasterPrefix + directoryName,
|
||||
action, subject, status == AuthStatus.Grant ? Value.Grant : Value.Deny);
|
||||
|
||||
if(done) {
|
||||
Log.LogEntry(MessageSetSuccess + GetLogMessage(Actions.ForDirectories.ResourceMasterPrefix, directoryName,
|
||||
action, subject, Set + status.ToString()), EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
else {
|
||||
Log.LogEntry(MessageSetFailure + GetLogMessage(Actions.ForDirectories.ResourceMasterPrefix, directoryName,
|
||||
action, subject, Set + status.ToString()), EntryType.Error, Log.SystemUsername);
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a permission for a page.
|
||||
/// </summary>
|
||||
/// <param name="status">The authorization status.</param>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <param name="action">The action of which to modify the authorization status.</param>
|
||||
/// <param name="group">The group subject of the authorization change.</param>
|
||||
/// <returns><c>true</c> if the authorization status is changed, <c>false</c> otherwise.</returns>
|
||||
public static bool SetPermissionForPage(AuthStatus status, PageInfo page, string action, UserGroup group) {
|
||||
if(group == null) throw new ArgumentNullException("group");
|
||||
|
||||
return SetPermissionForPage(status, page, action, AuthTools.PrepareGroup(group.Name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a permission for a page.
|
||||
/// </summary>
|
||||
/// <param name="status">The authorization status.</param>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <param name="action">The action of which to modify the authorization status.</param>
|
||||
/// <param name="user">The user subject of the authorization change.</param>
|
||||
/// <returns><c>true</c> if the authorization status is changed, <c>false</c> otherwise.</returns>
|
||||
public static bool SetPermissionForPage(AuthStatus status, PageInfo page, string action, UserInfo user) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
|
||||
return SetPermissionForPage(status, page, action, AuthTools.PrepareUsername(user.Username));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a permission for a page.
|
||||
/// </summary>
|
||||
/// <param name="status">The authorization status.</param>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <param name="action">The action of which to modify the authorization status.</param>
|
||||
/// <param name="subject">The subject of the authorization change.</param>
|
||||
/// <returns><c>true</c> if the authorization status is changed, <c>false</c> otherwise.</returns>
|
||||
private static bool SetPermissionForPage(AuthStatus status, PageInfo page, string action, string subject) {
|
||||
if(page == null) throw new ArgumentNullException("page");
|
||||
if(action == null) throw new ArgumentNullException("action");
|
||||
if(action.Length == 0) throw new ArgumentException("Action cannot be empty", "action");
|
||||
if(action != Actions.FullControl && !AuthTools.IsValidAction(action, Actions.ForPages.All)) {
|
||||
throw new ArgumentException("Invalid action", "action");
|
||||
}
|
||||
|
||||
if(status == AuthStatus.Delete) {
|
||||
bool done = SettingsProvider.AclManager.DeleteEntry(Actions.ForPages.ResourceMasterPrefix + page.FullName,
|
||||
action, subject);
|
||||
|
||||
if(done) {
|
||||
Log.LogEntry(MessageDeleteSuccess + GetLogMessage(Actions.ForPages.ResourceMasterPrefix, page.FullName,
|
||||
action, subject, Delete), EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
else {
|
||||
Log.LogEntry(MessageDeleteFailure + GetLogMessage(Actions.ForPages.ResourceMasterPrefix, page.FullName,
|
||||
action, subject, Delete), EntryType.Error, Log.SystemUsername);
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
else {
|
||||
bool done = SettingsProvider.AclManager.StoreEntry(Actions.ForPages.ResourceMasterPrefix + page.FullName,
|
||||
action, subject, status == AuthStatus.Grant ? Value.Grant : Value.Deny);
|
||||
|
||||
if(done) {
|
||||
Log.LogEntry(MessageSetSuccess + GetLogMessage(Actions.ForPages.ResourceMasterPrefix, page.FullName,
|
||||
action, subject, Set + status.ToString()), EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
else {
|
||||
Log.LogEntry(MessageSetFailure + GetLogMessage(Actions.ForPages.ResourceMasterPrefix, page.FullName,
|
||||
action, subject, Set + status.ToString()), EntryType.Error, Log.SystemUsername);
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all the ACL Entries for global resources that are bound to a user group.
|
||||
/// </summary>
|
||||
/// <param name="group">The user group.</param>
|
||||
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
|
||||
public static bool RemoveEntriesForGlobals(UserGroup group) {
|
||||
if(group == null) throw new ArgumentNullException("group");
|
||||
|
||||
return RemoveEntriesForGlobals(AuthTools.PrepareGroup(group.Name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all the ACL Entries for global resources that are bound to a user.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
|
||||
public static bool RemoveEntriesForGlobals(UserInfo user) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
|
||||
return RemoveEntriesForGlobals(AuthTools.PrepareUsername(user.Username));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all the ACL Entries for global resources that are bound to a subject.
|
||||
/// </summary>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
|
||||
private static bool RemoveEntriesForGlobals(string subject) {
|
||||
AclEntry[] entries = SettingsProvider.AclManager.RetrieveEntriesForSubject(subject);
|
||||
|
||||
foreach(AclEntry entry in entries) {
|
||||
if(entry.Resource == Actions.ForGlobals.ResourceMasterPrefix) {
|
||||
// This call automatically logs the operation result
|
||||
bool done = SetPermissionForGlobals(AuthStatus.Delete, entry.Action, subject);
|
||||
if(!done) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all the ACL Entries for a namespace that are bound to a user group.
|
||||
/// </summary>
|
||||
/// <param name="group">The user group.</param>
|
||||
/// <param name="nspace">The namespace (<c>null</c> for the root).</param>
|
||||
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
|
||||
public static bool RemoveEntriesForNamespace(UserGroup group, NamespaceInfo nspace) {
|
||||
if(group == null) throw new ArgumentNullException("group");
|
||||
|
||||
return RemoveEntriesForNamespace(AuthTools.PrepareGroup(group.Name), nspace);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all the ACL Entries for a namespace that are bound to a user.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="nspace">The namespace (<c>null</c> for the root).</param>
|
||||
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
|
||||
public static bool RemoveEntriesForNamespace(UserInfo user, NamespaceInfo nspace) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
|
||||
return RemoveEntriesForNamespace(AuthTools.PrepareUsername(user.Username), nspace);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all the ACL Entries for a namespace that are bound to a subject.
|
||||
/// </summary>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <param name="nspace">The namespace (<c>null</c> for the root).</param>
|
||||
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
|
||||
private static bool RemoveEntriesForNamespace(string subject, NamespaceInfo nspace) {
|
||||
string resourceName = Actions.ForNamespaces.ResourceMasterPrefix;
|
||||
if(nspace != null) resourceName += nspace.Name;
|
||||
|
||||
AclEntry[] entries = SettingsProvider.AclManager.RetrieveEntriesForSubject(subject);
|
||||
|
||||
foreach(AclEntry entry in entries) {
|
||||
if(entry.Resource == resourceName) {
|
||||
// This call automatically logs the operation result
|
||||
bool done = SetPermissionForNamespace(AuthStatus.Delete, nspace, entry.Action, subject);
|
||||
if(!done) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all the ACL Entries for a page that are bound to a user group.
|
||||
/// </summary>
|
||||
/// <param name="group">The user group.</param>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
|
||||
public static bool RemoveEntriesForPage(UserGroup group, PageInfo page) {
|
||||
if(group == null) throw new ArgumentNullException("group");
|
||||
|
||||
return RemoveEntriesForPage(AuthTools.PrepareGroup(group.Name), page);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all the ACL Entries for a page that are bound to a user.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
|
||||
public static bool RemoveEntriesForPage(UserInfo user, PageInfo page) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
|
||||
return RemoveEntriesForPage(AuthTools.PrepareUsername(user.Username), page);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all the ACL Entries for a page that are bound to a subject.
|
||||
/// </summary>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
|
||||
private static bool RemoveEntriesForPage(string subject, PageInfo page) {
|
||||
if(page == null) throw new ArgumentNullException("page");
|
||||
|
||||
string resourceName = Actions.ForPages.ResourceMasterPrefix + page.FullName;
|
||||
|
||||
AclEntry[] entries = SettingsProvider.AclManager.RetrieveEntriesForSubject(subject);
|
||||
|
||||
foreach(AclEntry entry in entries) {
|
||||
if(entry.Resource == resourceName) {
|
||||
// This call automatically logs the operation result
|
||||
bool done = SetPermissionForPage(AuthStatus.Delete, page, entry.Action, subject);
|
||||
if(!done) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all the ACL Entries for a directory that are bound to a user group.
|
||||
/// </summary>
|
||||
/// <param name="group">The group.</param>
|
||||
/// <param name="provider">The provider.</param>
|
||||
/// <param name="directory">The directory.</param>
|
||||
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
|
||||
public static bool RemoveEntriesForDirectory(UserGroup group, IFilesStorageProviderV30 provider, string directory) {
|
||||
if(group == null) throw new ArgumentNullException("group");
|
||||
|
||||
return RemoveEntriesForDirectory(AuthTools.PrepareGroup(group.Name), provider, directory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all the ACL Entries for a directory that are bound to a user.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="provider">The provider.</param>
|
||||
/// <param name="directory">The directory.</param>
|
||||
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
|
||||
public static bool RemoveEntriesForDirectory(UserInfo user, IFilesStorageProviderV30 provider, string directory) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
|
||||
return RemoveEntriesForDirectory(AuthTools.PrepareUsername(user.Username), provider, directory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all the ACL Entries for a directory that are bound to a subject.
|
||||
/// </summary>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <param name="provider">The provider.</param>
|
||||
/// <param name="directory">The directory.</param>
|
||||
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
|
||||
private static bool RemoveEntriesForDirectory(string subject, IFilesStorageProviderV30 provider, string directory) {
|
||||
if(provider == null) throw new ArgumentNullException("provider");
|
||||
if(directory == null) throw new ArgumentNullException("directory");
|
||||
if(directory.Length == 0) throw new ArgumentException("Directory cannot be empty", "directory");
|
||||
|
||||
string resourceName = Actions.ForDirectories.ResourceMasterPrefix + AuthTools.GetDirectoryName(provider, directory);
|
||||
|
||||
AclEntry[] entries = SettingsProvider.AclManager.RetrieveEntriesForSubject(subject);
|
||||
|
||||
foreach(AclEntry entry in entries) {
|
||||
if(entry.Resource == resourceName) {
|
||||
// This call automatically logs the operation result
|
||||
bool done = SetPermissionForDirectory(AuthStatus.Delete, provider, directory, entry.Action, subject);
|
||||
if(!done) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all the ACL entries for a directory.
|
||||
/// </summary>
|
||||
/// <param name="provider">The provider.</param>
|
||||
/// <param name="directory">The directory.</param>
|
||||
public static void ClearEntriesForDirectory(IFilesStorageProviderV30 provider, string directory) {
|
||||
if(provider == null) throw new ArgumentNullException("provider");
|
||||
|
||||
if(directory == null) throw new ArgumentNullException("directory");
|
||||
if(directory.Length == 0) throw new ArgumentException("Directory cannot be empty", "directory");
|
||||
|
||||
string resourceName = Actions.ForDirectories.ResourceMasterPrefix + AuthTools.GetDirectoryName(provider, directory);
|
||||
|
||||
SettingsProvider.AclManager.DeleteEntriesForResource(resourceName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all the ACL entries for a namespace.
|
||||
/// </summary>
|
||||
/// <param name="nspace">The namespace.</param>
|
||||
/// <param name="pages">The local names of the pages in the namespace.</param>
|
||||
public static void ClearEntriesForNamespace(string nspace, List<string> pages) {
|
||||
if(nspace == null) throw new ArgumentNullException("nspac");
|
||||
if(nspace.Length == 0) throw new ArgumentException("Namespace cannot be empty", "nspace");
|
||||
|
||||
if(pages == null) throw new ArgumentNullException("pages");
|
||||
|
||||
foreach(string p in pages) {
|
||||
if(p == null) throw new ArgumentNullException("pages");
|
||||
if(p.Length == 0) throw new ArgumentException("Page Element cannot be empty", "pages");
|
||||
}
|
||||
|
||||
string resourceName;
|
||||
|
||||
foreach(string p in pages) {
|
||||
resourceName = Actions.ForPages.ResourceMasterPrefix + NameTools.GetFullName(nspace, p);
|
||||
SettingsProvider.AclManager.DeleteEntriesForResource(resourceName);
|
||||
}
|
||||
|
||||
resourceName = Actions.ForNamespaces.ResourceMasterPrefix + nspace;
|
||||
SettingsProvider.AclManager.DeleteEntriesForResource(resourceName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all the ACL entries for a page.
|
||||
/// </summary>
|
||||
/// <param name="page">The page full name.</param>
|
||||
public static void ClearEntriesForPage(string page) {
|
||||
if(page == null) throw new ArgumentNullException("page");
|
||||
if(page.Length == 0) throw new ArgumentException("Page cannot be empty", "page");
|
||||
|
||||
string resourceName = Actions.ForPages.ResourceMasterPrefix + page;
|
||||
|
||||
SettingsProvider.AclManager.DeleteEntriesForResource(resourceName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes the renaming of a directory.
|
||||
/// </summary>
|
||||
/// <param name="provider">The provider.</param>
|
||||
/// <param name="oldName">The old directory name (full path).</param>
|
||||
/// <param name="newName">The new directory name (full path).</param>
|
||||
/// <returns><c>true</c> if the operation completed successfully, <c>false</c> otherwise.</returns>
|
||||
/// <remarks>The method <b>does not</b> recurse in sub-directories.</remarks>
|
||||
public static bool ProcessDirectoryRenaming(IFilesStorageProviderV30 provider, string oldName, string newName) {
|
||||
if(provider == null) throw new ArgumentNullException("provider");
|
||||
|
||||
if(oldName == null) throw new ArgumentNullException("oldName");
|
||||
if(oldName.Length == 0) throw new ArgumentException("Old Name cannot be empty", "oldName");
|
||||
|
||||
if(newName == null) throw new ArgumentNullException("newName");
|
||||
if(newName.Length == 0) throw new ArgumentException("New Name cannot be empty", "newName");
|
||||
|
||||
return SettingsProvider.AclManager.RenameResource(
|
||||
Actions.ForDirectories.ResourceMasterPrefix + AuthTools.GetDirectoryName(provider, oldName),
|
||||
Actions.ForDirectories.ResourceMasterPrefix + AuthTools.GetDirectoryName(provider, newName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes the renaming of a namespace.
|
||||
/// </summary>
|
||||
/// <param name="oldName">The old name of the namespace.</param>
|
||||
/// <param name="oldPages">The list of local names of the pages in the renamed namespace.</param>
|
||||
/// <param name="newName">The new name of the namespace.</param>
|
||||
/// <returns><c>true</c> if the operation completed successfully, <c>false</c> otherwise.</returns>
|
||||
public static bool ProcessNamespaceRenaming(string oldName, List<string> oldPages, string newName) {
|
||||
if(oldName == null) throw new ArgumentNullException("oldName");
|
||||
if(oldName.Length == 0) throw new ArgumentException("Old Name cannot be empty", "oldName");
|
||||
|
||||
if(oldPages == null) throw new ArgumentNullException("oldPages");
|
||||
foreach(string p in oldPages) {
|
||||
if(p == null) throw new ArgumentNullException("oldPages");
|
||||
if(p.Length == 0) throw new ArgumentException("Page cannot be empty", "oldPages");
|
||||
}
|
||||
|
||||
if(newName == null) throw new ArgumentNullException("newName");
|
||||
if(newName.Length == 0) throw new ArgumentException("New Name cannot be empty", "newName");
|
||||
|
||||
foreach(string p in oldPages) {
|
||||
SettingsProvider.AclManager.RenameResource(
|
||||
Actions.ForPages.ResourceMasterPrefix + NameTools.GetFullName(oldName, p),
|
||||
Actions.ForPages.ResourceMasterPrefix + NameTools.GetFullName(newName, p));
|
||||
}
|
||||
|
||||
return SettingsProvider.AclManager.RenameResource(
|
||||
Actions.ForNamespaces.ResourceMasterPrefix + oldName,
|
||||
Actions.ForNamespaces.ResourceMasterPrefix + newName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes the renaming of a page.
|
||||
/// </summary>
|
||||
/// <param name="oldName">The old full page name.</param>
|
||||
/// <param name="newName">The new full page name.</param>
|
||||
/// <returns><c>true</c> if the operation completed successfully, <c>false</c> otherwise.</returns>
|
||||
public static bool ProcessPageRenaming(string oldName, string newName) {
|
||||
if(oldName == null) throw new ArgumentNullException("oldName");
|
||||
if(oldName.Length == 0) throw new ArgumentException("Old Name cannot be empty", "oldName");
|
||||
|
||||
if(newName == null) throw new ArgumentNullException("newName");
|
||||
if(newName.Length == 0) throw new ArgumentException("New Name cannot be empty", "newName");
|
||||
|
||||
return SettingsProvider.AclManager.RenameResource(
|
||||
Actions.ForPages.ResourceMasterPrefix + oldName,
|
||||
Actions.ForPages.ResourceMasterPrefix + newName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the log message for an ACL entry change.
|
||||
/// </summary>
|
||||
/// <param name="resourcePrefix">The resource prefix.</param>
|
||||
/// <param name="resource">The resource name.</param>
|
||||
/// <param name="action">The action.</param>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <param name="status">The status.</param>
|
||||
/// <returns>The message.</returns>
|
||||
private static string GetLogMessage(string resourcePrefix, string resource, string action, string subject, string status) {
|
||||
return resourcePrefix + resource + ":" + action + ":" + subject + "->" + status;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
151
Core/BreadcrumbsManager.cs
Normal file
151
Core/BreadcrumbsManager.cs
Normal file
|
@ -0,0 +1,151 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
using System.Web;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Manages navigation Breadcrumbs.
|
||||
/// </summary>
|
||||
public class BreadcrumbsManager {
|
||||
|
||||
private const int MaxPages = 10;
|
||||
private const string CookieName = "ScrewTurnWikiBreadcrumbs3";
|
||||
private const string CookieValue = "B";
|
||||
|
||||
private List<PageInfo> pages;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cookie.
|
||||
/// </summary>
|
||||
/// <returns>The cookie, or <c>null</c>.</returns>
|
||||
private HttpCookie GetCookie() {
|
||||
if(HttpContext.Current.Request != null) {
|
||||
HttpCookie cookie = HttpContext.Current.Request.Cookies[CookieName];
|
||||
return cookie;
|
||||
}
|
||||
else return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <b>BreadcrumbsManager</b> class.
|
||||
/// </summary>
|
||||
public BreadcrumbsManager() {
|
||||
pages = new List<PageInfo>(MaxPages);
|
||||
|
||||
HttpCookie cookie = GetCookie();
|
||||
if(cookie != null && !string.IsNullOrEmpty(cookie.Values[CookieValue])) {
|
||||
try {
|
||||
foreach(string p in cookie.Values[CookieValue].Split('|')) {
|
||||
PageInfo page = Pages.FindPage(p);
|
||||
if(page != null) pages.Add(page);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the cookie.
|
||||
/// </summary>
|
||||
private void UpdateCookie() {
|
||||
HttpCookie cookie = GetCookie();
|
||||
if(cookie == null) {
|
||||
cookie = new HttpCookie(CookieName);
|
||||
}
|
||||
cookie.Path = Settings.CookiePath;
|
||||
|
||||
StringBuilder sb = new StringBuilder(MaxPages * 20);
|
||||
for(int i = 0; i < pages.Count; i++) {
|
||||
sb.Append(pages[i].FullName);
|
||||
if(i != pages.Count - 1) sb.Append("|");
|
||||
}
|
||||
|
||||
cookie.Values[CookieValue] = sb.ToString();
|
||||
if(HttpContext.Current.Response != null) {
|
||||
HttpContext.Current.Response.Cookies.Set(cookie);
|
||||
}
|
||||
if(HttpContext.Current.Request != null) {
|
||||
HttpContext.Current.Request.Cookies.Set(cookie);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a Page to the Breadcrumbs trail.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page to add.</param>
|
||||
public void AddPage(PageInfo page) {
|
||||
lock(this) {
|
||||
int index = FindPage(page);
|
||||
if(index != -1) pages.RemoveAt(index);
|
||||
pages.Add(page);
|
||||
if(pages.Count > MaxPages) pages.RemoveRange(0, pages.Count - MaxPages);
|
||||
|
||||
UpdateCookie();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a page by name.
|
||||
/// </summary>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <returns>The index in the collection.</returns>
|
||||
private int FindPage(PageInfo page) {
|
||||
lock(this) {
|
||||
if(pages == null || pages.Count == 0) return -1;
|
||||
|
||||
PageNameComparer comp = new PageNameComparer();
|
||||
for(int i = 0; i < pages.Count; i++) {
|
||||
if(comp.Compare(pages[i], page) == 0) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a Page from the Breadcrumbs trail.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page to remove.</param>
|
||||
public void RemovePage(PageInfo page) {
|
||||
lock(this) {
|
||||
int index = FindPage(page);
|
||||
if(index >= 0) pages.RemoveAt(index);
|
||||
|
||||
UpdateCookie();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the Breadcrumbs trail.
|
||||
/// </summary>
|
||||
public void Clear() {
|
||||
lock(this) {
|
||||
pages.Clear();
|
||||
|
||||
UpdateCookie();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the Pages in the trail that still exist.
|
||||
/// </summary>
|
||||
public PageInfo[] AllPages {
|
||||
get {
|
||||
lock(this) {
|
||||
List<PageInfo> newPages = new List<PageInfo>(pages.Count);
|
||||
foreach(PageInfo p in pages) {
|
||||
if(Pages.FindPage(p.FullName) != null) newPages.Add(p);
|
||||
}
|
||||
|
||||
return newPages.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
126
Core/Cache.cs
Normal file
126
Core/Cache.cs
Normal file
|
@ -0,0 +1,126 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Manages data cache.
|
||||
/// </summary>
|
||||
public static class Cache {
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cache provider.
|
||||
/// </summary>
|
||||
public static ICacheProviderV30 Provider {
|
||||
get { return Collectors.CacheProviderCollector.GetProvider(Settings.DefaultCacheProvider); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of users online.
|
||||
/// </summary>
|
||||
public static int OnlineUsers {
|
||||
get { return Provider.OnlineUsers; }
|
||||
set { Provider.OnlineUsers = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the pages cache.
|
||||
/// </summary>
|
||||
public static void ClearPageCache() {
|
||||
Provider.ClearPageContentCache();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the pseudo cache.
|
||||
/// </summary>
|
||||
public static void ClearPseudoCache() {
|
||||
Provider.ClearPseudoCache();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a cached <see cref="T:PageContent" />.
|
||||
/// </summary>
|
||||
/// <param name="page">The page to get the content of.</param>
|
||||
/// <returns>The page content, or <c>null</c>.</returns>
|
||||
public static PageContent GetPageContent(PageInfo page) {
|
||||
if(page == null) return null;
|
||||
return Provider.GetPageContent(page);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a cached formatted page content.
|
||||
/// </summary>
|
||||
/// <param name="page">The page to get the formatted content of.</param>
|
||||
/// <returns>The formatted page content, or <c>null</c>.</returns>
|
||||
public static string GetFormattedPageContent(PageInfo page) {
|
||||
if(page == null) return null;
|
||||
return Provider.GetFormattedPageContent(page);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the page content in cache.
|
||||
/// </summary>
|
||||
/// <param name="page">The page to set the content of.</param>
|
||||
/// <param name="content">The content.</param>
|
||||
public static void SetPageContent(PageInfo page, PageContent content) {
|
||||
Provider.SetPageContent(page, content);
|
||||
if(Provider.PageCacheUsage > Settings.CacheSize) {
|
||||
Provider.CutCache(Settings.CacheCutSize);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the formatted page content in cache.
|
||||
/// </summary>
|
||||
/// <param name="page">The page to set the content of.</param>
|
||||
/// <param name="content">The content.</param>
|
||||
public static void SetFormattedPageContent(PageInfo page, string content) {
|
||||
Provider.SetFormattedPageContent(page, content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a page from the cache.
|
||||
/// </summary>
|
||||
/// <param name="page">The page to remove.</param>
|
||||
public static void RemovePage(PageInfo page) {
|
||||
Provider.RemovePage(page);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a pseudo cache item value.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the item to get the value of.</param>
|
||||
/// <returns>The value of the item, or <c>null</c>.</returns>
|
||||
public static string GetPseudoCacheValue(string name) {
|
||||
return Provider.GetPseudoCacheValue(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a pseudo cache item value.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the item to set the value of.</param>
|
||||
/// <param name="value">The value of the item.</param>
|
||||
public static void SetPseudoCacheValue(string name, string value) {
|
||||
Provider.SetPseudoCacheValue(name, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of pages currently in the page cache.
|
||||
/// </summary>
|
||||
public static int PageCacheUsage {
|
||||
get { return Provider.PageCacheUsage; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of formatted pages currently in the page cache.
|
||||
/// </summary>
|
||||
public static int FormattedPageCacheUsage {
|
||||
get { return Provider.FormatterPageCacheUsage; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
539
Core/CacheProvider.cs
Normal file
539
Core/CacheProvider.cs
Normal file
|
@ -0,0 +1,539 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Implements a local cache provider. All instance members are thread-safe.
|
||||
/// </summary>
|
||||
public class CacheProvider : ICacheProviderV30 {
|
||||
|
||||
private readonly ComponentInformation _info =
|
||||
new ComponentInformation("Local Cache Provider", "ScrewTurn Software", Settings.WikiVersion, "http://www.screwturn.eu", null);
|
||||
|
||||
private IHostV30 _host;
|
||||
|
||||
// Implements the pseudo cache
|
||||
private Dictionary<string, string> _pseudoCache;
|
||||
|
||||
// Contains the page contents
|
||||
private Dictionary<PageInfo, PageContent> _pageContentCache;
|
||||
|
||||
// Contains the partially-formatted page content
|
||||
private Dictionary<PageInfo, string> _formattedContentCache;
|
||||
|
||||
// Records, for each page, how many times a page has been requested,
|
||||
// limited to page contents (not formatted content)
|
||||
private Dictionary<PageInfo, int> _pageCacheUsage;
|
||||
|
||||
private int _onlineUsers = 0;
|
||||
|
||||
private List<EditingSession> _sessions;
|
||||
|
||||
// Key is lowercase, invariant culture
|
||||
private Dictionary<string, string> _redirections;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Storage Provider.
|
||||
/// </summary>
|
||||
/// <param name="host">The Host of the Component.</param>
|
||||
/// <param name="config">The Configuration data, if any.</param>
|
||||
/// <exception cref="ArgumentNullException">If <b>host</b> or <b>config</b> are <c>null</c>.</exception>
|
||||
/// <exception cref="InvalidConfigurationException">If <b>config</b> is not valid or is incorrect.</exception>
|
||||
public void Init(IHostV30 host, string config) {
|
||||
if(host == null) throw new ArgumentNullException("host");
|
||||
if(config == null) throw new ArgumentNullException("config");
|
||||
|
||||
_host = host;
|
||||
|
||||
int s = int.Parse(host.GetSettingValue(SettingName.CacheSize));
|
||||
|
||||
// Initialize pseudo cache
|
||||
_pseudoCache = new Dictionary<string, string>(10);
|
||||
|
||||
// Initialize page content cache
|
||||
_pageContentCache = new Dictionary<PageInfo, PageContent>(s);
|
||||
_pageCacheUsage = new Dictionary<PageInfo, int>(s);
|
||||
|
||||
// Initialize formatted page content cache
|
||||
_formattedContentCache = new Dictionary<PageInfo, string>(s);
|
||||
|
||||
_sessions = new List<EditingSession>(50);
|
||||
|
||||
_redirections = new Dictionary<string, string>(50);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method invoked on shutdown.
|
||||
/// </summary>
|
||||
/// <remarks>This method might not be invoked in some cases.</remarks>
|
||||
public void Shutdown() {
|
||||
ClearPseudoCache();
|
||||
ClearPageContentCache();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of users online.
|
||||
/// </summary>
|
||||
public int OnlineUsers {
|
||||
get {
|
||||
lock(this) {
|
||||
return _onlineUsers;
|
||||
}
|
||||
}
|
||||
set {
|
||||
lock(this) {
|
||||
_onlineUsers = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Information about the Provider.
|
||||
/// </summary>
|
||||
public ComponentInformation Information {
|
||||
get { return _info; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a brief summary of the configuration string format, in HTML. Returns <c>null</c> if no configuration is needed.
|
||||
/// </summary>
|
||||
public string ConfigHelpHtml {
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of a Pseudo-cache item, previously stored in the cache.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the item being requested.</param>
|
||||
/// <returns>The value of the item, or <c>null</c> if the item is not found.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <b>name</b> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <b>name</b> is empty.</exception>
|
||||
public string GetPseudoCacheValue(string name) {
|
||||
if(name == null) throw new ArgumentNullException("name");
|
||||
if(name.Length == 0) throw new ArgumentException("Name cannot be empty", "name");
|
||||
|
||||
string value = null;
|
||||
lock(_pseudoCache) {
|
||||
_pseudoCache.TryGetValue(name, out value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of a Pseudo-cache item.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the item being stored.</param>
|
||||
/// <param name="value">The value of the item. If the value is <c>null</c>, then the item should be removed from the cache.</param>
|
||||
/// <exception cref="ArgumentNullException">If <b>name</b> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <b>name</b> is empty.</exception>
|
||||
public void SetPseudoCacheValue(string name, string value) {
|
||||
if(name == null) throw new ArgumentNullException("name");
|
||||
if(name.Length == 0) throw new ArgumentException("Name cannot be empty", "name");
|
||||
|
||||
lock(_pseudoCache) {
|
||||
if(value == null) _pseudoCache.Remove(name);
|
||||
else _pseudoCache[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Content of a Page, previously stored in cache.
|
||||
/// </summary>
|
||||
/// <param name="pageInfo">The Page Info object related to the Content being requested.</param>
|
||||
/// <returns>The Page Content object, or <c>null</c> if the item is not found.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <b>pageInfo</b> is <c>null</c>.</exception>
|
||||
public PageContent GetPageContent(PageInfo pageInfo) {
|
||||
if(pageInfo == null) throw new ArgumentNullException("pageInfo");
|
||||
|
||||
PageContent value = null;
|
||||
lock(_pageContentCache) {
|
||||
if(_pageContentCache.TryGetValue(pageInfo, out value)) {
|
||||
_pageCacheUsage[pageInfo]++;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the Content of a Page.
|
||||
/// </summary>
|
||||
/// <param name="pageInfo">The Page Info object related to the Content being stored.</param>
|
||||
/// <param name="content">The Content of the Page.</param>
|
||||
/// <exception cref="ArgumentNullException">If <b>pageInfo</b> or <b>content</b> are <c>null</c>.</exception>
|
||||
public void SetPageContent(PageInfo pageInfo, PageContent content) {
|
||||
if(pageInfo == null) throw new ArgumentNullException("pageInfo");
|
||||
if(content == null) throw new ArgumentNullException("content");
|
||||
|
||||
lock(_pageContentCache) {
|
||||
_pageContentCache[pageInfo] = content;
|
||||
lock(_pageCacheUsage) {
|
||||
_pageCacheUsage[pageInfo] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the partially-formatted content (text) of a Page, previously stored in the cache.
|
||||
/// </summary>
|
||||
/// <param name="pageInfo">The Page Info object related to the content being requested.</param>
|
||||
/// <returns>The partially-formatted content, or <c>null</c> if the item is not found.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <b>pageInfo</b> is <c>null</c>.</exception>
|
||||
public string GetFormattedPageContent(PageInfo pageInfo) {
|
||||
if(pageInfo == null) throw new ArgumentNullException("pageInfo");
|
||||
|
||||
string value = null;
|
||||
lock(_formattedContentCache) {
|
||||
_formattedContentCache.TryGetValue(pageInfo, out value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the partially-preformatted content (text) of a Page.
|
||||
/// </summary>
|
||||
/// <param name="pageInfo">The Page Info object related to the content being stored.</param>
|
||||
/// <param name="content">The partially-preformatted content.</param>
|
||||
/// <exception cref="ArgumentNullException">If <b>pageInfo</b> or <b>content</b> are <c>null</c>.</exception>
|
||||
public void SetFormattedPageContent(PageInfo pageInfo, string content) {
|
||||
if(pageInfo == null) throw new ArgumentNullException("pageInfo");
|
||||
if(content == null) throw new ArgumentNullException("content");
|
||||
|
||||
lock(_formattedContentCache) {
|
||||
_formattedContentCache[pageInfo] = content;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a Page from the cache.
|
||||
/// </summary>
|
||||
/// <param name="pageInfo">The Page Info object related to the Page that has to be removed.</param>
|
||||
/// <exception cref="ArgumentNullException">If <b>pageInfo</b> is <c>null</c>.</exception>
|
||||
public void RemovePage(PageInfo pageInfo) {
|
||||
if(pageInfo == null) throw new ArgumentNullException("pageInfo");
|
||||
|
||||
// In this order
|
||||
lock(_formattedContentCache) {
|
||||
_formattedContentCache.Remove(pageInfo);
|
||||
}
|
||||
lock(_pageContentCache) {
|
||||
_pageContentCache.Remove(pageInfo);
|
||||
}
|
||||
lock(_pageCacheUsage) {
|
||||
_pageCacheUsage.Remove(pageInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the Page Content cache.
|
||||
/// </summary>
|
||||
public void ClearPageContentCache() {
|
||||
// In this order
|
||||
lock(_formattedContentCache) {
|
||||
_formattedContentCache.Clear();
|
||||
}
|
||||
lock(_pageContentCache) {
|
||||
_pageContentCache.Clear();
|
||||
}
|
||||
lock(_pageCacheUsage) {
|
||||
_pageCacheUsage.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the Pseudo-Cache.
|
||||
/// </summary>
|
||||
public void ClearPseudoCache() {
|
||||
lock(_pseudoCache) {
|
||||
_pseudoCache.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reduces the size of the Page Content cache, removing the least-recently used items.
|
||||
/// </summary>
|
||||
/// <param name="cutSize">The number of Pages to remove.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">If <b>cutSize</b> is less than or equal to zero.</exception>
|
||||
public void CutCache(int cutSize) {
|
||||
if(cutSize <= 0) throw new ArgumentOutOfRangeException("cutSize", "Cut Size should be greater than zero");
|
||||
|
||||
lock(_pageContentCache) {
|
||||
|
||||
// TODO: improve performance - now the operation is O(cache_size^2)
|
||||
for(int i = 0; i < cutSize; i++) {
|
||||
PageInfo key = null;
|
||||
int min = int.MaxValue;
|
||||
|
||||
// Find the page that has been requested the least times
|
||||
foreach(PageInfo p in _pageCacheUsage.Keys) {
|
||||
if(_pageCacheUsage[p] < min) {
|
||||
key = p;
|
||||
min = _pageCacheUsage[p];
|
||||
}
|
||||
}
|
||||
|
||||
// This is necessary to avoid infinite loops
|
||||
if(key == null) {
|
||||
break;
|
||||
}
|
||||
// Remove the page from cache
|
||||
RemovePage(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of Pages whose content is currently stored in the cache.
|
||||
/// </summary>
|
||||
public int PageCacheUsage {
|
||||
get {
|
||||
lock(_pageContentCache) {
|
||||
return _pageContentCache.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the numer of Pages whose formatted content is currently stored in the cache.
|
||||
/// </summary>
|
||||
public int FormatterPageCacheUsage {
|
||||
get {
|
||||
lock(_formattedContentCache) {
|
||||
return _formattedContentCache.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds or updates an editing session.
|
||||
/// </summary>
|
||||
/// <param name="page">The edited Page.</param>
|
||||
/// <param name="user">The User who is editing the Page.</param>
|
||||
/// <exception cref="ArgumentNullException">If <b>page</b> or <b>user</b> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <b>page</b> or <b>user</b> are empty.</exception>
|
||||
public void RenewEditingSession(string page, string user) {
|
||||
if(page == null) throw new ArgumentNullException("page");
|
||||
if(page.Length == 0) throw new ArgumentException("Page cannot be empty", "page");
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
if(user.Length == 0) throw new ArgumentException("User cannot be empty", "user");
|
||||
|
||||
lock(this) {
|
||||
bool found = false;
|
||||
for(int i = 0; i < _sessions.Count; i++) {
|
||||
if(_sessions[i].Page == page && _sessions[i].User.Equals(user)) {
|
||||
_sessions[i].Renew();
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found) _sessions.Add(new EditingSession(page, user));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels an editing session.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page.</param>
|
||||
/// <param name="user">The User.</param>
|
||||
/// <exception cref="ArgumentNullException">If <b>page</b> or <b>user</b> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <b>page</b> or <b>user</b> are empty.</exception>
|
||||
public void CancelEditingSession(string page, string user) {
|
||||
if(page == null) throw new ArgumentNullException("page");
|
||||
if(page.Length == 0) throw new ArgumentException("Page cannot be empty", "page");
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
if(user.Length == 0) throw new ArgumentException("User cannot be empty", "user");
|
||||
|
||||
lock(this) {
|
||||
for(int i = 0; i < _sessions.Count; i++) {
|
||||
if(_sessions[i].Page == page && _sessions[i].User.Equals(user)) {
|
||||
_sessions.Remove(_sessions[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds whether a Page is being edited by a different user.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page.</param>
|
||||
/// <param name="currentUser">The User who is requesting the status of the Page.</param>
|
||||
/// <returns>True if the Page is being edited by another User.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <b>page</b> or <b>currentUser</b> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <b>page</b> or <b>currentUser</b> are empty.</exception>
|
||||
public bool IsPageBeingEdited(string page, string currentUser) {
|
||||
if(page == null) throw new ArgumentNullException("page");
|
||||
if(page.Length == 0) throw new ArgumentException("Page cannot be empty", "page");
|
||||
if(currentUser == null) throw new ArgumentNullException("currentUser");
|
||||
if(currentUser.Length == 0) throw new ArgumentException("Current User cannot be empty", "currentUser");
|
||||
|
||||
lock(this) {
|
||||
int timeout = int.Parse(_host.GetSettingValue(SettingName.EditingSessionTimeout));
|
||||
DateTime limit = DateTime.Now.AddSeconds(-(timeout + 5)); // Allow 5 seconds for network delays
|
||||
|
||||
for(int i = 0; i < _sessions.Count; i++) {
|
||||
if(_sessions[i].Page == page && !_sessions[i].User.Equals(currentUser)) {
|
||||
if(_sessions[i].LastContact.CompareTo(limit) >= 0) {
|
||||
// Page is being edited
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// Lost contact
|
||||
_sessions.Remove(_sessions[i]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the username of the user who's editing a page.
|
||||
/// </summary>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <returns>The username.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <b>page</b> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <b>page</b> is empty.</exception>
|
||||
public string WhosEditing(string page) {
|
||||
if(page == null) throw new ArgumentNullException("page");
|
||||
if(page.Length == 0) throw new ArgumentException("Page cannot be empty", "page");
|
||||
|
||||
lock(this) {
|
||||
foreach(EditingSession s in _sessions) {
|
||||
if(s.Page == page) return s.User;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the redirection information for a page (overwrites the previous value, if any).
|
||||
/// </summary>
|
||||
/// <param name="source">The source page.</param>
|
||||
/// <param name="destination">The destination page.</param>
|
||||
/// <exception cref="ArgumentNullException">If <b>source</b> or <b>destination</b> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <b>source</b> or <b>destination</b> are empty.</exception>
|
||||
public void AddRedirection(string source, string destination) {
|
||||
if(source == null) throw new ArgumentNullException("source");
|
||||
if(source.Length == 0) throw new ArgumentException("Source cannot be empty", "source");
|
||||
if(destination == null) throw new ArgumentNullException("destination");
|
||||
if(destination.Length == 0) throw new ArgumentException("Destination cannot be empty", "destination");
|
||||
|
||||
lock(_redirections) {
|
||||
_redirections[source.ToLowerInvariant()] = destination;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the destination of a redirection.
|
||||
/// </summary>
|
||||
/// <param name="source">The source page.</param>
|
||||
/// <returns>The destination page, if any, <c>null</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <b>source</b> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <b>source</b> is empty.</exception>
|
||||
public string GetRedirectionDestination(string source) {
|
||||
if(source == null) throw new ArgumentNullException("source");
|
||||
if(source.Length == 0) throw new ArgumentException("Source cannot be empty", "source");
|
||||
|
||||
string dest = null;
|
||||
|
||||
lock(_redirections) {
|
||||
_redirections.TryGetValue(source.ToLowerInvariant(), out dest);
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a pge from both sources and destinations.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the page.</param>
|
||||
/// <exception cref="ArgumentNullException">If <b>name</b> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <b>name</b> is empty.</exception>
|
||||
public void RemovePageFromRedirections(string name) {
|
||||
if(name == null) throw new ArgumentNullException("name");
|
||||
if(name.Length == 0) throw new ArgumentException("Name cannot be empty", "name");
|
||||
|
||||
name = name.ToLowerInvariant();
|
||||
|
||||
lock(_redirections) {
|
||||
_redirections.Remove(name);
|
||||
|
||||
List<string> keysToRemove = new List<string>(10);
|
||||
|
||||
foreach(KeyValuePair<string, string> pair in _redirections) {
|
||||
if(pair.Value.ToLowerInvariant() == name) keysToRemove.Add(pair.Key);
|
||||
}
|
||||
|
||||
foreach(string key in keysToRemove) {
|
||||
_redirections.Remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all the redirections information.
|
||||
/// </summary>
|
||||
public void ClearRedirections() {
|
||||
lock(_redirections) {
|
||||
_redirections.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an Editing Session.
|
||||
/// </summary>
|
||||
public class EditingSession {
|
||||
|
||||
private string page;
|
||||
private string user;
|
||||
private DateTime lastContact;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <b>EditingSession</b> class.
|
||||
/// </summary>
|
||||
/// <param name="page">The edited Page.</param>
|
||||
/// <param name="user">The User who is editing the Page.</param>
|
||||
public EditingSession(string page, string user) {
|
||||
this.page = page;
|
||||
this.user = user;
|
||||
lastContact = DateTime.Now;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the edited Page.
|
||||
/// </summary>
|
||||
public string Page {
|
||||
get { return page; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the User.
|
||||
/// </summary>
|
||||
public string User {
|
||||
get { return user; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the Last Contact to now.
|
||||
/// </summary>
|
||||
public void Renew() {
|
||||
lastContact = DateTime.Now;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Last Contact Date/Time.
|
||||
/// </summary>
|
||||
public DateTime LastContact {
|
||||
get { return lastContact; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
268
Core/Collectors.cs
Normal file
268
Core/Collectors.cs
Normal file
|
@ -0,0 +1,268 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Contains instances of the Providers Collectors.
|
||||
/// </summary>
|
||||
public static class Collectors {
|
||||
|
||||
// The following static instances are "almost" thread-safe because they are set at startup and never changed
|
||||
// The ProviderCollector generic class is fully thread-safe
|
||||
|
||||
/// <summary>
|
||||
/// Contains the file names of the DLLs containing each provider (provider->file).
|
||||
/// </summary>
|
||||
public static Dictionary<string, string> FileNames;
|
||||
|
||||
/// <summary>
|
||||
/// The settings storage provider.
|
||||
/// </summary>
|
||||
public static ISettingsStorageProviderV30 SettingsProvider;
|
||||
|
||||
/// <summary>
|
||||
/// The Users Provider Collector instance.
|
||||
/// </summary>
|
||||
public static ProviderCollector<IUsersStorageProviderV30> UsersProviderCollector;
|
||||
|
||||
/// <summary>
|
||||
/// The Pages Provider Collector instance.
|
||||
/// </summary>
|
||||
public static ProviderCollector<IPagesStorageProviderV30> PagesProviderCollector;
|
||||
|
||||
/// <summary>
|
||||
/// The Files Provider Collector instance.
|
||||
/// </summary>
|
||||
public static ProviderCollector<IFilesStorageProviderV30> FilesProviderCollector;
|
||||
|
||||
/// <summary>
|
||||
/// The Formatter Provider Collector instance.
|
||||
/// </summary>
|
||||
public static ProviderCollector<IFormatterProviderV30> FormatterProviderCollector;
|
||||
|
||||
/// <summary>
|
||||
/// The Cache Provider Collector instance.
|
||||
/// </summary>
|
||||
public static ProviderCollector<ICacheProviderV30> CacheProviderCollector;
|
||||
|
||||
/// <summary>
|
||||
/// The Disabled Users Provider Collector instance.
|
||||
/// </summary>
|
||||
public static ProviderCollector<IUsersStorageProviderV30> DisabledUsersProviderCollector;
|
||||
|
||||
/// <summary>
|
||||
/// The Disabled Files Provider Collector instance.
|
||||
/// </summary>
|
||||
public static ProviderCollector<IFilesStorageProviderV30> DisabledFilesProviderCollector;
|
||||
|
||||
/// <summary>
|
||||
/// The Disabled Pages Provider Collector instance.
|
||||
/// </summary>
|
||||
public static ProviderCollector<IPagesStorageProviderV30> DisabledPagesProviderCollector;
|
||||
|
||||
/// <summary>
|
||||
/// The Disabled Formatter Provider Collector instance.
|
||||
/// </summary>
|
||||
public static ProviderCollector<IFormatterProviderV30> DisabledFormatterProviderCollector;
|
||||
|
||||
/// <summary>
|
||||
/// The Disabled Cache Provider Collector instance.
|
||||
/// </summary>
|
||||
public static ProviderCollector<ICacheProviderV30> DisabledCacheProviderCollector;
|
||||
|
||||
/// <summary>
|
||||
/// Finds a provider.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The provider type name.</param>
|
||||
/// <param name="enabled">A value indicating whether the provider is enabled.</param>
|
||||
/// <param name="canDisable">A value indicating whether the provider can be disabled.</param>
|
||||
/// <returns>The provider, or <c>null</c>.</returns>
|
||||
public static IProviderV30 FindProvider(string typeName, out bool enabled, out bool canDisable) {
|
||||
enabled = true;
|
||||
canDisable = true;
|
||||
IProviderV30 prov = null;
|
||||
|
||||
prov = PagesProviderCollector.GetProvider(typeName);
|
||||
canDisable = typeName != Settings.DefaultPagesProvider;
|
||||
if(prov == null) {
|
||||
prov = DisabledPagesProviderCollector.GetProvider(typeName);
|
||||
if(prov != null) enabled = false;
|
||||
}
|
||||
if(prov != null) return prov;
|
||||
|
||||
prov = UsersProviderCollector.GetProvider(typeName);
|
||||
canDisable = typeName != Settings.DefaultUsersProvider;
|
||||
if(prov == null) {
|
||||
prov = DisabledUsersProviderCollector.GetProvider(typeName);
|
||||
if(prov != null) enabled = false;
|
||||
}
|
||||
if(prov != null) return prov;
|
||||
|
||||
prov = FilesProviderCollector.GetProvider(typeName);
|
||||
canDisable = typeName != Settings.DefaultFilesProvider;
|
||||
if(prov == null) {
|
||||
prov = DisabledFilesProviderCollector.GetProvider(typeName);
|
||||
if(prov != null) enabled = false;
|
||||
}
|
||||
if(prov != null) return prov;
|
||||
|
||||
prov = CacheProviderCollector.GetProvider(typeName);
|
||||
canDisable = typeName != Settings.DefaultCacheProvider;
|
||||
if(prov == null) {
|
||||
prov = DisabledCacheProviderCollector.GetProvider(typeName);
|
||||
if(prov != null) enabled = false;
|
||||
}
|
||||
if(prov != null) return prov;
|
||||
|
||||
prov = FormatterProviderCollector.GetProvider(typeName);
|
||||
if(prov == null) {
|
||||
prov = DisabledFormatterProviderCollector.GetProvider(typeName);
|
||||
if(prov != null) enabled = false;
|
||||
}
|
||||
if(prov != null) return prov;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to unload a provider.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The provider.</param>
|
||||
public static void TryUnload(string typeName) {
|
||||
bool enabled, canDisable;
|
||||
IProviderV30 prov = FindProvider(typeName, out enabled, out canDisable);
|
||||
|
||||
PagesProviderCollector.RemoveProvider(prov as IPagesStorageProviderV30);
|
||||
DisabledPagesProviderCollector.RemoveProvider(prov as IPagesStorageProviderV30);
|
||||
|
||||
UsersProviderCollector.RemoveProvider(prov as IUsersStorageProviderV30);
|
||||
DisabledUsersProviderCollector.RemoveProvider(prov as IUsersStorageProviderV30);
|
||||
|
||||
FilesProviderCollector.RemoveProvider(prov as IFilesStorageProviderV30);
|
||||
DisabledFilesProviderCollector.RemoveProvider(prov as IFilesStorageProviderV30);
|
||||
|
||||
CacheProviderCollector.RemoveProvider(prov as ICacheProviderV30);
|
||||
DisabledCacheProviderCollector.RemoveProvider(prov as ICacheProviderV30);
|
||||
|
||||
FormatterProviderCollector.RemoveProvider(prov as IFormatterProviderV30);
|
||||
DisabledFormatterProviderCollector.RemoveProvider(prov as IFormatterProviderV30);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to disable a provider.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The provider.</param>
|
||||
public static void TryDisable(string typeName) {
|
||||
IProviderV30 prov = null;
|
||||
|
||||
prov = PagesProviderCollector.GetProvider(typeName);
|
||||
if(prov != null) {
|
||||
DisabledPagesProviderCollector.AddProvider((IPagesStorageProviderV30)prov);
|
||||
PagesProviderCollector.RemoveProvider((IPagesStorageProviderV30)prov);
|
||||
return;
|
||||
}
|
||||
|
||||
prov = UsersProviderCollector.GetProvider(typeName);
|
||||
if(prov != null) {
|
||||
DisabledUsersProviderCollector.AddProvider((IUsersStorageProviderV30)prov);
|
||||
UsersProviderCollector.RemoveProvider((IUsersStorageProviderV30)prov);
|
||||
return;
|
||||
}
|
||||
|
||||
prov = FilesProviderCollector.GetProvider(typeName);
|
||||
if(prov != null) {
|
||||
DisabledFilesProviderCollector.AddProvider((IFilesStorageProviderV30)prov);
|
||||
FilesProviderCollector.RemoveProvider((IFilesStorageProviderV30)prov);
|
||||
return;
|
||||
}
|
||||
|
||||
prov = CacheProviderCollector.GetProvider(typeName);
|
||||
if(prov != null) {
|
||||
DisabledCacheProviderCollector.AddProvider((ICacheProviderV30)prov);
|
||||
CacheProviderCollector.RemoveProvider((ICacheProviderV30)prov);
|
||||
return;
|
||||
}
|
||||
|
||||
prov = FormatterProviderCollector.GetProvider(typeName);
|
||||
if(prov != null) {
|
||||
DisabledFormatterProviderCollector.AddProvider((IFormatterProviderV30)prov);
|
||||
FormatterProviderCollector.RemoveProvider((IFormatterProviderV30)prov);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to enable a provider.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The provider.</param>
|
||||
public static void TryEnable(string typeName) {
|
||||
IProviderV30 prov = null;
|
||||
|
||||
prov = DisabledPagesProviderCollector.GetProvider(typeName);
|
||||
if(prov != null) {
|
||||
PagesProviderCollector.AddProvider((IPagesStorageProviderV30)prov);
|
||||
DisabledPagesProviderCollector.RemoveProvider((IPagesStorageProviderV30)prov);
|
||||
return;
|
||||
}
|
||||
|
||||
prov = DisabledUsersProviderCollector.GetProvider(typeName);
|
||||
if(prov != null) {
|
||||
UsersProviderCollector.AddProvider((IUsersStorageProviderV30)prov);
|
||||
DisabledUsersProviderCollector.RemoveProvider((IUsersStorageProviderV30)prov);
|
||||
return;
|
||||
}
|
||||
|
||||
prov = DisabledFilesProviderCollector.GetProvider(typeName);
|
||||
if(prov != null) {
|
||||
FilesProviderCollector.AddProvider((IFilesStorageProviderV30)prov);
|
||||
DisabledFilesProviderCollector.RemoveProvider((IFilesStorageProviderV30)prov);
|
||||
return;
|
||||
}
|
||||
|
||||
prov = DisabledCacheProviderCollector.GetProvider(typeName);
|
||||
if(prov != null) {
|
||||
CacheProviderCollector.AddProvider((ICacheProviderV30)prov);
|
||||
DisabledCacheProviderCollector.RemoveProvider((ICacheProviderV30)prov);
|
||||
return;
|
||||
}
|
||||
|
||||
prov = DisabledFormatterProviderCollector.GetProvider(typeName);
|
||||
if(prov != null) {
|
||||
FormatterProviderCollector.AddProvider((IFormatterProviderV30)prov);
|
||||
DisabledFormatterProviderCollector.RemoveProvider((IFormatterProviderV30)prov);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of all known providers, both enabled and disabled.
|
||||
/// </summary>
|
||||
/// <returns>The names of the known providers.</returns>
|
||||
public static string[] GetAllProviders() {
|
||||
List<string> result = new List<string>(20);
|
||||
|
||||
foreach(IProviderV30 prov in PagesProviderCollector.AllProviders) result.Add(prov.GetType().FullName);
|
||||
foreach(IProviderV30 prov in DisabledPagesProviderCollector.AllProviders) result.Add(prov.GetType().FullName);
|
||||
|
||||
foreach(IProviderV30 prov in UsersProviderCollector.AllProviders) result.Add(prov.GetType().FullName);
|
||||
foreach(IProviderV30 prov in DisabledUsersProviderCollector.AllProviders) result.Add(prov.GetType().FullName);
|
||||
|
||||
foreach(IProviderV30 prov in FilesProviderCollector.AllProviders) result.Add(prov.GetType().FullName);
|
||||
foreach(IProviderV30 prov in DisabledFilesProviderCollector.AllProviders) result.Add(prov.GetType().FullName);
|
||||
|
||||
foreach(IProviderV30 prov in CacheProviderCollector.AllProviders) result.Add(prov.GetType().FullName);
|
||||
foreach(IProviderV30 prov in DisabledCacheProviderCollector.AllProviders) result.Add(prov.GetType().FullName);
|
||||
|
||||
foreach(IProviderV30 prov in FormatterProviderCollector.AllProviders) result.Add(prov.GetType().FullName);
|
||||
foreach(IProviderV30 prov in DisabledFormatterProviderCollector.AllProviders) result.Add(prov.GetType().FullName);
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
57
Core/Collisions.cs
Normal file
57
Core/Collisions.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Manages Page Editing collisions.
|
||||
/// </summary>
|
||||
public static class Collisions {
|
||||
|
||||
/// <summary>
|
||||
/// The refresh interval used for renewing the sessions.
|
||||
/// </summary>
|
||||
public const int EditingSessionTimeout = 20;
|
||||
|
||||
/// <summary>
|
||||
/// Adds or updates an editing session.
|
||||
/// </summary>
|
||||
/// <param name="page">The edited Page.</param>
|
||||
/// <param name="user">The User who is editing the Page.</param>
|
||||
public static void RenewEditingSession(PageInfo page, string user) {
|
||||
Cache.Provider.RenewEditingSession(page.FullName, user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels an editing session.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page.</param>
|
||||
/// <param name="user">The User.</param>
|
||||
public static void CancelEditingSession(PageInfo page, string user) {
|
||||
Cache.Provider.CancelEditingSession(page.FullName, user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds whether a Page is being edited by a different user.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page.</param>
|
||||
/// <param name="currentUser">The User who is requesting the status of the Page.</param>
|
||||
/// <returns>True if the Page is being edited by another User.</returns>
|
||||
public static bool IsPageBeingEdited(PageInfo page, string currentUser) {
|
||||
return Cache.Provider.IsPageBeingEdited(page.FullName, currentUser);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the username of the user who's editing a page.
|
||||
/// </summary>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <returns>The username.</returns>
|
||||
public static string WhosEditing(PageInfo page) {
|
||||
return Cache.Provider.WhosEditing(page.FullName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
105
Core/Content.cs
Normal file
105
Core/Content.cs
Normal file
|
@ -0,0 +1,105 @@
|
|||
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Web;
|
||||
using System.Collections.Generic;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Contains the Contents.
|
||||
/// </summary>
|
||||
public static class Content {
|
||||
|
||||
/// <summary>
|
||||
/// Gets a pseudo cache item value.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the item to retrieve the value of.</param>
|
||||
/// <returns>The value of the item, or <c>null</c>.</returns>
|
||||
public static string GetPseudoCacheValue(string name) {
|
||||
return Cache.GetPseudoCacheValue(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a pseudo cache item value, only if the content cache is enabled.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the item to store the value of.</param>
|
||||
/// <param name="value">The value of the item.</param>
|
||||
public static void SetPseudoCacheValue(string name, string value) {
|
||||
if(!Settings.DisableCache) {
|
||||
Cache.SetPseudoCacheValue(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the pseudo cache.
|
||||
/// </summary>
|
||||
public static void ClearPseudoCache() {
|
||||
Cache.ClearPseudoCache();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the Content of a Page.
|
||||
/// </summary>
|
||||
/// <param name="pageInfo">The Page.</param>
|
||||
/// <param name="cached">Specifies whether the page has to be cached or not.</param>
|
||||
/// <returns>The Page Content.</returns>
|
||||
public static PageContent GetPageContent(PageInfo pageInfo, bool cached) {
|
||||
PageContent result = Cache.GetPageContent(pageInfo);
|
||||
if(result == null) {
|
||||
result = pageInfo.Provider.GetContent(pageInfo);
|
||||
if(result!= null && !result.IsEmpty()) {
|
||||
if(cached && !pageInfo.NonCached && !Settings.DisableCache) {
|
||||
Cache.SetPageContent(pageInfo, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// result should NEVER be null
|
||||
if(result == null) {
|
||||
Log.LogEntry("PageContent could not be retrieved for page " + pageInfo.FullName + " - returning empty", EntryType.Error, Log.SystemUsername);
|
||||
result = PageContent.GetEmpty(pageInfo);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the formatted Page Content, properly handling content caching and the Formatting Pipeline.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page to get the formatted Content of.</param>
|
||||
/// <param name="cached">Specifies whether the formatted content has to be cached or not.</param>
|
||||
/// <returns>The formatted content.</returns>
|
||||
public static string GetFormattedPageContent(PageInfo page, bool cached) {
|
||||
string content = Cache.GetFormattedPageContent(page);
|
||||
if(content == null) {
|
||||
PageContent pg = GetPageContent(page, cached);
|
||||
string[] linkedPages;
|
||||
content = FormattingPipeline.FormatWithPhase1And2(pg.Content, false, FormattingContext.PageContent, page, out linkedPages);
|
||||
pg.LinkedPages = linkedPages;
|
||||
if(!pg.IsEmpty() && cached && !page.NonCached && !Settings.DisableCache) {
|
||||
Cache.SetFormattedPageContent(page, content);
|
||||
}
|
||||
}
|
||||
return FormattingPipeline.FormatWithPhase3(content, FormattingContext.PageContent, page);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalidates the cached Content of a Page.
|
||||
/// </summary>
|
||||
/// <param name="pageInfo">The Page to invalidate the cached content of.</param>
|
||||
public static void InvalidatePage(PageInfo pageInfo) {
|
||||
Cache.RemovePage(pageInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalidates all the cache Contents.
|
||||
/// </summary>
|
||||
public static void InvalidateAllPages() {
|
||||
Cache.ClearPageCache();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
141
Core/Core.csproj
Normal file
141
Core/Core.csproj
Normal file
|
@ -0,0 +1,141 @@
|
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.30729</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{C353A35C-86D0-4154-9500-4F88CAAB29C3}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>ScrewTurn.Wiki</RootNamespace>
|
||||
<AssemblyName>ScrewTurn.Wiki.Core</AssemblyName>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>
|
||||
</AssemblyOriginatorKeyFile>
|
||||
<FileUpgradeFlags>
|
||||
</FileUpgradeFlags>
|
||||
<OldToolsVersion>2.0</OldToolsVersion>
|
||||
<UpgradeBackupLocation>
|
||||
</UpgradeBackupLocation>
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<DocumentationFile>bin\Debug\ScrewTurn.Wiki.Core.XML</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<DocumentationFile>bin\Release\ScrewTurn.Wiki.Core.xml</DocumentationFile>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\AssemblyVersion.cs">
|
||||
<Link>AssemblyVersion.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="AclStorer.cs" />
|
||||
<Compile Include="AuthChecker.cs" />
|
||||
<Compile Include="AuthStatus.cs" />
|
||||
<Compile Include="AuthTools.cs" />
|
||||
<Compile Include="AuthWriter.cs" />
|
||||
<Compile Include="BreadcrumbsManager.cs" />
|
||||
<Compile Include="Cache.cs" />
|
||||
<Compile Include="CacheProvider.cs" />
|
||||
<Compile Include="Collectors.cs" />
|
||||
<Compile Include="Collisions.cs" />
|
||||
<Compile Include="Content.cs" />
|
||||
<Compile Include="DataMigrator.cs" />
|
||||
<Compile Include="Defaults.cs" />
|
||||
<Compile Include="DiffTools.cs" />
|
||||
<Compile Include="EmailTools.cs" />
|
||||
<Compile Include="FileDocument.cs" />
|
||||
<Compile Include="FilesAndAttachments.cs" />
|
||||
<Compile Include="FilesStorageProvider.cs" />
|
||||
<Compile Include="Formatter.cs" />
|
||||
<Compile Include="FormattingPipeline.cs" />
|
||||
<Compile Include="Hash.cs" />
|
||||
<Compile Include="Host.cs" />
|
||||
<Compile Include="IndexStorer.cs" />
|
||||
<Compile Include="ITranslator.cs" />
|
||||
<Compile Include="LocalPageInfo.cs" />
|
||||
<Compile Include="LocalProvidersTools.cs" />
|
||||
<Compile Include="LocalUserInfo.cs" />
|
||||
<Compile Include="Log.cs" />
|
||||
<Compile Include="MimeTypes.cs" />
|
||||
<Compile Include="NavigationPaths.cs" />
|
||||
<Compile Include="PageAttachmentDocument.cs" />
|
||||
<Compile Include="Pages.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="PagesStorageProvider.cs" />
|
||||
<Compile Include="AuthReader.cs" />
|
||||
<Compile Include="Preferences.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ProviderCollector.cs" />
|
||||
<Compile Include="ProviderLoader.cs" />
|
||||
<Compile Include="ProviderUpdater.cs" />
|
||||
<Compile Include="RecentChanges.cs" />
|
||||
<Compile Include="Redirections.cs" />
|
||||
<Compile Include="ReverseFormatter.cs" />
|
||||
<Compile Include="SearchTools.cs" />
|
||||
<Compile Include="SessionFacade.cs" />
|
||||
<Compile Include="Settings.cs" />
|
||||
<Compile Include="SettingsStorageProvider.cs" />
|
||||
<Compile Include="Snippets.cs" />
|
||||
<Compile Include="StartupTools.cs" />
|
||||
<Compile Include="SubjectInfo.cs" />
|
||||
<Compile Include="Templates.cs" />
|
||||
<Compile Include="Tools.cs" />
|
||||
<Compile Include="Translator.cs" />
|
||||
<Compile Include="TranslatorFlex.cs" />
|
||||
<Compile Include="UrlTools.cs" />
|
||||
<Compile Include="Users.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="UsersStorageProvider.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AclEngine\AclEngine.csproj">
|
||||
<Project>{44B0F4C1-8CDC-4272-B2A2-C0AF689CEB81}</Project>
|
||||
<Name>AclEngine</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\PluginFramework\PluginFramework.csproj">
|
||||
<Project>{531A83D6-76F9-4014-91C5-295818E2D948}</Project>
|
||||
<Name>PluginFramework</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SearchEngine\SearchEngine.csproj">
|
||||
<Project>{2DF980A6-4742-49B1-A090-DE79314644D0}</Project>
|
||||
<Name>SearchEngine</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
384
Core/DataMigrator.cs
Normal file
384
Core/DataMigrator.cs
Normal file
|
@ -0,0 +1,384 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using ScrewTurn.Wiki.AclEngine;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Provides methods for migrating data from a Provider to another.
|
||||
/// </summary>
|
||||
public static class DataMigrator {
|
||||
|
||||
/// <summary>
|
||||
/// Migrates <b>all</b> the data from a Pages Provider to another one.
|
||||
/// </summary>
|
||||
/// <param name="source">The source Provider.</param>
|
||||
/// <param name="destination">The destination Provider.</param>
|
||||
public static void MigratePagesStorageProviderData(IPagesStorageProviderV30 source, IPagesStorageProviderV30 destination) {
|
||||
// Move Snippets
|
||||
Snippet[] snippets = source.GetSnippets();
|
||||
for(int i = 0; i < snippets.Length; i++) {
|
||||
destination.AddSnippet(snippets[i].Name, snippets[i].Content);
|
||||
source.RemoveSnippet(snippets[i].Name);
|
||||
}
|
||||
|
||||
// Move Content Templates
|
||||
ContentTemplate[] templates = source.GetContentTemplates();
|
||||
for(int i = 0; i < templates.Length; i++) {
|
||||
destination.AddContentTemplate(templates[i].Name, templates[i].Content);
|
||||
source.RemoveContentTemplate(templates[i].Name);
|
||||
}
|
||||
|
||||
// Create namespaces
|
||||
NamespaceInfo[] namespaces = source.GetNamespaces();
|
||||
NamespaceInfo[] createdNamespaces = new NamespaceInfo[namespaces.Length];
|
||||
for(int i = 0; i < namespaces.Length; i++) {
|
||||
createdNamespaces[i] = destination.AddNamespace(namespaces[i].Name);
|
||||
}
|
||||
|
||||
List<NamespaceInfo> sourceNamespaces = new List<NamespaceInfo>();
|
||||
sourceNamespaces.Add(null);
|
||||
sourceNamespaces.AddRange(namespaces);
|
||||
|
||||
int currentNamespaceIndex = 0;
|
||||
foreach(NamespaceInfo currentNamespace in sourceNamespaces) {
|
||||
|
||||
// Load all nav paths now to avoid problems with missing pages from source provider
|
||||
// after the pages have been moved already
|
||||
NavigationPath[] sourceNavPaths = source.GetNavigationPaths(currentNamespace);
|
||||
|
||||
// Copy categories (removed from source later)
|
||||
CategoryInfo[] sourceCats = source.GetCategories(currentNamespace);
|
||||
for(int i = 0; i < sourceCats.Length; i++) {
|
||||
destination.AddCategory(NameTools.GetNamespace(sourceCats[i].FullName), NameTools.GetLocalName(sourceCats[i].FullName));
|
||||
}
|
||||
|
||||
// Move Pages
|
||||
PageInfo[] pages = source.GetPages(currentNamespace);
|
||||
for(int i = 0; i < pages.Length; i++) {
|
||||
|
||||
// Create Page
|
||||
PageInfo newPage = destination.AddPage(NameTools.GetNamespace(pages[i].FullName),
|
||||
NameTools.GetLocalName(pages[i].FullName), pages[i].CreationDateTime);
|
||||
if(newPage == null) {
|
||||
Log.LogEntry("Unable to move Page " + pages[i].FullName + " - Skipping", EntryType.Error, Log.SystemUsername);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get content and store it, without backup
|
||||
PageContent c = source.GetContent(pages[i]);
|
||||
destination.ModifyPage(newPage, c.Title, c.User, c.LastModified, c.Comment, c.Content, c.Keywords, c.Description, SaveMode.Normal);
|
||||
|
||||
// Move all the backups
|
||||
int[] revs = source.GetBackups(pages[i]);
|
||||
for(int k = 0; k < revs.Length; k++) {
|
||||
c = source.GetBackupContent(pages[i], revs[k]);
|
||||
destination.SetBackupContent(c, revs[k]);
|
||||
}
|
||||
|
||||
// Move all messages
|
||||
Message[] messages = source.GetMessages(pages[i]);
|
||||
destination.BulkStoreMessages(newPage, messages);
|
||||
|
||||
// Bind current page (find all proper categories, and use them to bind the page)
|
||||
List<string> pageCats = new List<string>();
|
||||
for(int k = 0; k < sourceCats.Length; k++) {
|
||||
for(int z = 0; z < sourceCats[k].Pages.Length; z++) {
|
||||
if(sourceCats[k].Pages[z].Equals(newPage.FullName)) {
|
||||
pageCats.Add(sourceCats[k].FullName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
destination.RebindPage(newPage, pageCats.ToArray());
|
||||
|
||||
// Copy draft
|
||||
PageContent draft = source.GetDraft(pages[i]);
|
||||
if(draft != null) {
|
||||
destination.ModifyPage(newPage, draft.Title, draft.User, draft.LastModified,
|
||||
draft.Comment, draft.Content, draft.Keywords, draft.Description, SaveMode.Draft);
|
||||
}
|
||||
|
||||
// Remove Page from source
|
||||
source.RemovePage(pages[i]); // Also deletes the Messages
|
||||
}
|
||||
|
||||
// Remove Categories from source
|
||||
for(int i = 0; i < sourceCats.Length; i++) {
|
||||
source.RemoveCategory(sourceCats[i]);
|
||||
}
|
||||
|
||||
// Move navigation paths
|
||||
List<PageInfo> newPages = new List<PageInfo>(destination.GetPages(currentNamespace == null ? null : createdNamespaces[currentNamespaceIndex]));
|
||||
for(int i = 0; i < sourceNavPaths.Length; i++) {
|
||||
PageInfo[] tmp = new PageInfo[sourceNavPaths[i].Pages.Length];
|
||||
for(int k = 0; k < tmp.Length; k++) {
|
||||
tmp[k] = newPages.Find(delegate(PageInfo p) { return p.FullName == sourceNavPaths[i].Pages[k]; });
|
||||
}
|
||||
destination.AddNavigationPath(NameTools.GetNamespace(sourceNavPaths[i].FullName),
|
||||
NameTools.GetLocalName(sourceNavPaths[i].FullName), tmp);
|
||||
source.RemoveNavigationPath(sourceNavPaths[i]);
|
||||
}
|
||||
|
||||
if(currentNamespace != null) {
|
||||
// Set default page
|
||||
PageInfo defaultPage = currentNamespace.DefaultPage == null ? null :
|
||||
newPages.Find(delegate(PageInfo p) { return p.FullName == currentNamespace.DefaultPage.FullName; });
|
||||
destination.SetNamespaceDefaultPage(createdNamespaces[currentNamespaceIndex], defaultPage);
|
||||
|
||||
// Remove namespace from source
|
||||
source.RemoveNamespace(currentNamespace);
|
||||
|
||||
currentNamespaceIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Migrates all the User Accounts from a Provider to another.
|
||||
/// </summary>
|
||||
/// <param name="source">The source Provider.</param>
|
||||
/// <param name="destination">The destination Provider.</param>
|
||||
/// <param name="sendEmailNotification">A value indicating whether or not to send a notification email message to the moved users.</param>
|
||||
public static void MigrateUsersStorageProviderData(IUsersStorageProviderV30 source, IUsersStorageProviderV30 destination, bool sendEmailNotification) {
|
||||
// User groups
|
||||
UserGroup[] groups = source.GetUserGroups();
|
||||
foreach(UserGroup group in groups) {
|
||||
destination.AddUserGroup(group.Name, group.Description);
|
||||
}
|
||||
|
||||
// Users
|
||||
UserInfo[] users = source.GetUsers();
|
||||
MovedUser[] movedUsers = new MovedUser[users.Length];
|
||||
|
||||
for(int i = 0; i < users.Length; i++) {
|
||||
// Generate new Password, create MovedUser object, add new User, delete old User
|
||||
string password = Tools.GenerateRandomPassword();
|
||||
movedUsers[i] = new MovedUser(users[i].Username, users[i].Email, users[i].DateTime);
|
||||
UserInfo newUser = destination.AddUser(users[i].Username, users[i].DisplayName, password, users[i].Email, users[i].Active, users[i].DateTime);
|
||||
|
||||
// Membership
|
||||
destination.SetUserMembership(newUser, users[i].Groups);
|
||||
|
||||
// User data
|
||||
IDictionary<string, string> uData = source.RetrieveAllUserData(users[i]);
|
||||
foreach(KeyValuePair<string, string> pair in uData) {
|
||||
destination.StoreUserData(newUser, pair.Key, pair.Value);
|
||||
}
|
||||
|
||||
source.RemoveUser(users[i]);
|
||||
}
|
||||
|
||||
// Remove old groups
|
||||
foreach(UserGroup group in groups) {
|
||||
source.RemoveUserGroup(group);
|
||||
}
|
||||
|
||||
if(sendEmailNotification) {
|
||||
// Send Emails
|
||||
for(int i = 0; i < movedUsers.Length; i++) {
|
||||
Users.SendPasswordResetMessage(movedUsers[i].Username, movedUsers[i].Email, movedUsers[i].DateTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Migrates all the stored files and page attachments from a Provider to another.
|
||||
/// </summary>
|
||||
/// <param name="source">The source Provider.</param>
|
||||
/// <param name="destination">The destination Provider.</param>
|
||||
/// <param name="settingsProvider">The settings storage provider that handles permissions.</param>
|
||||
public static void MigrateFilesStorageProviderData(IFilesStorageProviderV30 source, IFilesStorageProviderV30 destination, ISettingsStorageProviderV30 settingsProvider) {
|
||||
// Directories
|
||||
MigrateDirectories(source, destination, "/", settingsProvider);
|
||||
|
||||
// Attachments
|
||||
foreach(string page in source.GetPagesWithAttachments()) {
|
||||
PageInfo pageInfo = new PageInfo(page, null, DateTime.Now);
|
||||
|
||||
string[] attachments = source.ListPageAttachments(pageInfo);
|
||||
|
||||
foreach(string attachment in attachments) {
|
||||
// Copy file content
|
||||
using(MemoryStream ms = new MemoryStream(1048576)) {
|
||||
source.RetrievePageAttachment(pageInfo, attachment, ms, false);
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
destination.StorePageAttachment(pageInfo, attachment, ms, false);
|
||||
}
|
||||
|
||||
// Copy download count
|
||||
FileDetails fileDetails = source.GetPageAttachmentDetails(pageInfo, attachment);
|
||||
destination.SetPageAttachmentRetrievalCount(pageInfo, attachment, fileDetails.RetrievalCount);
|
||||
|
||||
// Delete attachment
|
||||
source.DeletePageAttachment(pageInfo, attachment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void MigrateDirectories(IFilesStorageProviderV30 source, IFilesStorageProviderV30 destination, string current, ISettingsStorageProviderV30 settingsProvider) {
|
||||
// Copy files
|
||||
string[] files = source.ListFiles(current);
|
||||
foreach(string file in files) {
|
||||
// Copy file content
|
||||
using(MemoryStream ms = new MemoryStream(1048576)) {
|
||||
source.RetrieveFile(file, ms, false);
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
destination.StoreFile(file, ms, false);
|
||||
}
|
||||
|
||||
// Copy download count
|
||||
FileDetails fileDetails = source.GetFileDetails(file);
|
||||
destination.SetFileRetrievalCount(file, fileDetails.RetrievalCount);
|
||||
|
||||
// Delete source file, if root
|
||||
if(current == "/") {
|
||||
source.DeleteFile(file);
|
||||
}
|
||||
}
|
||||
|
||||
settingsProvider.AclManager.RenameResource(
|
||||
Actions.ForDirectories.ResourceMasterPrefix + AuthTools.GetDirectoryName(source, current),
|
||||
Actions.ForDirectories.ResourceMasterPrefix + AuthTools.GetDirectoryName(destination, current));
|
||||
|
||||
// Copy directories
|
||||
string[] directories = source.ListDirectories(current);
|
||||
foreach(string dir in directories) {
|
||||
destination.CreateDirectory(current, dir.Substring(dir.TrimEnd('/').LastIndexOf("/") + 1).Trim('/'));
|
||||
MigrateDirectories(source, destination, dir, settingsProvider);
|
||||
|
||||
// Delete directory, if root
|
||||
if(current == "/") {
|
||||
source.DeleteDirectory(dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the settings from a Provider to another.
|
||||
/// </summary>
|
||||
/// <param name="source">The source Provider.</param>
|
||||
/// <param name="destination">The destination Provider.</param>
|
||||
/// <param name="knownNamespaces">The currently known page namespaces.</param>
|
||||
/// <param name="knownPlugins">The currently known plugins.</param>
|
||||
public static void CopySettingsStorageProviderData(ISettingsStorageProviderV30 source, ISettingsStorageProviderV30 destination,
|
||||
string[] knownNamespaces, string[] knownPlugins) {
|
||||
|
||||
// Settings
|
||||
destination.BeginBulkUpdate();
|
||||
foreach(KeyValuePair<string, string> pair in source.GetAllSettings()) {
|
||||
destination.SetSetting(pair.Key, pair.Value);
|
||||
}
|
||||
destination.EndBulkUpdate();
|
||||
|
||||
// Meta-data (global)
|
||||
destination.SetMetaDataItem(MetaDataItem.AccountActivationMessage, null,
|
||||
source.GetMetaDataItem(MetaDataItem.AccountActivationMessage, null));
|
||||
destination.SetMetaDataItem(MetaDataItem.PasswordResetProcedureMessage, null,
|
||||
source.GetMetaDataItem(MetaDataItem.PasswordResetProcedureMessage, null));
|
||||
destination.SetMetaDataItem(MetaDataItem.LoginNotice, null,
|
||||
source.GetMetaDataItem(MetaDataItem.LoginNotice, null));
|
||||
destination.SetMetaDataItem(MetaDataItem.PageChangeMessage, null,
|
||||
source.GetMetaDataItem(MetaDataItem.PageChangeMessage, null));
|
||||
destination.SetMetaDataItem(MetaDataItem.DiscussionChangeMessage, null,
|
||||
source.GetMetaDataItem(MetaDataItem.DiscussionChangeMessage, null));
|
||||
|
||||
// Meta-data (ns-specific)
|
||||
List<string> namespacesToProcess = new List<string>();
|
||||
namespacesToProcess.Add(null);
|
||||
namespacesToProcess.AddRange(knownNamespaces);
|
||||
foreach(string nspace in namespacesToProcess) {
|
||||
destination.SetMetaDataItem(MetaDataItem.EditNotice, nspace,
|
||||
source.GetMetaDataItem(MetaDataItem.EditNotice, nspace));
|
||||
destination.SetMetaDataItem(MetaDataItem.Footer, nspace,
|
||||
source.GetMetaDataItem(MetaDataItem.Footer, nspace));
|
||||
destination.SetMetaDataItem(MetaDataItem.Header, nspace,
|
||||
source.GetMetaDataItem(MetaDataItem.Header, nspace));
|
||||
destination.SetMetaDataItem(MetaDataItem.HtmlHead, nspace,
|
||||
source.GetMetaDataItem(MetaDataItem.HtmlHead, nspace));
|
||||
destination.SetMetaDataItem(MetaDataItem.PageFooter, nspace,
|
||||
source.GetMetaDataItem(MetaDataItem.PageFooter, nspace));
|
||||
destination.SetMetaDataItem(MetaDataItem.PageHeader, nspace,
|
||||
source.GetMetaDataItem(MetaDataItem.PageHeader, nspace));
|
||||
destination.SetMetaDataItem(MetaDataItem.Sidebar, nspace,
|
||||
source.GetMetaDataItem(MetaDataItem.Sidebar, nspace));
|
||||
}
|
||||
|
||||
// Plugin assemblies
|
||||
string[] assemblies = source.ListPluginAssemblies();
|
||||
foreach(string asm in assemblies) {
|
||||
destination.StorePluginAssembly(asm,
|
||||
source.RetrievePluginAssembly(asm));
|
||||
}
|
||||
|
||||
// Plugin status and config
|
||||
foreach(string plug in knownPlugins) {
|
||||
destination.SetPluginStatus(plug,
|
||||
source.GetPluginStatus(plug));
|
||||
|
||||
destination.SetPluginConfiguration(plug,
|
||||
source.GetPluginConfiguration(plug));
|
||||
}
|
||||
|
||||
// Outgoing links
|
||||
IDictionary<string, string[]> allLinks = source.GetAllOutgoingLinks();
|
||||
foreach(KeyValuePair<string, string[]> link in allLinks) {
|
||||
destination.StoreOutgoingLinks(link.Key, link.Value);
|
||||
}
|
||||
|
||||
// ACLs
|
||||
AclEntry[] allEntries = source.AclManager.RetrieveAllEntries();
|
||||
foreach(AclEntry entry in allEntries) {
|
||||
destination.AclManager.StoreEntry(entry.Resource, entry.Action, entry.Subject, entry.Value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains username, email and registration date/time of a moved User account.
|
||||
/// </summary>
|
||||
public class MovedUser {
|
||||
|
||||
private string username, email;
|
||||
private DateTime dateTime;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <b>MovedUser</b> class.
|
||||
/// </summary>
|
||||
/// <param name="username">The esername.</param>
|
||||
/// <param name="email">The email address.</param>
|
||||
/// <param name="dateTime">The registration date/time.</param>
|
||||
public MovedUser(string username, string email, DateTime dateTime) {
|
||||
this.username = username;
|
||||
this.email = email;
|
||||
this.dateTime = dateTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Username.
|
||||
/// </summary>
|
||||
public string Username {
|
||||
get { return username; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Email.
|
||||
/// </summary>
|
||||
public string Email {
|
||||
get { return email; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the registration date/time.
|
||||
/// </summary>
|
||||
public DateTime DateTime {
|
||||
get { return dateTime; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
178
Core/Defaults.cs
Normal file
178
Core/Defaults.cs
Normal file
|
@ -0,0 +1,178 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Contains default values.
|
||||
/// </summary>
|
||||
public static class Defaults {
|
||||
|
||||
/// <summary>
|
||||
/// The default content of the main page.
|
||||
/// </summary>
|
||||
public const string MainPageContent = @"Welcome to '''{WIKITITLE}'''!{BR}
|
||||
This is the main page of your new ScrewTurn Wiki, created for you by the system.
|
||||
|
||||
You should [Edit.aspx?Page=MainPage|edit this page] and customize the contents. You can also [Edit.aspx|create a new page] from scratch.
|
||||
|
||||
If you need help, try to visit [http://www.screwturn.eu|our website] or [http://www.screwturn.eu/forum|our forum].
|
||||
|
||||
'''Warning''': remember to setup the ''admin'' account by editing the {{Web.config}} file placed in the root directory of the Wiki. It is ''extremely dangerous'' to keep the default password.";
|
||||
|
||||
/// <summary>
|
||||
/// The default content of the main page of a sub-namespace.
|
||||
/// </summary>
|
||||
public const string MainPageContentForSubNamespace = @"Welcome to the '''{NAMESPACE}''' namespace of '''{WIKITITLE}'''!{BR}
|
||||
This is the main page of the namespace, created for you by the system.
|
||||
|
||||
You should [Edit.aspx?Page={NAMESPACE}.MainPage|edit this page] and customize the contents. You can also [Edit.aspx|create a new page] from scratch.
|
||||
|
||||
If you need help, try to visit [http://www.screwturn.eu|our website] or [http://www.screwturn.eu/forum|our forum].";
|
||||
|
||||
/// <summary>
|
||||
/// The default content of the account activation message.
|
||||
/// </summary>
|
||||
public const string AccountActivationMessageContent = @"Hi ##USERNAME## and welcome to ##WIKITITLE##!
|
||||
You must activate your new ##WIKITITLE## Account within 24 hours, following the link below.
|
||||
|
||||
##ACTIVATIONLINK##
|
||||
|
||||
If you have any trouble, please contact us at our Email address, ##EMAILADDRESS## .
|
||||
|
||||
Thank you.
|
||||
|
||||
Best regards,
|
||||
The ##WIKITITLE## Team.";
|
||||
|
||||
/// <summary>
|
||||
/// The default content of the edit notice.
|
||||
/// </summary>
|
||||
public const string EditNoticeContent = @"Please '''do not''' include contents covered by copyright without the explicit permission of the Author. Always preview the result before saving.{BR}
|
||||
If you are having trouble, please visit the [http://www.screwturn.eu/Help.aspx|Help section] at [http://www.screwturn.eu|ScrewTurn Software].";
|
||||
|
||||
/// <summary>
|
||||
/// The default content of the footer.
|
||||
/// </summary>
|
||||
public const string FooterContent = @"<p class=""small"">[http://www.screwturn.eu|ScrewTurn Wiki] version {WIKIVERSION}. Some of the icons created by [http://www.famfamfam.com|FamFamFam].</p>";
|
||||
|
||||
/// <summary>
|
||||
/// The default content of the header.
|
||||
/// </summary>
|
||||
public const string HeaderContent = @"<div style=""float: right;"">Welcome {USERNAME}, you are in: {NAMESPACEDROPDOWN} • {LOGINLOGOUT}</div><h1>{WIKITITLE}</h1>";
|
||||
|
||||
/// <summary>
|
||||
/// The default content of the password reset message.
|
||||
/// </summary>
|
||||
public const string PasswordResetProcedureMessageContent = @"Hi ##USERNAME##!
|
||||
Your can change your password following the instructions you will see at this link:
|
||||
##LINK##
|
||||
|
||||
If you have any trouble, please contact us at our Email address, ##EMAILADDRESS## .
|
||||
|
||||
Thank you.
|
||||
|
||||
Best regards,
|
||||
The ##WIKITITLE## Team.";
|
||||
|
||||
/// <summary>
|
||||
/// The default content of the sidebar.
|
||||
/// </summary>
|
||||
public const string SidebarContent = @"<div style=""float: right;"">
|
||||
<a href=""RSS.aspx"" title=""Update notifications for {WIKITITLE} (RSS 2.0)""><img src=""{THEMEPATH}Images/RSS.png"" alt=""RSS"" /></a>
|
||||
<a href=""RSS.aspx?Discuss=1"" title=""Update notifications for {WIKITITLE} Discussions (RSS 2.0)""><img src=""{THEMEPATH}Images/RSS-Discussion.png"" alt=""RSS"" /></a></div>
|
||||
====Navigation====
|
||||
* '''[MainPage|Main Page]'''
|
||||
|
||||
* [RandPage.aspx|Random Page]
|
||||
* [Edit.aspx|Create a new Page]
|
||||
* [AllPages.aspx|All Pages]
|
||||
* [Category.aspx|Categories]
|
||||
* [NavPath.aspx|Navigation Paths]
|
||||
|
||||
* [AdminHome.aspx|Administration]
|
||||
* [Upload.aspx|File Management]
|
||||
|
||||
* [Register.aspx|Create Account]
|
||||
|
||||
<small>'''Search the wiki'''</small>{BR}
|
||||
{SEARCHBOX}
|
||||
|
||||
[image|PoweredBy|Images/PoweredBy.png|http://www.screwturn.eu]";
|
||||
|
||||
/// <summary>
|
||||
/// The default content of the sidebar of a sub-namespace.
|
||||
/// </summary>
|
||||
public const string SidebarContentForSubNamespace = @"<div style=""float: right;"">
|
||||
<a href=""{NAMESPACE}.RSS.aspx"" title=""Update notifications for {WIKITITLE} ({NAMESPACE}) (RSS 2.0)""><img src=""{THEMEPATH}Images/RSS.png"" alt=""RSS"" /></a>
|
||||
<a href=""{NAMESPACE}.RSS.aspx?Discuss=1"" title=""Update notifications for {WIKITITLE} Discussions ({NAMESPACE}) (RSS 2.0)""><img src=""{THEMEPATH}Images/RSS-Discussion.png"" alt=""RSS"" /></a></div>
|
||||
====Navigation ({NAMESPACE})====
|
||||
* '''[MainPage|Main Page]'''
|
||||
* [++MainPage|Main Page (root)]
|
||||
|
||||
* [RandPage.aspx|Random Page]
|
||||
* [Edit.aspx|Create a new Page]
|
||||
* [AllPages.aspx|All Pages]
|
||||
* [Category.aspx|Categories]
|
||||
* [NavPath.aspx|Navigation Paths]
|
||||
|
||||
* [AdminHome.aspx|Administration]
|
||||
* [Upload.aspx|File Management]
|
||||
|
||||
* [Register.aspx|Create Account]
|
||||
|
||||
<small>'''Search the wiki'''</small>{BR}
|
||||
{SEARCHBOX}
|
||||
|
||||
[image|PoweredBy|Images/PoweredBy.png|http://www.screwturn.eu]";
|
||||
|
||||
/// <summary>
|
||||
/// The default content of the page change email message.
|
||||
/// </summary>
|
||||
public const string PageChangeMessage = @"The page ""##PAGE##"" was modified by ##USER## on ##DATETIME##.
|
||||
Author's comment: ##COMMENT##.
|
||||
|
||||
The page can be found at the following address:
|
||||
##LINK##
|
||||
|
||||
Thank you.
|
||||
|
||||
Best regards,
|
||||
The ##WIKITITLE## Team.";
|
||||
|
||||
/// <summary>
|
||||
/// The default content of the discussion change email message.
|
||||
/// </summary>
|
||||
public const string DiscussionChangeMessage = @"A new message was posted on the page ""##PAGE##"" by ##USER## on ##DATETIME##.
|
||||
|
||||
The subject of the message is ""##SUBJECT##"" and it can be found at the following address:
|
||||
##LINK##
|
||||
|
||||
Thank you.
|
||||
|
||||
Best regards,
|
||||
The ##WIKITITLE## Team.";
|
||||
|
||||
/// <summary>
|
||||
/// The default content of the approve draft email message.
|
||||
/// </summary>
|
||||
public const string ApproveDraftMessage = @"A draft for the page ""##PAGE##"" was created or modified by ##USER## on ##DATETIME## and is currently held for **approval**.
|
||||
Author's comment: ##COMMENT##.
|
||||
|
||||
The draft can be found and edited at the following address:
|
||||
##LINK##
|
||||
You can directly approve or reject the draft at the following address:
|
||||
##LINK2##
|
||||
|
||||
Please note that the draft will not be displayed until it is approved.
|
||||
|
||||
Thank you.
|
||||
|
||||
Best regards,
|
||||
The ##WIKITITLE## Team.";
|
||||
|
||||
}
|
||||
|
||||
}
|
477
Core/DiffTools.cs
Normal file
477
Core/DiffTools.cs
Normal file
|
@ -0,0 +1,477 @@
|
|||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Provides methods for diffing text and items.
|
||||
/// </summary>
|
||||
public static class DiffTools {
|
||||
|
||||
/// <summary>
|
||||
/// Computes the difference between two revisions.
|
||||
/// </summary>
|
||||
/// <param name="rev1">The first revision.</param>
|
||||
/// <param name="rev2">The second revision.</param>
|
||||
/// <returns>The XHTML-formatted result.</returns>
|
||||
public static string DiffRevisions(string rev1, string rev2) {
|
||||
string[] aLines = rev1.Split('\n');
|
||||
string[] bLines = rev2.Split('\n');
|
||||
Difference.Item[] f = Difference.DiffText(rev1, rev2, true, false, false);
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
result.Append(@"<table cellpadding=""0"" cellspacing=""0"" style=""color: #000000; background-color: #FFFFFF;"">");
|
||||
|
||||
int n = 0;
|
||||
for(int fdx = 0; fdx < f.Length; fdx++) {
|
||||
Difference.Item aItem = f[fdx];
|
||||
// Write unchanged lines
|
||||
while((n < aItem.StartB) && (n < bLines.Length)) {
|
||||
result.Append(WriteLine(n, "", bLines[n]));
|
||||
n++;
|
||||
}
|
||||
|
||||
// Write deleted lines
|
||||
for(int m = 0; m < aItem.deletedA; m++) {
|
||||
result.Append(WriteLine(-1, "d", aLines[aItem.StartA + m]));
|
||||
}
|
||||
|
||||
// Write inserted lines
|
||||
while(n < aItem.StartB + aItem.insertedB) {
|
||||
result.Append(WriteLine(n, "i", bLines[n]));
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
// Write the rest of unchanged lines
|
||||
while(n < bLines.Length) {
|
||||
result.Append(WriteLine(n, "", bLines[n]));
|
||||
n++;
|
||||
}
|
||||
|
||||
result.Append("</table>");
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
private static string WriteLine(int n, string typ, string line) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append("<tr>");
|
||||
|
||||
sb.Append(@"<td valign=""top"" width=""30"" style=""font-family: Courier New, monospace;"">");
|
||||
if(n >= 0) sb.Append(((int)(n + 1)).ToString());
|
||||
else sb.Append(" ");
|
||||
sb.Append("</td>");
|
||||
|
||||
sb.Append(@"<td valign=""top"" style=""font-family: Courier New, monospace;"">");
|
||||
sb.Append(@"<div style=""");
|
||||
switch(typ) {
|
||||
case "i":
|
||||
sb.Append("background-color: #88CC33;");
|
||||
break;
|
||||
case "d":
|
||||
sb.Append("background-color: #FFDF66;");
|
||||
break;
|
||||
}
|
||||
sb.Append(@""">" + HttpContext.Current.Server.HtmlEncode(line) + "</div>");
|
||||
sb.Append("</td>");
|
||||
|
||||
sb.Append("</tr>");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// O(ND) Difference Algorithm for C#
|
||||
/// Created by Matthias Hertel, see http://www.mathertel.de
|
||||
/// This work is licensed under a Creative Commons Attribution 2.0 Germany License.
|
||||
/// see http://creativecommons.org/licenses/by/2.0/de/
|
||||
/// </summary>
|
||||
public class Difference {
|
||||
|
||||
/// <summary>details of one difference.</summary>
|
||||
public struct Item {
|
||||
/// <summary>Start Line number in Data A.</summary>
|
||||
public int StartA;
|
||||
/// <summary>Start Line number in Data B.</summary>
|
||||
public int StartB;
|
||||
|
||||
/// <summary>Number of changes in Data A.</summary>
|
||||
public int deletedA;
|
||||
/// <summary>Number of changes in Data A.</summary>
|
||||
public int insertedB;
|
||||
} // Item
|
||||
|
||||
/// <summary>
|
||||
/// Shortest Middle Snake Return Data
|
||||
/// </summary>
|
||||
private struct SMSRD {
|
||||
internal int x, y;
|
||||
// internal int u, v; // 2002.09.20: no need for 2 points
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the difference in 2 texts, comparing by textlines.
|
||||
/// </summary>
|
||||
/// <param name="TextA">A-version of the text (usualy the old one)</param>
|
||||
/// <param name="TextB">B-version of the text (usualy the new one)</param>
|
||||
/// <returns>Returns a array of Items that describe the differences.</returns>
|
||||
public Item[] DiffText(string TextA, string TextB) {
|
||||
return (DiffText(TextA, TextB, false, false, false));
|
||||
} // DiffText
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Find the difference in 2 text documents, comparing by textlines.
|
||||
/// The algorithm itself is comparing 2 arrays of numbers so when comparing 2 text documents
|
||||
/// each line is converted into a (hash) number. This hash-value is computed by storing all
|
||||
/// textlines into a common hashtable so i can find dublicates in there, and generating a
|
||||
/// new number each time a new textline is inserted.
|
||||
/// </summary>
|
||||
/// <param name="TextA">A-version of the text (usualy the old one)</param>
|
||||
/// <param name="TextB">B-version of the text (usualy the new one)</param>
|
||||
/// <param name="trimSpace">When set to true, all leading and trailing whitespace characters are stripped out before the comparation is done.</param>
|
||||
/// <param name="ignoreSpace">When set to true, all whitespace characters are converted to a single space character before the comparation is done.</param>
|
||||
/// <param name="ignoreCase">When set to true, all characters are converted to their lowercase equivivalence before the comparation is done.</param>
|
||||
/// <returns>Returns a array of Items that describe the differences.</returns>
|
||||
public static Item[] DiffText(string TextA, string TextB, bool trimSpace, bool ignoreSpace, bool ignoreCase) {
|
||||
// prepare the input-text and convert to comparable numbers.
|
||||
Hashtable h = new Hashtable(TextA.Length + TextB.Length);
|
||||
|
||||
// The A-Version of the data (original data) to be compared.
|
||||
DiffData DataA = new DiffData(DiffCodes(TextA, h, trimSpace, ignoreSpace, ignoreCase));
|
||||
|
||||
// The B-Version of the data (modified data) to be compared.
|
||||
DiffData DataB = new DiffData(DiffCodes(TextB, h, trimSpace, ignoreSpace, ignoreCase));
|
||||
|
||||
h = null; // free up hashtable memory (maybe)
|
||||
|
||||
LCS(DataA, 0, DataA.Length, DataB, 0, DataB.Length);
|
||||
return CreateDiffs(DataA, DataB);
|
||||
} // DiffText
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Find the difference in 2 arrays of integers.
|
||||
/// </summary>
|
||||
/// <param name="ArrayA">A-version of the numbers (usualy the old one)</param>
|
||||
/// <param name="ArrayB">B-version of the numbers (usualy the new one)</param>
|
||||
/// <returns>Returns a array of Items that describe the differences.</returns>
|
||||
public static Item[] DiffInt(int[] ArrayA, int[] ArrayB) {
|
||||
// The A-Version of the data (original data) to be compared.
|
||||
DiffData DataA = new DiffData(ArrayA);
|
||||
|
||||
// The B-Version of the data (modified data) to be compared.
|
||||
DiffData DataB = new DiffData(ArrayB);
|
||||
|
||||
LCS(DataA, 0, DataA.Length, DataB, 0, DataB.Length);
|
||||
return CreateDiffs(DataA, DataB);
|
||||
} // Diff
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Converts all textlines of the text into unique numbers for every unique textline
|
||||
/// so further work can work only with simple numbers.
|
||||
/// </summary>
|
||||
/// <param name="aText">The input text</param>
|
||||
/// <param name="h">This extern initialized hashtable is used for storing all ever used textlines.</param>
|
||||
/// <param name="trimSpace">Ignore leading and trailing space characters</param>
|
||||
/// <param name="ignoreSpace">Ignore spaces.</param>
|
||||
/// <param name="ignoreCase">Ignore case.</param>
|
||||
/// <returns>An array of integers.</returns>
|
||||
private static int[] DiffCodes(string aText, Hashtable h, bool trimSpace, bool ignoreSpace, bool ignoreCase) {
|
||||
// get all codes of the text
|
||||
string[] Lines;
|
||||
int[] Codes;
|
||||
int lastUsedCode = h.Count;
|
||||
object aCode;
|
||||
string s;
|
||||
|
||||
// strip off all cr, only use lf as textline separator.
|
||||
aText = aText.Replace("\r", "");
|
||||
Lines = aText.Split('\n');
|
||||
|
||||
Codes = new int[Lines.Length];
|
||||
|
||||
for(int i = 0; i < Lines.Length; ++i) {
|
||||
s = Lines[i];
|
||||
if(trimSpace)
|
||||
s = s.Trim();
|
||||
|
||||
if(ignoreSpace) {
|
||||
s = Regex.Replace(s, "\\s+", " "); // TODO: optimization: faster blank removal.
|
||||
}
|
||||
|
||||
if(ignoreCase)
|
||||
s = s.ToLowerInvariant();
|
||||
|
||||
aCode = h[s];
|
||||
if(aCode == null) {
|
||||
lastUsedCode++;
|
||||
h[s] = lastUsedCode;
|
||||
Codes[i] = lastUsedCode;
|
||||
}
|
||||
else {
|
||||
Codes[i] = (int)aCode;
|
||||
} // if
|
||||
} // for
|
||||
return (Codes);
|
||||
} // DiffCodes
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This is the algorithm to find the Shortest Middle Snake (SMS).
|
||||
/// </summary>
|
||||
/// <param name="DataA">sequence A</param>
|
||||
/// <param name="LowerA">lower bound of the actual range in DataA</param>
|
||||
/// <param name="UpperA">upper bound of the actual range in DataA (exclusive)</param>
|
||||
/// <param name="DataB">sequence B</param>
|
||||
/// <param name="LowerB">lower bound of the actual range in DataB</param>
|
||||
/// <param name="UpperB">upper bound of the actual range in DataB (exclusive)</param>
|
||||
/// <returns>a MiddleSnakeData record containing x,y and u,v</returns>
|
||||
private static SMSRD SMS(DiffData DataA, int LowerA, int UpperA, DiffData DataB, int LowerB, int UpperB) {
|
||||
SMSRD ret;
|
||||
int MAX = DataA.Length + DataB.Length + 1;
|
||||
|
||||
int DownK = LowerA - LowerB; // the k-line to start the forward search
|
||||
int UpK = UpperA - UpperB; // the k-line to start the reverse search
|
||||
|
||||
int Delta = (UpperA - LowerA) - (UpperB - LowerB);
|
||||
bool oddDelta = (Delta & 1) != 0;
|
||||
|
||||
// vector for the (0,0) to (x,y) search
|
||||
int[] DownVector = new int[2 * MAX + 2];
|
||||
|
||||
// vector for the (u,v) to (N,M) search
|
||||
int[] UpVector = new int[2 * MAX + 2];
|
||||
|
||||
// The vectors in the publication accepts negative indexes. the vectors implemented here are 0-based
|
||||
// and are access using a specific offset: UpOffset UpVector and DownOffset for DownVektor
|
||||
int DownOffset = MAX - DownK;
|
||||
int UpOffset = MAX - UpK;
|
||||
|
||||
int MaxD = ((UpperA - LowerA + UpperB - LowerB) / 2) + 1;
|
||||
|
||||
// Debug.Write(2, "SMS", String.Format("Search the box: A[{0}-{1}] to B[{2}-{3}]", LowerA, UpperA, LowerB, UpperB));
|
||||
|
||||
// init vectors
|
||||
DownVector[DownOffset + DownK + 1] = LowerA;
|
||||
UpVector[UpOffset + UpK - 1] = UpperA;
|
||||
|
||||
for(int D = 0; D <= MaxD; D++) {
|
||||
|
||||
// Extend the forward path.
|
||||
for(int k = DownK - D; k <= DownK + D; k += 2) {
|
||||
// Debug.Write(0, "SMS", "extend forward path " + k.ToString());
|
||||
|
||||
// find the only or better starting point
|
||||
int x, y;
|
||||
if(k == DownK - D) {
|
||||
x = DownVector[DownOffset + k + 1]; // down
|
||||
}
|
||||
else {
|
||||
x = DownVector[DownOffset + k - 1] + 1; // a step to the right
|
||||
if((k < DownK + D) && (DownVector[DownOffset + k + 1] >= x))
|
||||
x = DownVector[DownOffset + k + 1]; // down
|
||||
}
|
||||
y = x - k;
|
||||
|
||||
// find the end of the furthest reaching forward D-path in diagonal k.
|
||||
while((x < UpperA) && (y < UpperB) && (DataA.data[x] == DataB.data[y])) {
|
||||
x++; y++;
|
||||
}
|
||||
DownVector[DownOffset + k] = x;
|
||||
|
||||
// overlap ?
|
||||
if(oddDelta && (UpK - D < k) && (k < UpK + D)) {
|
||||
if(UpVector[UpOffset + k] <= DownVector[DownOffset + k]) {
|
||||
ret.x = DownVector[DownOffset + k];
|
||||
ret.y = DownVector[DownOffset + k] - k;
|
||||
// ret.u = UpVector[UpOffset + k]; // 2002.09.20: no need for 2 points
|
||||
// ret.v = UpVector[UpOffset + k] - k;
|
||||
return (ret);
|
||||
} // if
|
||||
} // if
|
||||
|
||||
} // for k
|
||||
|
||||
// Extend the reverse path.
|
||||
for(int k = UpK - D; k <= UpK + D; k += 2) {
|
||||
// Debug.Write(0, "SMS", "extend reverse path " + k.ToString());
|
||||
|
||||
// find the only or better starting point
|
||||
int x, y;
|
||||
if(k == UpK + D) {
|
||||
x = UpVector[UpOffset + k - 1]; // up
|
||||
}
|
||||
else {
|
||||
x = UpVector[UpOffset + k + 1] - 1; // left
|
||||
if((k > UpK - D) && (UpVector[UpOffset + k - 1] < x))
|
||||
x = UpVector[UpOffset + k - 1]; // up
|
||||
} // if
|
||||
y = x - k;
|
||||
|
||||
while((x > LowerA) && (y > LowerB) && (DataA.data[x - 1] == DataB.data[y - 1])) {
|
||||
x--; y--; // diagonal
|
||||
}
|
||||
UpVector[UpOffset + k] = x;
|
||||
|
||||
// overlap ?
|
||||
if(!oddDelta && (DownK - D <= k) && (k <= DownK + D)) {
|
||||
if(UpVector[UpOffset + k] <= DownVector[DownOffset + k]) {
|
||||
ret.x = DownVector[DownOffset + k];
|
||||
ret.y = DownVector[DownOffset + k] - k;
|
||||
// ret.u = UpVector[UpOffset + k]; // 2002.09.20: no need for 2 points
|
||||
// ret.v = UpVector[UpOffset + k] - k;
|
||||
return (ret);
|
||||
} // if
|
||||
} // if
|
||||
|
||||
} // for k
|
||||
|
||||
} // for D
|
||||
|
||||
throw new ApplicationException("the algorithm should never come here.");
|
||||
} // SMS
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This is the divide-and-conquer implementation of the longes common-subsequence (LCS)
|
||||
/// algorithm.
|
||||
/// The published algorithm passes recursively parts of the A and B sequences.
|
||||
/// To avoid copying these arrays the lower and upper bounds are passed while the sequences stay constant.
|
||||
/// </summary>
|
||||
/// <param name="DataA">sequence A</param>
|
||||
/// <param name="LowerA">lower bound of the actual range in DataA</param>
|
||||
/// <param name="UpperA">upper bound of the actual range in DataA (exclusive)</param>
|
||||
/// <param name="DataB">sequence B</param>
|
||||
/// <param name="LowerB">lower bound of the actual range in DataB</param>
|
||||
/// <param name="UpperB">upper bound of the actual range in DataB (exclusive)</param>
|
||||
private static void LCS(DiffData DataA, int LowerA, int UpperA, DiffData DataB, int LowerB, int UpperB) {
|
||||
// Debug.Write(2, "LCS", String.Format("Analyse the box: A[{0}-{1}] to B[{2}-{3}]", LowerA, UpperA, LowerB, UpperB));
|
||||
|
||||
// Fast walkthrough equal lines at the start
|
||||
while(LowerA < UpperA && LowerB < UpperB && DataA.data[LowerA] == DataB.data[LowerB]) {
|
||||
LowerA++; LowerB++;
|
||||
}
|
||||
|
||||
// Fast walkthrough equal lines at the end
|
||||
while(LowerA < UpperA && LowerB < UpperB && DataA.data[UpperA - 1] == DataB.data[UpperB - 1]) {
|
||||
--UpperA; --UpperB;
|
||||
}
|
||||
|
||||
if(LowerA == UpperA) {
|
||||
// mark as inserted lines.
|
||||
while(LowerB < UpperB)
|
||||
DataB.modified[LowerB++] = true;
|
||||
|
||||
}
|
||||
else if(LowerB == UpperB) {
|
||||
// mark as deleted lines.
|
||||
while(LowerA < UpperA)
|
||||
DataA.modified[LowerA++] = true;
|
||||
|
||||
}
|
||||
else {
|
||||
// Find the middle snakea and length of an optimal path for A and B
|
||||
SMSRD smsrd = SMS(DataA, LowerA, UpperA, DataB, LowerB, UpperB);
|
||||
// Debug.Write(2, "MiddleSnakeData", String.Format("{0},{1}", smsrd.x, smsrd.y));
|
||||
|
||||
// The path is from LowerX to (x,y) and (x,y) ot UpperX
|
||||
LCS(DataA, LowerA, smsrd.x, DataB, LowerB, smsrd.y);
|
||||
LCS(DataA, smsrd.x, UpperA, DataB, smsrd.y, UpperB); // 2002.09.20: no need for 2 points
|
||||
}
|
||||
} // LCS()
|
||||
|
||||
|
||||
/// <summary>Scan the tables of which lines are inserted and deleted,
|
||||
/// producing an edit script in forward order.
|
||||
/// </summary>
|
||||
/// dynamic array
|
||||
private static Item[] CreateDiffs(DiffData DataA, DiffData DataB) {
|
||||
ArrayList a = new ArrayList();
|
||||
Item aItem;
|
||||
Item[] result;
|
||||
|
||||
int StartA, StartB;
|
||||
int LineA, LineB;
|
||||
|
||||
LineA = 0;
|
||||
LineB = 0;
|
||||
while(LineA < DataA.Length || LineB < DataB.Length) {
|
||||
if((LineA < DataA.Length) && (!DataA.modified[LineA])
|
||||
&& (LineB < DataB.Length) && (!DataB.modified[LineB])) {
|
||||
// equal lines
|
||||
LineA++;
|
||||
LineB++;
|
||||
|
||||
}
|
||||
else {
|
||||
// maybe deleted and/or inserted lines
|
||||
StartA = LineA;
|
||||
StartB = LineB;
|
||||
|
||||
while(LineA < DataA.Length && (LineB >= DataB.Length || DataA.modified[LineA]))
|
||||
// while (LineA < DataA.Length && DataA.modified[LineA])
|
||||
LineA++;
|
||||
|
||||
while(LineB < DataB.Length && (LineA >= DataA.Length || DataB.modified[LineB]))
|
||||
// while (LineB < DataB.Length && DataB.modified[LineB])
|
||||
LineB++;
|
||||
|
||||
if((StartA < LineA) || (StartB < LineB)) {
|
||||
// store a new difference-item
|
||||
aItem = new Item();
|
||||
aItem.StartA = StartA;
|
||||
aItem.StartB = StartB;
|
||||
aItem.deletedA = LineA - StartA;
|
||||
aItem.insertedB = LineB - StartB;
|
||||
a.Add(aItem);
|
||||
} // if
|
||||
} // if
|
||||
} // while
|
||||
|
||||
result = new Item[a.Count];
|
||||
a.CopyTo(result);
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
} // class Diff
|
||||
|
||||
/// <summary>Data on one input file being compared.
|
||||
/// </summary>
|
||||
internal class DiffData {
|
||||
|
||||
/// <summary>Number of elements (lines).</summary>
|
||||
internal int Length;
|
||||
|
||||
/// <summary>Buffer of numbers that will be compared.</summary>
|
||||
internal int[] data;
|
||||
|
||||
/// <summary>
|
||||
/// Array of booleans that flag for modified data.
|
||||
/// This is the result of the diff.
|
||||
/// This means deletedA in the first Data or inserted in the second Data.
|
||||
/// </summary>
|
||||
internal bool[] modified;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the Diff-Data buffer.
|
||||
/// </summary>
|
||||
/// <param name="initData">Reference to the buffer</param>
|
||||
internal DiffData(int[] initData) {
|
||||
data = initData;
|
||||
Length = initData.Length;
|
||||
modified = new bool[Length + 2];
|
||||
} // DiffData
|
||||
|
||||
} // class DiffData
|
||||
|
||||
}
|
130
Core/EmailTools.cs
Normal file
130
Core/EmailTools.cs
Normal file
|
@ -0,0 +1,130 @@
|
|||
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Net;
|
||||
using System.Net.Mail;
|
||||
using System.Web;
|
||||
using System.Web.Security;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
using System.Web.UI.WebControls.WebParts;
|
||||
using System.Web.UI.HtmlControls;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Implements email-related tools.
|
||||
/// </summary>
|
||||
public static class EmailTools {
|
||||
|
||||
/// <summary>
|
||||
/// Sends an email.
|
||||
/// </summary>
|
||||
/// <param name="recipient">The recipient.</param>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <param name="body">The message body.</param>
|
||||
/// <param name="html"><c>true</c> if the body is HTML.</param>
|
||||
public static void AsyncSendEmail(string recipient, string sender, string subject, string body, bool html) {
|
||||
System.Threading.ThreadPool.QueueUserWorkItem(delegate(object state) {
|
||||
MailMessage message = new MailMessage(sender, recipient, subject, body);
|
||||
message.IsBodyHtml = html;
|
||||
TrySendMessage(message);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to send a message, swallowing all exceptions.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to send.</param>
|
||||
private static void TrySendMessage(MailMessage message) {
|
||||
try {
|
||||
GenerateSmtpClient().Send(message);
|
||||
}
|
||||
catch(Exception ex) {
|
||||
if(ex is SmtpException) {
|
||||
Log.LogEntry("Unable to send Email: " + ex.Message, EntryType.Error, Log.SystemUsername);
|
||||
}
|
||||
else Log.LogEntry(ex.ToString(), EntryType.Error, Log.SystemUsername);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a new SMTP client with the proper settings.
|
||||
/// </summary>
|
||||
/// <returns>The generates SMTP client.</returns>
|
||||
private static SmtpClient GenerateSmtpClient() {
|
||||
SmtpClient client = new SmtpClient(Settings.SmtpServer);
|
||||
if(Settings.SmtpUsername.Length > 0) {
|
||||
client.Credentials = new NetworkCredential(Settings.SmtpUsername, Settings.SmtpPassword);
|
||||
}
|
||||
client.EnableSsl = Settings.SmtpSsl;
|
||||
if(Settings.SmtpPort != -1) client.Port = Settings.SmtpPort;
|
||||
else if(Settings.SmtpSsl) client.Port = 465;
|
||||
return client;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the email addresses of a set of users.
|
||||
/// </summary>
|
||||
/// <param name="users">The users.</param>
|
||||
/// <returns>The email addresses.</returns>
|
||||
public static string[] GetRecipients(UserInfo[] users) {
|
||||
if(users == null) return new string[0];
|
||||
|
||||
string[] result = new string[users.Length];
|
||||
|
||||
for(int i = 0; i < result.Length; i++) {
|
||||
result[i] = users[i].Email;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously sends a mass email, using BCC.
|
||||
/// </summary>
|
||||
/// <param name="recipients">The recipents.</param>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="html"><c>true</c> if the body is HTML.</param>
|
||||
public static void AsyncSendMassEmail(string[] recipients, string sender, string subject, string body, bool html) {
|
||||
if(recipients.Length == 0) return;
|
||||
|
||||
System.Threading.ThreadPool.QueueUserWorkItem(delegate(object state) {
|
||||
MailMessage message = new MailMessage(new MailAddress(sender), new MailAddress(sender));
|
||||
message.Subject = subject;
|
||||
message.Body = body;
|
||||
for(int i = 0; i < recipients.Length; i++) {
|
||||
message.Bcc.Add(new MailAddress(recipients[i]));
|
||||
}
|
||||
message.IsBodyHtml = html;
|
||||
TrySendMessage(message);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies an error to the email addresses set in the configuration, swallowing all exceptions.
|
||||
/// </summary>
|
||||
/// <param name="ex">The exception to notify.</param>
|
||||
/// <param name="url">The URL that caused the error, if any.</param>
|
||||
public static void NotifyError(Exception ex, string url) {
|
||||
try {
|
||||
string[] recipients = Settings.ErrorsEmails;
|
||||
|
||||
if(recipients.Length > 0) {
|
||||
AsyncSendMassEmail(recipients, Settings.SenderEmail, "Error Notification", "An error occurred on " +
|
||||
DateTime.Now.ToString("yyyy'/'MM'/'dd' 'HH':'mm':'ss") + " (server time) in the wiki hosted at " +
|
||||
Settings.MainUrl + " - server stack trace follows.\r\n\r\n" +
|
||||
(!string.IsNullOrEmpty(url) ? url + "\r\n\r\n" : "") +
|
||||
ex.ToString(), false);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
113
Core/FileDocument.cs
Normal file
113
Core/FileDocument.cs
Normal file
|
@ -0,0 +1,113 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.SearchEngine;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Represents a file document.
|
||||
/// </summary>
|
||||
public class FileDocument : IDocument {
|
||||
|
||||
/// <summary>
|
||||
/// The type tag for a <see cref="T:FileDocument" />.
|
||||
/// </summary>
|
||||
public const string StandardTypeTag = "F";
|
||||
|
||||
private uint id;
|
||||
private string name;
|
||||
private string title;
|
||||
private string typeTag = StandardTypeTag;
|
||||
private DateTime dateTime;
|
||||
private string provider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:FileDocument" /> class.
|
||||
/// </summary>
|
||||
/// <param name="fullName">The file full name.</param>
|
||||
/// <param name="provider">The file provider.</param>
|
||||
/// <param name="dateTime">The modification date/time.</param>
|
||||
public FileDocument(string fullName, string provider, DateTime dateTime) {
|
||||
if(fullName == null) throw new ArgumentNullException("fullName");
|
||||
if(fullName.Length == 0) throw new ArgumentException("Full Name cannot be empty", "fullName");
|
||||
if(provider == null) throw new ArgumentNullException("provider");
|
||||
if(provider.Length == 0) throw new ArgumentException("Provider cannot be empty", "provider");
|
||||
|
||||
id = Tools.HashDocumentNameForTemporaryIndex(fullName);
|
||||
name = provider + "|" + fullName;
|
||||
title = fullName.Substring(Tools.GetDirectoryName(fullName).Length);
|
||||
this.dateTime = dateTime;
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:FileDocument" /> class.
|
||||
/// </summary>
|
||||
/// <param name="doc">The dumped document.</param>
|
||||
public FileDocument(DumpedDocument doc) {
|
||||
string[] fields = doc.Name.Split('|');
|
||||
|
||||
id = doc.ID;
|
||||
name = doc.Name;
|
||||
title = doc.Title;
|
||||
dateTime = doc.DateTime;
|
||||
provider = fields[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the globally unique ID of the document.
|
||||
/// </summary>
|
||||
public uint ID {
|
||||
get { return id; }
|
||||
set { id = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the globally-unique name of the document.
|
||||
/// </summary>
|
||||
public string Name {
|
||||
get { return name; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the title of the document, if any.
|
||||
/// </summary>
|
||||
public string Title {
|
||||
get { return title; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tag for the document type.
|
||||
/// </summary>
|
||||
public string TypeTag {
|
||||
get { return typeTag; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the document date/time.
|
||||
/// </summary>
|
||||
public DateTime DateTime {
|
||||
get { return dateTime; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the tokenization of the document content.
|
||||
/// </summary>
|
||||
/// <param name="content">The content to tokenize.</param>
|
||||
/// <returns>The extracted words and their positions (always an empty array).</returns>
|
||||
public WordInfo[] Tokenize(string content) {
|
||||
return ScrewTurn.Wiki.SearchEngine.Tools.Tokenize(content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the provider.
|
||||
/// </summary>
|
||||
public string Provider {
|
||||
get { return provider; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
294
Core/FilesAndAttachments.cs
Normal file
294
Core/FilesAndAttachments.cs
Normal file
|
@ -0,0 +1,294 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
using System.IO;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Manages files, directories and attachments.
|
||||
/// </summary>
|
||||
public static class FilesAndAttachments {
|
||||
|
||||
#region Files
|
||||
|
||||
/// <summary>
|
||||
/// Finds the provider that has a file.
|
||||
/// </summary>
|
||||
/// <param name="fullName">The full name of the file.</param>
|
||||
/// <returns>The provider that has the file, or <c>null</c> if the file could not be found.</returns>
|
||||
public static IFilesStorageProviderV30 FindFileProvider(string fullName) {
|
||||
if(fullName == null) throw new ArgumentNullException("fullName");
|
||||
if(string.IsNullOrEmpty(fullName)) throw new ArgumentException("Full Name cannot be empty", "fullName");
|
||||
|
||||
fullName = NormalizeFullName(fullName);
|
||||
|
||||
foreach(IFilesStorageProviderV30 provider in Collectors.FilesProviderCollector.AllProviders) {
|
||||
FileDetails details = provider.GetFileDetails(fullName);
|
||||
if(details != null) return provider;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the details of a file.
|
||||
/// </summary>
|
||||
/// <param name="fullName">The full name of the file.</param>
|
||||
/// <returns>The details of the file, or <c>null</c> if no file is found.</returns>
|
||||
public static FileDetails GetFileDetails(string fullName) {
|
||||
if(fullName == null) throw new ArgumentNullException("fullName");
|
||||
if(string.IsNullOrEmpty(fullName)) throw new ArgumentException("Full Name cannot be empty", "fullName");
|
||||
|
||||
fullName = NormalizeFullName(fullName);
|
||||
|
||||
foreach(IFilesStorageProviderV30 provider in Collectors.FilesProviderCollector.AllProviders) {
|
||||
FileDetails details = provider.GetFileDetails(fullName);
|
||||
if(details != null) return details;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a File.
|
||||
/// </summary>
|
||||
/// <param name="fullName">The full name of the File.</param>
|
||||
/// <param name="output">The output stream.</param>
|
||||
/// <param name="countHit">A value indicating whether or not to count this retrieval in the statistics.</param>
|
||||
/// <returns><c>true</c> if the file is retrieved, <c>false</c> otherwise.</returns>
|
||||
public static bool RetrieveFile(string fullName, Stream output, bool countHit) {
|
||||
if(fullName == null) throw new ArgumentNullException("fullName");
|
||||
if(fullName.Length == 0) throw new ArgumentException("Full Name cannot be empty", "fullName");
|
||||
if(output == null) throw new ArgumentNullException("destinationStream");
|
||||
if(!output.CanWrite) throw new ArgumentException("Cannot write into Destination Stream", "destinationStream");
|
||||
|
||||
fullName = NormalizeFullName(fullName);
|
||||
|
||||
IFilesStorageProviderV30 provider = FindFileProvider(fullName);
|
||||
|
||||
if(provider == null) return false;
|
||||
else return provider.RetrieveFile(fullName, output, countHit);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Directories
|
||||
|
||||
/// <summary>
|
||||
/// Finds the provider that has a directory.
|
||||
/// </summary>
|
||||
/// <param name="fullPath">The full path of the directory.</param>
|
||||
/// <returns>The provider that has the directory, or <c>null</c> if no directory is found.</returns>
|
||||
public static IFilesStorageProviderV30 FindDirectoryProvider(string fullPath) {
|
||||
if(fullPath == null) throw new ArgumentNullException("fullPath");
|
||||
if(fullPath.Length == 0) throw new ArgumentException("Full Path cannot be empty");
|
||||
|
||||
fullPath = NormalizeFullPath(fullPath);
|
||||
|
||||
// In order to verify that the full path exists, it is necessary to navigate
|
||||
// from the root down to the specified directory level
|
||||
// Example: /my/very/nested/directory/structure/
|
||||
// 1. Check that / contains /my/
|
||||
// 2. Check that /my/ contains /my/very/
|
||||
// 3. ...
|
||||
|
||||
// allLevels contains this:
|
||||
// /my/very/nested/directory/structure/
|
||||
// /my/very/nested/directory/
|
||||
// /my/very/nested/
|
||||
// /my/very/
|
||||
// /my/
|
||||
// /
|
||||
|
||||
string oneLevelUp = fullPath;
|
||||
|
||||
List<string> allLevels = new List<string>(10);
|
||||
allLevels.Add(fullPath.ToLowerInvariant());
|
||||
while(oneLevelUp != "/") {
|
||||
oneLevelUp = UpOneLevel(oneLevelUp);
|
||||
allLevels.Add(oneLevelUp.ToLowerInvariant());
|
||||
}
|
||||
|
||||
foreach(IFilesStorageProviderV30 provider in Collectors.FilesProviderCollector.AllProviders) {
|
||||
bool allLevelsFound = true;
|
||||
|
||||
for(int i = allLevels.Count - 1; i >= 1; i--) {
|
||||
string[] dirs = provider.ListDirectories(allLevels[i]);
|
||||
|
||||
string nextLevel =
|
||||
(from d in dirs
|
||||
where d.ToLowerInvariant() == allLevels[i - 1]
|
||||
select d).FirstOrDefault();
|
||||
|
||||
if(string.IsNullOrEmpty(nextLevel)) {
|
||||
allLevelsFound = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(allLevelsFound) return provider;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists the directories in a directory.
|
||||
/// </summary>
|
||||
/// <param name="fullPath">The full path.</param>
|
||||
/// <returns>The directories.</returns>
|
||||
/// <remarks>If the specified directory is the root, then the list is performed on all providers.</remarks>
|
||||
public static string[] ListDirectories(string fullPath) {
|
||||
fullPath = NormalizeFullPath(fullPath);
|
||||
|
||||
if(fullPath == "/") {
|
||||
List<string> directories = new List<string>(50);
|
||||
|
||||
foreach(IFilesStorageProviderV30 provider in Collectors.FilesProviderCollector.AllProviders) {
|
||||
directories.AddRange(provider.ListDirectories(fullPath));
|
||||
}
|
||||
|
||||
directories.Sort();
|
||||
|
||||
return directories.ToArray();
|
||||
}
|
||||
else {
|
||||
IFilesStorageProviderV30 provider = FindDirectoryProvider(fullPath);
|
||||
return provider.ListDirectories(fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists the files in a directory.
|
||||
/// </summary>
|
||||
/// <param name="fullPath">The full path.</param>
|
||||
/// <returns>The files.</returns>
|
||||
/// <remarks>If the specified directory is the root, then the list is performed on all providers.</remarks>
|
||||
public static string[] ListFiles(string fullPath) {
|
||||
fullPath = NormalizeFullPath(fullPath);
|
||||
|
||||
if(fullPath == "/") {
|
||||
List<string> files = new List<string>(50);
|
||||
|
||||
foreach(IFilesStorageProviderV30 provider in Collectors.FilesProviderCollector.AllProviders) {
|
||||
files.AddRange(provider.ListFiles(fullPath));
|
||||
}
|
||||
|
||||
files.Sort();
|
||||
|
||||
return files.ToArray();
|
||||
}
|
||||
else {
|
||||
IFilesStorageProviderV30 provider = FindDirectoryProvider(fullPath);
|
||||
return provider.ListFiles(fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Page Attachments
|
||||
|
||||
/// <summary>
|
||||
/// Finds the provider that has a page attachment.
|
||||
/// </summary>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <param name="attachmentName">The name of the attachment.</param>
|
||||
/// <returns>The provider that has the attachment, or <c>null</c> if the attachment could not be found.</returns>
|
||||
public static IFilesStorageProviderV30 FindPageAttachmentProvider(PageInfo page, string attachmentName) {
|
||||
if(page == null) throw new ArgumentNullException("page");
|
||||
if(attachmentName == null) throw new ArgumentNullException("attachmentName");
|
||||
if(attachmentName.Length == 0) throw new ArgumentException("Attachment Name cannot be empty", "attachmentName");
|
||||
|
||||
foreach(IFilesStorageProviderV30 provider in Collectors.FilesProviderCollector.AllProviders) {
|
||||
FileDetails details = provider.GetPageAttachmentDetails(page, attachmentName);
|
||||
if(details != null) return provider;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the details of a page attachment.
|
||||
/// </summary>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <param name="attachmentName">The name of the attachment.</param>
|
||||
/// <returns>The details of the attachment, or <c>null</c> if the attachment could not be found.</returns>
|
||||
public static FileDetails GetPageAttachmentDetails(PageInfo page, string attachmentName) {
|
||||
if(page == null) throw new ArgumentNullException("page");
|
||||
if(attachmentName == null) throw new ArgumentNullException("attachmentName");
|
||||
if(attachmentName.Length == 0) throw new ArgumentException("Attachment Name cannot be empty", "attachmentName");
|
||||
|
||||
foreach(IFilesStorageProviderV30 provider in Collectors.FilesProviderCollector.AllProviders) {
|
||||
FileDetails details = provider.GetPageAttachmentDetails(page, attachmentName);
|
||||
if(details != null) return details;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a Page Attachment.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page Info that owns the Attachment.</param>
|
||||
/// <param name="attachmentName">The name of the Attachment, for example "myfile.jpg".</param>
|
||||
/// <param name="output">The output stream.</param>
|
||||
/// <param name="countHit">A value indicating whether or not to count this retrieval in the statistics.</param>
|
||||
/// <returns><c>true</c> if the Attachment is retrieved, <c>false</c> otherwise.</returns>
|
||||
public static bool RetrievePageAttachment(PageInfo page, string attachmentName, Stream output, bool countHit) {
|
||||
if(page == null) throw new ArgumentNullException("pageInfo");
|
||||
if(attachmentName == null) throw new ArgumentNullException("name");
|
||||
if(attachmentName.Length == 0) throw new ArgumentException("Name cannot be empty", "name");
|
||||
if(output == null) throw new ArgumentNullException("destinationStream");
|
||||
if(!output.CanWrite) throw new ArgumentException("Cannot write into Destination Stream", "destinationStream");
|
||||
|
||||
IFilesStorageProviderV30 provider = FindPageAttachmentProvider(page, attachmentName);
|
||||
|
||||
if(provider == null) return false;
|
||||
else return provider.RetrievePageAttachment(page, attachmentName, output, countHit);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes a full name.
|
||||
/// </summary>
|
||||
/// <param name="fullName">The full name.</param>
|
||||
/// <returns>The normalized full name.</returns>
|
||||
private static string NormalizeFullName(string fullName) {
|
||||
if(!fullName.StartsWith("/")) fullName = "/" + fullName;
|
||||
return fullName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes a full path.
|
||||
/// </summary>
|
||||
/// <param name="fullPath">The full path.</param>
|
||||
/// <returns>The normalized full path.</returns>
|
||||
private static string NormalizeFullPath(string fullPath) {
|
||||
if(fullPath == null) return "/";
|
||||
if(!fullPath.StartsWith("/")) fullPath = "/" + fullPath;
|
||||
if(!fullPath.EndsWith("/")) fullPath += "/";
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Goes up one level in a directory path.
|
||||
/// </summary>
|
||||
/// <param name="fullPath">The full path, normalized, different from "/".</param>
|
||||
/// <returns>The directory.</returns>
|
||||
private static string UpOneLevel(string fullPath) {
|
||||
if(fullPath == "/") throw new ArgumentException("Cannot navigate up from the root");
|
||||
|
||||
string temp = fullPath.Trim('/');
|
||||
int lastIndex = temp.LastIndexOf("/");
|
||||
|
||||
return "/" + temp.Substring(0, lastIndex + 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
989
Core/FilesStorageProvider.cs
Normal file
989
Core/FilesStorageProvider.cs
Normal file
|
@ -0,0 +1,989 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Implements a Local Files Storage Provider.
|
||||
/// </summary>
|
||||
public class FilesStorageProvider : IFilesStorageProviderV30 {
|
||||
|
||||
private readonly ComponentInformation info = new ComponentInformation("Local Files Provider",
|
||||
"ScrewTurn Software", Settings.WikiVersion, "http://www.screwturn.eu", null);
|
||||
|
||||
// The following strings MUST terminate with DirectorySeparatorPath in order to properly work
|
||||
// in BuildFullPath method
|
||||
private readonly string UploadDirectory = "Upload" + Path.DirectorySeparatorChar;
|
||||
private readonly string AttachmentsDirectory = "Attachments" + Path.DirectorySeparatorChar;
|
||||
|
||||
private const string FileDownloadsFile = "FileDownloads.cs";
|
||||
private const string AttachmentDownloadsFile = "AttachmentDownloads.cs";
|
||||
|
||||
// 16 KB buffer used in the StreamCopy method
|
||||
// 16 KB seems to be the best break-even between performance and memory usage
|
||||
private const int BufferSize = 16384;
|
||||
|
||||
private IHostV30 host;
|
||||
|
||||
private string GetFullPath(string finalChunk) {
|
||||
return Path.Combine(host.GetSettingValue(SettingName.PublicDirectory), finalChunk);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Storage Provider.
|
||||
/// </summary>
|
||||
/// <param name="host">The Host of the Component.</param>
|
||||
/// <param name="config">The Configuration data, if any.</param>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="host"/> or <paramref name="config"/> are <c>null</c>.</exception>
|
||||
/// <exception cref="InvalidConfigurationException">If <paramref name="config"/> is not valid or is incorrect.</exception>
|
||||
public void Init(IHostV30 host, string config) {
|
||||
if(host == null) throw new ArgumentNullException("host");
|
||||
if(config == null) throw new ArgumentNullException("config");
|
||||
|
||||
this.host = host;
|
||||
|
||||
if(!LocalProvidersTools.CheckWritePermissions(host.GetSettingValue(SettingName.PublicDirectory))) {
|
||||
throw new InvalidConfigurationException("Cannot write into the public directory - check permissions");
|
||||
}
|
||||
|
||||
// Create directories, if needed
|
||||
if(!Directory.Exists(GetFullPath(UploadDirectory))) {
|
||||
Directory.CreateDirectory(GetFullPath(UploadDirectory));
|
||||
}
|
||||
if(!Directory.Exists(GetFullPath(AttachmentsDirectory))) {
|
||||
Directory.CreateDirectory(GetFullPath(AttachmentsDirectory));
|
||||
}
|
||||
if(!File.Exists(GetFullPath(FileDownloadsFile))) {
|
||||
File.Create(GetFullPath(FileDownloadsFile)).Close();
|
||||
}
|
||||
if(!File.Exists(GetFullPath(AttachmentDownloadsFile))) {
|
||||
File.Create(GetFullPath(AttachmentDownloadsFile)).Close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method invoked on shutdown.
|
||||
/// </summary>
|
||||
/// <remarks>This method might not be invoked in some cases.</remarks>
|
||||
public void Shutdown() {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Information about the Provider.
|
||||
/// </summary>
|
||||
public ComponentInformation Information {
|
||||
get { return info; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a brief summary of the configuration string format, in HTML. Returns <c>null</c> if no configuration is needed.
|
||||
/// </summary>
|
||||
public string ConfigHelpHtml {
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value specifying whether the provider is read-only, i.e. it can only provide data and not store it.
|
||||
/// </summary>
|
||||
public bool ReadOnly {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a full path from a provider-specific partial path.
|
||||
/// </summary>
|
||||
/// <param name="partialPath">The partial path.</param>
|
||||
/// <returns>The full path.</returns>
|
||||
/// <remarks>For example: if <b>partialPath</b> is "/my/directory", the method returns
|
||||
/// "C:\Inetpub\wwwroot\Wiki\public\Upload\my\directory", assuming the Wiki resides in "C:\Inetpub\wwwroot\Wiki".</remarks>
|
||||
private string BuildFullPath(string partialPath) {
|
||||
if(partialPath == null) partialPath = "";
|
||||
partialPath = partialPath.Replace("/", Path.DirectorySeparatorChar.ToString()).TrimStart(Path.DirectorySeparatorChar);
|
||||
string up = Path.Combine(host.GetSettingValue(SettingName.PublicDirectory), UploadDirectory);
|
||||
return Path.Combine(up, partialPath); // partialPath CANNOT start with "\" -> Path.Combine does not work
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a full path from a provider-specific partial path.
|
||||
/// </summary>
|
||||
/// <param name="partialPath">The partial path.</param>
|
||||
/// <returns>The full path.</returns>
|
||||
/// <remarks>For example: if <b>partialPath</b> is "/my/directory", the method returns
|
||||
/// "C:\Inetpub\wwwroot\Wiki\public\Attachments\my\directory", assuming the Wiki resides in "C:\Inetpub\wwwroot\Wiki".</remarks>
|
||||
private string BuildFullPathForAttachments(string partialPath) {
|
||||
if(partialPath == null) partialPath = "";
|
||||
partialPath = partialPath.Replace("/", Path.DirectorySeparatorChar.ToString()).TrimStart(Path.DirectorySeparatorChar);
|
||||
string up = Path.Combine(host.GetSettingValue(SettingName.PublicDirectory), AttachmentsDirectory);
|
||||
return Path.Combine(up, partialPath); // partialPath CANNOT start with "\" -> Path.Combine does not work
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists the Files in the specified Directory.
|
||||
/// </summary>
|
||||
/// <param name="directory">The full directory name, for example "/my/directory". Null, empty or "/" for the root directory.</param>
|
||||
/// <returns>The list of Files in the directory.</returns>
|
||||
/// <exception cref="ArgumentException">If <paramref name="directory"/> does not exist.</exception>
|
||||
public string[] ListFiles(string directory) {
|
||||
string d = BuildFullPath(directory);
|
||||
if(!Directory.Exists(d)) throw new ArgumentException("Directory does not exist", "directory");
|
||||
|
||||
string[] temp = Directory.GetFiles(d);
|
||||
|
||||
// Result must be transformed in the form /my/dir/file.ext
|
||||
List<string> res = new List<string>(temp.Length);
|
||||
string root = GetFullPath(UploadDirectory);
|
||||
foreach(string s in temp) {
|
||||
// root = C:\blah\ - ends with '\'
|
||||
res.Add(s.Substring(root.Length - 1).Replace(Path.DirectorySeparatorChar, '/'));
|
||||
}
|
||||
|
||||
return res.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists the Directories in the specified directory.
|
||||
/// </summary>
|
||||
/// <param name="directory">The full directory name, for example "/my/directory". Null, empty or "/" for the root directory.</param>
|
||||
/// <returns>The list of Directories in the Directory.</returns>
|
||||
/// <exception cref="ArgumentException">If <paramref name="directory"/> does not exist.</exception>
|
||||
public string[] ListDirectories(string directory) {
|
||||
string d = BuildFullPath(directory);
|
||||
if(!Directory.Exists(d)) throw new ArgumentException("Directory does not exist", "directory");
|
||||
|
||||
string[] temp = Directory.GetDirectories(d);
|
||||
|
||||
// Result must be transformed in the form /my/dir
|
||||
List<string> res = new List<string>(temp.Length);
|
||||
string root = GetFullPath(UploadDirectory);
|
||||
foreach(string s in temp) {
|
||||
// root = C:\blah\ - ends with '\'
|
||||
res.Add(s.Substring(root.Length - 1).Replace(Path.DirectorySeparatorChar, '/') + "/");
|
||||
}
|
||||
|
||||
return res.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies data from a Stream to another.
|
||||
/// </summary>
|
||||
/// <param name="source">The Source stream.</param>
|
||||
/// <param name="destination">The destination Stream.</param>
|
||||
private static void StreamCopy(Stream source, Stream destination) {
|
||||
byte[] buff = new byte[BufferSize];
|
||||
int copied = 0;
|
||||
do {
|
||||
copied = source.Read(buff, 0, buff.Length);
|
||||
if(copied > 0) {
|
||||
destination.Write(buff, 0, copied);
|
||||
}
|
||||
} while(copied > 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores a file.
|
||||
/// </summary>
|
||||
/// <param name="fullName">The full name of the file.</param>
|
||||
/// <param name="sourceStream">A Stream object used as <b>source</b> of a byte stream,
|
||||
/// i.e. the method reads from the Stream and stores the content properly.</param>
|
||||
/// <param name="overwrite"><c>true</c> to overwrite an existing file.</param>
|
||||
/// <returns><c>true</c> if the File is stored, <c>false</c> otherwise.</returns>
|
||||
/// <remarks>If <b>overwrite</b> is <c>false</c> and File already exists, the method returns <c>false</c>.</remarks>
|
||||
/// <exception cref="ArgumentNullException">If <typeparamref name="fullName"/> os <paramref name="sourceStream"/> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="fullName"/> is empty or <paramref name="sourceStream"/> does not support reading.</exception>
|
||||
public bool StoreFile(string fullName, Stream sourceStream, bool overwrite) {
|
||||
if(fullName == null) throw new ArgumentNullException("fullName");
|
||||
if(fullName.Length == 0) throw new ArgumentException("Full Name cannot be empty", "fullName");
|
||||
if(sourceStream == null) throw new ArgumentNullException("sourceStream");
|
||||
if(!sourceStream.CanRead) throw new ArgumentException("Cannot read from Source Stream", "sourceStream");
|
||||
|
||||
string filename = BuildFullPath(fullName);
|
||||
|
||||
// Abort if the file already exists and overwrite is false
|
||||
if(File.Exists(filename) && !overwrite) return false;
|
||||
|
||||
FileStream fs = null;
|
||||
|
||||
bool done = false;
|
||||
|
||||
try {
|
||||
fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
|
||||
// StreamCopy content (throws exception in case of error)
|
||||
StreamCopy(sourceStream, fs);
|
||||
|
||||
done = true;
|
||||
}
|
||||
catch(IOException) {
|
||||
done = false;
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
fs.Close();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a File.
|
||||
/// </summary>
|
||||
/// <param name="fullName">The full name of the File.</param>
|
||||
/// <param name="destinationStream">A Stream object used as <b>destination</b> of a byte stream,
|
||||
/// i.e. the method writes to the Stream the file content.</param>
|
||||
/// <param name="countHit">A value indicating whether or not to count this retrieval in the statistics.</param>
|
||||
/// <returns><c>true</c> if the file is retrieved, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <typeparamref name="fullName"/> os <paramref name="destinationStream"/> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="fullName"/> is empty or <paramref name="destinationStream"/> does not support writing, or if <paramref name="fullName"/> does not exist.</exception>
|
||||
public bool RetrieveFile(string fullName, Stream destinationStream, bool countHit) {
|
||||
if(fullName == null) throw new ArgumentNullException("fullName");
|
||||
if(fullName.Length == 0) throw new ArgumentException("Full Name cannot be empty", "fullName");
|
||||
if(destinationStream == null) throw new ArgumentNullException("destinationStream");
|
||||
if(!destinationStream.CanWrite) throw new ArgumentException("Cannot write into Destination Stream", "destinationStream");
|
||||
|
||||
string filename = BuildFullPath(fullName);
|
||||
|
||||
if(!File.Exists(filename)) throw new ArgumentException("File does not exist", "fullName");
|
||||
|
||||
FileStream fs = null;
|
||||
|
||||
bool done = false;
|
||||
|
||||
try {
|
||||
fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
|
||||
// StreamCopy content (throws exception in case of error)
|
||||
StreamCopy(fs, destinationStream);
|
||||
|
||||
done = true;
|
||||
}
|
||||
catch(IOException) {
|
||||
done = false;
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
fs.Close();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
if(countHit) {
|
||||
AddDownloadHit(fullName, GetFullPath(FileDownloadsFile));
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a download hit for the specified item in the specified output file.
|
||||
/// </summary>
|
||||
/// <param name="itemName">The item.</param>
|
||||
/// <param name="outputFile">The full path to the output file.</param>
|
||||
private void AddDownloadHit(string itemName, string outputFile) {
|
||||
lock(this) {
|
||||
string[] lines = File.ReadAllLines(outputFile);
|
||||
|
||||
string lowercaseItemName = itemName.ToLowerInvariant();
|
||||
|
||||
string[] fields;
|
||||
bool found = false;
|
||||
for(int i = 0; i < lines.Length; i++) {
|
||||
fields = lines[i].Split('|');
|
||||
|
||||
if(fields[0].ToLowerInvariant() == lowercaseItemName) {
|
||||
int count = 0;
|
||||
int.TryParse(fields[1], out count);
|
||||
count = count + 1;
|
||||
lines[i] = itemName + "|" + count.ToString();
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!found) {
|
||||
// Add a new line for the current item
|
||||
string[] newLines = new string[lines.Length + 1];
|
||||
Array.Copy(lines, 0, newLines, 0, lines.Length);
|
||||
newLines[newLines.Length - 1] = itemName + "|1";
|
||||
|
||||
lines = newLines;
|
||||
}
|
||||
|
||||
// Overwrite file with updated data
|
||||
File.WriteAllLines(outputFile, lines);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the download hits for the specified item in the specified file.
|
||||
/// </summary>
|
||||
/// <param name="itemName">The item.</param>
|
||||
/// <param name="outputFile">The full path of the output file.</param>
|
||||
/// <param name="count">The hit count to set.</param>
|
||||
private void SetDownloadHits(string itemName, string outputFile, int count) {
|
||||
lock(this) {
|
||||
string[] lines = File.ReadAllLines(outputFile);
|
||||
|
||||
List<string> outputLines = new List<string>(lines.Length);
|
||||
|
||||
string lowercaseItemName = itemName.ToLowerInvariant();
|
||||
|
||||
string[] fields;
|
||||
foreach(string line in lines) {
|
||||
fields = line.Split('|');
|
||||
|
||||
if(fields[0].ToLowerInvariant() == lowercaseItemName) {
|
||||
// Set the new count
|
||||
outputLines.Add(fields[0] + "|" + count.ToString());
|
||||
}
|
||||
else {
|
||||
// Copy data with no modification
|
||||
outputLines.Add(line);
|
||||
}
|
||||
}
|
||||
|
||||
File.WriteAllLines(outputFile, outputLines.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the download hits for the items that match <b>itemName</b> in the specified file.
|
||||
/// </summary>
|
||||
/// <param name="itemName">The first part of the item name.</param>
|
||||
/// <param name="outputFile">The full path of the output file.</param>
|
||||
private void ClearDownloadHitsPartialMatch(string itemName, string outputFile) {
|
||||
lock(this) {
|
||||
string[] lines = File.ReadAllLines(outputFile);
|
||||
|
||||
List<string> newLines = new List<string>(lines.Length);
|
||||
|
||||
string lowercaseItemName = itemName.ToLowerInvariant();
|
||||
|
||||
string[] fields;
|
||||
foreach(string line in lines) {
|
||||
fields = line.Split('|');
|
||||
|
||||
if(!fields[0].ToLowerInvariant().StartsWith(lowercaseItemName)) {
|
||||
newLines.Add(line);
|
||||
}
|
||||
}
|
||||
|
||||
File.WriteAllLines(outputFile, newLines.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renames an item of the download count list in the specified file.
|
||||
/// </summary>
|
||||
/// <param name="oldItemName">The old item name.</param>
|
||||
/// <param name="newItemName">The new item name.</param>
|
||||
/// <param name="outputFile">The full path of the output file.</param>
|
||||
private void RenameDownloadHitsItem(string oldItemName, string newItemName, string outputFile) {
|
||||
lock(this) {
|
||||
string[] lines = File.ReadAllLines(outputFile);
|
||||
|
||||
string lowercaseOldItemName = oldItemName.ToLowerInvariant();
|
||||
|
||||
string[] fields;
|
||||
bool found = false;
|
||||
for(int i = 0; i < lines.Length; i++) {
|
||||
fields = lines[i].Split('|');
|
||||
|
||||
if(fields[0].ToLowerInvariant() == lowercaseOldItemName) {
|
||||
lines[i] = newItemName + "|" + fields[1];
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(found) {
|
||||
File.WriteAllLines(outputFile, lines);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renames an item of the download count list in the specified file.
|
||||
/// </summary>
|
||||
/// <param name="oldItemName">The initial part of the old item name.</param>
|
||||
/// <param name="newItemName">The corresponding initial part of the new item name.</param>
|
||||
/// <param name="outputFile">The full path of the output file.</param>
|
||||
private void RenameDownloadHitsItemPartialMatch(string oldItemName, string newItemName, string outputFile) {
|
||||
lock(this) {
|
||||
string[] lines = File.ReadAllLines(outputFile);
|
||||
|
||||
string lowercaseOldItemName = oldItemName.ToLowerInvariant();
|
||||
|
||||
string[] fields;
|
||||
bool found = false;
|
||||
for(int i = 0; i < lines.Length; i++) {
|
||||
fields = lines[i].Split('|');
|
||||
|
||||
if(fields[0].ToLowerInvariant().StartsWith(lowercaseOldItemName)) {
|
||||
lines[i] = newItemName + fields[0].Substring(lowercaseOldItemName.Length) + "|" + fields[1];
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(found) {
|
||||
File.WriteAllLines(outputFile, lines);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of times a file was retrieved.
|
||||
/// </summary>
|
||||
/// <param name="fullName">The full name of the file.</param>
|
||||
/// <returns>The number of times the file was retrieved.</returns>
|
||||
private int GetFileRetrievalCount(string fullName) {
|
||||
if(fullName == null) throw new ArgumentNullException("fullName");
|
||||
if(fullName.Length == 0) throw new ArgumentException("Full Name cannot be empty", "fullName");
|
||||
|
||||
lock(this) {
|
||||
// Format
|
||||
// /Full/Path/To/File.txt|DownloadCount
|
||||
|
||||
string[] lines = File.ReadAllLines(GetFullPath(FileDownloadsFile));
|
||||
|
||||
string lowercaseFullName = fullName.ToLowerInvariant();
|
||||
|
||||
string[] fields;
|
||||
foreach(string line in lines) {
|
||||
fields = line.Split('|');
|
||||
if(fields[0].ToLowerInvariant() == lowercaseFullName) {
|
||||
int res = 0;
|
||||
if(int.TryParse(fields[1], out res)) return res;
|
||||
else return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the number of times a file was retrieved.
|
||||
/// </summary>
|
||||
/// <param name="fullName">The full name of the file.</param>
|
||||
/// <param name="count">The count to set.</param>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="fullName"/> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="fullName"/> is empty.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">If <paramref name="count"/> is less than zero.</exception>
|
||||
public void SetFileRetrievalCount(string fullName, int count) {
|
||||
if(fullName == null) throw new ArgumentNullException("fullName");
|
||||
if(fullName.Length == 0) throw new ArgumentException("Full Name cannot be empty", "fullName");
|
||||
if(count < 0) throw new ArgumentOutOfRangeException("count", "Count must be greater than or equal to zero");
|
||||
|
||||
SetDownloadHits(fullName, GetFullPath(FileDownloadsFile), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the details of a file.
|
||||
/// </summary>
|
||||
/// <param name="fullName">The full name of the file.</param>
|
||||
/// <returns>The details, or <c>null</c> if the file does not exist.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="fullName"/> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="fullName"/> is empty.</exception>
|
||||
public FileDetails GetFileDetails(string fullName) {
|
||||
if(fullName == null) throw new ArgumentNullException("fullName");
|
||||
if(fullName.Length == 0) throw new ArgumentException("Full Name cannot be empty", "fullName");
|
||||
|
||||
string n = BuildFullPath(fullName);
|
||||
|
||||
if(!File.Exists(n)) return null;
|
||||
|
||||
FileInfo fi = new FileInfo(n);
|
||||
|
||||
return new FileDetails(fi.Length, fi.LastWriteTime, GetFileRetrievalCount(fullName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a File.
|
||||
/// </summary>
|
||||
/// <param name="fullName">The full name of the File.</param>
|
||||
/// <returns><c>true</c> if the File is deleted, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="fullName"/> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="fullName"/> is empty or it does not exist.</exception>
|
||||
public bool DeleteFile(string fullName) {
|
||||
if(fullName == null) throw new ArgumentNullException("fullName");
|
||||
if(fullName.Length == 0) throw new ArgumentException("Full Name cannot be empty", "fullName");
|
||||
|
||||
string n = BuildFullPath(fullName);
|
||||
|
||||
if(!File.Exists(n)) throw new ArgumentException("File does not exist", "fullName");
|
||||
|
||||
try {
|
||||
File.Delete(n);
|
||||
SetDownloadHits(fullName, GetFullPath(FileDownloadsFile), 0);
|
||||
return true;
|
||||
}
|
||||
catch(IOException) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renames or moves a File.
|
||||
/// </summary>
|
||||
/// <param name="oldFullName">The old full name of the File.</param>
|
||||
/// <param name="newFullName">The new full name of the File.</param>
|
||||
/// <returns><c>true</c> if the File is renamed, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="oldFullName"/> or <paramref name="newFullName"/> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="oldFullName"/> or <paramref name="newFullName"/> are empty, or if the old file does not exist, or if the new file already exist.</exception>
|
||||
public bool RenameFile(string oldFullName, string newFullName) {
|
||||
if(oldFullName == null) throw new ArgumentNullException("oldFullName");
|
||||
if(oldFullName.Length == 0) throw new ArgumentException("Old Full Name cannot be empty", "oldFullName");
|
||||
if(newFullName == null) throw new ArgumentNullException("newFullName");
|
||||
if(newFullName.Length == 0) throw new ArgumentException("New Full Name cannot be empty", "newFullName");
|
||||
|
||||
string oldFilename = BuildFullPath(oldFullName);
|
||||
string newFilename = BuildFullPath(newFullName);
|
||||
|
||||
if(!File.Exists(oldFilename)) throw new ArgumentException("Old File does not exist", "oldFullName");
|
||||
if(File.Exists(newFilename)) throw new ArgumentException("New File already exists", "newFullName");
|
||||
|
||||
try {
|
||||
File.Move(oldFilename, newFilename);
|
||||
RenameDownloadHitsItem(oldFullName, newFullName, GetFullPath(FileDownloadsFile));
|
||||
return true;
|
||||
}
|
||||
catch(IOException) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to create the new Directory in.</param>
|
||||
/// <param name="name">The name of the new Directory.</param>
|
||||
/// <returns><c>true</c> if the Directory is created, <c>false</c> otherwise.</returns>
|
||||
/// <remarks>If <b>path</b> is "/my/directory" and <b>name</b> is "newdir", a new directory named "/my/directory/newdir" is created.</remarks>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="path"/> or <paramref name="name"/> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="name"/> is empty or if the directory does not exist, or if the new directory already exists.</exception>
|
||||
public bool CreateDirectory(string path, string name) {
|
||||
if(path == null) throw new ArgumentNullException("path");
|
||||
if(name == null) throw new ArgumentNullException("name");
|
||||
if(name.Length == 0) throw new ArgumentException("Name cannot be empty", "name");
|
||||
|
||||
if(!Directory.Exists(BuildFullPath(path))) throw new ArgumentException("Directory does not exist", "path");
|
||||
|
||||
string partialPath = path + (!path.EndsWith("/") ? "/" : "") + name;
|
||||
string d = BuildFullPath(partialPath);
|
||||
|
||||
if(Directory.Exists(d)) throw new ArgumentException("Directory already exists", "name");
|
||||
|
||||
try {
|
||||
Directory.CreateDirectory(d);
|
||||
return true;
|
||||
}
|
||||
catch(IOException) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a Directory and <b>all of its content</b>.
|
||||
/// </summary>
|
||||
/// <param name="fullPath">The full path of the Directory.</param>
|
||||
/// <returns><c>true</c> if the Directory is delete, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="fullPath"/> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="fullPath"/> is empty or if it equals '/' or it does not exist.</exception>
|
||||
public bool DeleteDirectory(string fullPath) {
|
||||
if(fullPath == null) throw new ArgumentNullException("fullPath");
|
||||
if(fullPath.Length == 0) throw new ArgumentException("Full Path cannot be empty", "fullPath");
|
||||
if(fullPath == "/") throw new ArgumentException("Cannot delete the root directory", "fullPath");
|
||||
|
||||
string d = BuildFullPath(fullPath);
|
||||
|
||||
if(!Directory.Exists(d)) throw new ArgumentException("Directory does not exist", "fullPath");
|
||||
|
||||
try {
|
||||
Directory.Delete(d, true);
|
||||
// Make sure tht fullPath ends with "/" so that the method does not clear wrong items
|
||||
if(!fullPath.EndsWith("/")) fullPath += "/";
|
||||
ClearDownloadHitsPartialMatch(fullPath, GetFullPath(FileDownloadsFile));
|
||||
return true;
|
||||
}
|
||||
catch(IOException) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renames or moves a Directory.
|
||||
/// </summary>
|
||||
/// <param name="oldFullPath">The old full path of the Directory.</param>
|
||||
/// <param name="newFullPath">The new full path of the Directory.</param>
|
||||
/// <returns><c>true</c> if the Directory is renamed, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="oldFullPath"/> or <paramref name="newFullPath"/> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="oldFullPath"/> or <paramref name="newFullPath"/> are empty or equal to '/',
|
||||
/// or if the old directory does not exist or the new directory already exists.</exception>
|
||||
public bool RenameDirectory(string oldFullPath, string newFullPath) {
|
||||
if(oldFullPath == null) throw new ArgumentNullException("oldFullPath");
|
||||
if(oldFullPath.Length == 0) throw new ArgumentException("Old Full Path cannot be empty", "oldFullPath");
|
||||
if(oldFullPath == "/") throw new ArgumentException("Cannot rename the root directory", "oldFullPath");
|
||||
if(newFullPath == null) throw new ArgumentNullException("newFullPath");
|
||||
if(newFullPath.Length == 0) throw new ArgumentException("New Full Path cannot be empty", "newFullPath");
|
||||
if(newFullPath == "/") throw new ArgumentException("Cannot rename directory to the root directory", "newFullPath");
|
||||
|
||||
string olddir = BuildFullPath(oldFullPath);
|
||||
string newdir = BuildFullPath(newFullPath);
|
||||
|
||||
if(!Directory.Exists(olddir)) throw new ArgumentException("Directory does not exist", "oldFullPath");
|
||||
if(Directory.Exists(newdir)) throw new ArgumentException("Directory already exists", "newFullPath");
|
||||
|
||||
try {
|
||||
Directory.Move(olddir, newdir);
|
||||
// Make sure that oldFullPath and newFullPath end with "/" so that the method does not rename wrong items
|
||||
if(!oldFullPath.EndsWith("/")) oldFullPath += "/";
|
||||
if(!newFullPath.EndsWith("/")) newFullPath += "/";
|
||||
RenameDownloadHitsItemPartialMatch(oldFullPath, newFullPath, GetFullPath(FileDownloadsFile));
|
||||
return true;
|
||||
}
|
||||
catch(IOException) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the Directory containing the Attachments of a Page.
|
||||
/// </summary>
|
||||
/// <param name="pageInfo">The Page Info.</param>
|
||||
/// <returns>The name of the Directory (not the full path) that contains the Attachments of the specified Page.</returns>
|
||||
private string GetPageAttachmentDirectory(PageInfo pageInfo) {
|
||||
// Use the Hash to avoid problems with special chars and the like
|
||||
// Using the hash prevents GetPageWithAttachments to work
|
||||
//return Hash.Compute(pageInfo.FullName);
|
||||
|
||||
return pageInfo.FullName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The the names of the pages with attachments.
|
||||
/// </summary>
|
||||
/// <returns>The names of the pages with attachments.</returns>
|
||||
public string[] GetPagesWithAttachments() {
|
||||
string[] directories = Directory.GetDirectories(GetFullPath(AttachmentsDirectory));
|
||||
string[] result = new string[directories.Length];
|
||||
for(int i = 0; i < result.Length; i++) {
|
||||
result[i] = Path.GetFileName(directories[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the names of the Attachments of a Page.
|
||||
/// </summary>
|
||||
/// <param name="pageInfo">The Page Info object that owns the Attachments.</param>
|
||||
/// <returns>The names, or an empty list.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="pageInfo"/> is <c>null</c>.</exception>
|
||||
public string[] ListPageAttachments(PageInfo pageInfo) {
|
||||
if(pageInfo == null) throw new ArgumentNullException("pageInfo");
|
||||
|
||||
string dir = BuildFullPathForAttachments(GetPageAttachmentDirectory(pageInfo));
|
||||
|
||||
if(!Directory.Exists(dir)) return new string[0];
|
||||
|
||||
string[] files = Directory.GetFiles(dir);
|
||||
|
||||
// Result must contain only the filename, not the full path
|
||||
List<string> result = new List<string>(files.Length);
|
||||
foreach(string f in files) {
|
||||
result.Add(Path.GetFileName(f));
|
||||
}
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores a Page Attachment.
|
||||
/// </summary>
|
||||
/// <param name="pageInfo">The Page Info that owns the Attachment.</param>
|
||||
/// <param name="name">The name of the Attachment, for example "myfile.jpg".</param>
|
||||
/// <param name="sourceStream">A Stream object used as <b>source</b> of a byte stream,
|
||||
/// i.e. the method reads from the Stream and stores the content properly.</param>
|
||||
/// <param name="overwrite"><c>true</c> to overwrite an existing Attachment.</param>
|
||||
/// <returns><c>true</c> if the Attachment is stored, <c>false</c> otherwise.</returns>
|
||||
/// <remarks>If <b>overwrite</b> is <c>false</c> and Attachment already exists, the method returns <c>false</c>.</remarks>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="pageInfo"/>, <paramref name="name"/> or <paramref name="sourceStream"/> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="name"/> is empty or if <paramref name="sourceStream"/> does not support reading.</exception>
|
||||
public bool StorePageAttachment(PageInfo pageInfo, string name, Stream sourceStream, bool overwrite) {
|
||||
if(pageInfo == null) throw new ArgumentNullException("pageInfo");
|
||||
if(name == null) throw new ArgumentNullException("name");
|
||||
if(name.Length == 0) throw new ArgumentException("Name cannot be empty", "name");
|
||||
if(sourceStream == null) throw new ArgumentNullException("sourceStream");
|
||||
if(!sourceStream.CanRead) throw new ArgumentException("Cannot read from Source Stream", "sourceStream");
|
||||
|
||||
string filename = BuildFullPathForAttachments(GetPageAttachmentDirectory(pageInfo) + "/" + name);
|
||||
|
||||
if(!Directory.Exists(Path.GetDirectoryName(filename))) {
|
||||
try {
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(filename));
|
||||
}
|
||||
catch(IOException) {
|
||||
// Cannot create attachments dir
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(File.Exists(filename) && !overwrite) return false;
|
||||
|
||||
FileStream fs = null;
|
||||
|
||||
bool done = false;
|
||||
|
||||
try {
|
||||
fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
|
||||
// StreamCopy content (throws exception in case of error)
|
||||
StreamCopy(sourceStream, fs);
|
||||
|
||||
done = true;
|
||||
}
|
||||
catch(IOException) {
|
||||
return false;
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
fs.Close();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a Page Attachment.
|
||||
/// </summary>
|
||||
/// <param name="pageInfo">The Page Info that owns the Attachment.</param>
|
||||
/// <param name="name">The name of the Attachment, for example "myfile.jpg".</param>
|
||||
/// <param name="destinationStream">A Stream object used as <b>destination</b> of a byte stream,
|
||||
/// i.e. the method writes to the Stream the file content.</param>
|
||||
/// <param name="countHit">A value indicating whether or not to count this retrieval in the statistics.</param>
|
||||
/// <returns><c>true</c> if the Attachment is retrieved, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="pageInfo"/>, <paramref name="name"/> or <paramref name="destinationStream"/> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="name"/> is empty or if <paramref name="destinationStream"/> does not support writing,
|
||||
/// or if the page does not have attachments or if the attachment does not exist.</exception>
|
||||
public bool RetrievePageAttachment(PageInfo pageInfo, string name, Stream destinationStream, bool countHit) {
|
||||
if(pageInfo == null) throw new ArgumentNullException("pageInfo");
|
||||
if(name == null) throw new ArgumentNullException("name");
|
||||
if(name.Length == 0) throw new ArgumentException("Name cannot be empty", "name");
|
||||
if(destinationStream == null) throw new ArgumentNullException("destinationStream");
|
||||
if(!destinationStream.CanWrite) throw new ArgumentException("Cannot write into Destination Stream", "destinationStream");
|
||||
|
||||
string d = GetPageAttachmentDirectory(pageInfo);
|
||||
if(!Directory.Exists(BuildFullPathForAttachments(d))) throw new ArgumentException("No attachments for Page", "pageInfo");
|
||||
|
||||
string filename = BuildFullPathForAttachments(d + "/" + name);
|
||||
if(!File.Exists(filename)) throw new ArgumentException("Attachment does not exist", "name");
|
||||
|
||||
FileStream fs = null;
|
||||
|
||||
bool done = false;
|
||||
|
||||
try {
|
||||
fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
|
||||
// StreamCopy content (throws exception in case of error)
|
||||
StreamCopy(fs, destinationStream);
|
||||
|
||||
done = true;
|
||||
}
|
||||
catch(IOException) {
|
||||
done = false;
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
fs.Close();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
if(countHit) {
|
||||
AddDownloadHit(pageInfo.FullName + "." + name, GetFullPath(AttachmentDownloadsFile));
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of times a page attachment was retrieved.
|
||||
/// </summary>
|
||||
/// <param name="pageInfo">The page.</param>
|
||||
/// <param name="name">The name of the attachment.</param>
|
||||
/// <returns>The number of times the attachment was retrieved.</returns>
|
||||
private int GetPageAttachmentRetrievalCount(PageInfo pageInfo, string name) {
|
||||
if(pageInfo == null) throw new ArgumentNullException("pageInfo");
|
||||
if(name == null) throw new ArgumentNullException("name");
|
||||
if(name.Length == 0) throw new ArgumentException("Name cannot be empty", "name");
|
||||
|
||||
lock(this) {
|
||||
// Format
|
||||
// PageName.File|DownloadCount
|
||||
|
||||
string[] lines = File.ReadAllLines(GetFullPath(AttachmentDownloadsFile));
|
||||
|
||||
string lowercaseFullName = pageInfo.FullName + "." + name;
|
||||
lowercaseFullName = lowercaseFullName.ToLowerInvariant();
|
||||
|
||||
string[] fields;
|
||||
foreach(string line in lines) {
|
||||
fields = line.Split('|');
|
||||
|
||||
if(fields[0].ToLowerInvariant() == lowercaseFullName) {
|
||||
int count;
|
||||
if(int.TryParse(fields[1], out count)) return count;
|
||||
else return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the number of times a page attachment was retrieved.
|
||||
/// </summary>
|
||||
/// <param name="pageInfo">The page.</param>
|
||||
/// <param name="name">The name of the attachment.</param>
|
||||
/// <param name="count">The count to set.</param>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="pageInfo"/> or <paramref name="name"/> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="name"/> is empty.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">If <paramref name="count"/> is less than zero.</exception>
|
||||
public void SetPageAttachmentRetrievalCount(PageInfo pageInfo, string name, int count) {
|
||||
if(pageInfo == null) throw new ArgumentNullException("pageInfo");
|
||||
if(name == null) throw new ArgumentNullException("name");
|
||||
if(name.Length == 0) throw new ArgumentException("Name cannot be empty");
|
||||
if(count < 0) throw new ArgumentOutOfRangeException("Count must be greater than or equal to zero", "count");
|
||||
|
||||
SetDownloadHits(pageInfo.FullName + "." + name, GetFullPath(AttachmentDownloadsFile), count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the details of a page attachment.
|
||||
/// </summary>
|
||||
/// <param name="pageInfo">The page that owns the attachment.</param>
|
||||
/// <param name="name">The name of the attachment, for example "myfile.jpg".</param>
|
||||
/// <returns>The details of the attachment, or <c>null</c> if the attachment does not exist.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="pageInfo"/> or <paramref name="name"/> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="name"/> is empty.</exception>
|
||||
public FileDetails GetPageAttachmentDetails(PageInfo pageInfo, string name) {
|
||||
if(pageInfo == null) throw new ArgumentNullException("pageInfo");
|
||||
if(name == null) throw new ArgumentNullException("name");
|
||||
if(name.Length == 0) throw new ArgumentException("Name cannot be empty");
|
||||
|
||||
string d = GetPageAttachmentDirectory(pageInfo);
|
||||
if(!Directory.Exists(BuildFullPathForAttachments(d))) return null;
|
||||
|
||||
string filename = BuildFullPathForAttachments(d + "/" + name);
|
||||
if(!File.Exists(filename)) return null;
|
||||
|
||||
FileInfo fi = new FileInfo(filename);
|
||||
|
||||
return new FileDetails(fi.Length, fi.LastWriteTime, GetPageAttachmentRetrievalCount(pageInfo, name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a Page Attachment.
|
||||
/// </summary>
|
||||
/// <param name="pageInfo">The Page Info that owns the Attachment.</param>
|
||||
/// <param name="name">The name of the Attachment, for example "myfile.jpg".</param>
|
||||
/// <returns><c>true</c> if the Attachment is deleted, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="pageInfo"/> or <paramref name="name"/> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="name"/> is empty or if the page or attachment do not exist.</exception>
|
||||
public bool DeletePageAttachment(PageInfo pageInfo, string name) {
|
||||
if(pageInfo == null) throw new ArgumentNullException("pageInfo");
|
||||
if(name == null) throw new ArgumentNullException("name");
|
||||
if(name.Length == 0) throw new ArgumentException("Name cannot be empty");
|
||||
|
||||
string d = GetPageAttachmentDirectory(pageInfo);
|
||||
if(!Directory.Exists(BuildFullPathForAttachments(d))) throw new ArgumentException("Page does not exist", "pageInfo");
|
||||
|
||||
string filename = BuildFullPathForAttachments(d + "/" + name);
|
||||
if(!File.Exists(filename)) throw new ArgumentException("Attachment does not exist", "name");
|
||||
|
||||
try {
|
||||
File.Delete(filename);
|
||||
SetDownloadHits(pageInfo.FullName + "." + name, GetFullPath(AttachmentDownloadsFile), 0);
|
||||
return true;
|
||||
}
|
||||
catch(IOException) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renames a Page Attachment.
|
||||
/// </summary>
|
||||
/// <param name="pageInfo">The Page Info that owns the Attachment.</param>
|
||||
/// <param name="oldName">The old name of the Attachment.</param>
|
||||
/// <param name="newName">The new name of the Attachment.</param>
|
||||
/// <returns><c>true</c> if the Attachment is renamed, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="pageInfo"/>, <paramref name="oldName"/> or <paramref name="newName"/> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <paramref name="pageInfo"/>, <paramref name="oldName"/> or <paramref name="newName"/> are empty,
|
||||
/// or if the page or old attachment do not exist, or the new attachment name already exists.</exception>
|
||||
public bool RenamePageAttachment(PageInfo pageInfo, string oldName, string newName) {
|
||||
if(pageInfo == null) throw new ArgumentNullException("pageInfo");
|
||||
if(oldName == null) throw new ArgumentNullException("oldName");
|
||||
if(oldName.Length == 0) throw new ArgumentException("Old Name cannot be empty", "oldName");
|
||||
if(newName == null) throw new ArgumentNullException("newName");
|
||||
if(newName.Length == 0) throw new ArgumentException("New Name cannot be empty", "newName");
|
||||
|
||||
string d = GetPageAttachmentDirectory(pageInfo);
|
||||
if(!Directory.Exists(BuildFullPathForAttachments(d))) throw new ArgumentException("Page does not exist", "pageInfo");
|
||||
|
||||
string oldFilename = BuildFullPathForAttachments(d + "/" + oldName);
|
||||
if(!File.Exists(oldFilename)) throw new ArgumentException("Attachment does not exist", "oldName");
|
||||
|
||||
string newFilename = BuildFullPathForAttachments(d + "/" + newName);
|
||||
if(File.Exists(newFilename)) throw new ArgumentException("Attachment already exists", "newName");
|
||||
|
||||
try {
|
||||
File.Move(oldFilename, newFilename);
|
||||
RenameDownloadHitsItem(pageInfo.FullName + "." + oldName, pageInfo.FullName + "." + newName,
|
||||
GetFullPath(AttachmentDownloadsFile));
|
||||
return true;
|
||||
}
|
||||
catch(IOException) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies to the Provider that a Page has been renamed.
|
||||
/// </summary>
|
||||
/// <param name="oldPage">The old Page Info object.</param>
|
||||
/// <param name="newPage">The new Page Info object.</param>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="oldPage"/> or <paramref name="newPage"/> are <c>null</c></exception>
|
||||
/// <exception cref="ArgumentException">If the new page is already in use.</exception>
|
||||
public void NotifyPageRenaming(PageInfo oldPage, PageInfo newPage) {
|
||||
if(oldPage == null) throw new ArgumentNullException("oldPage");
|
||||
if(newPage == null) throw new ArgumentNullException("newPage");
|
||||
|
||||
string oldName = GetPageAttachmentDirectory(oldPage);
|
||||
string newName = GetPageAttachmentDirectory(newPage);
|
||||
|
||||
string oldDir = BuildFullPathForAttachments(oldName);
|
||||
string newDir = BuildFullPathForAttachments(newName);
|
||||
|
||||
if(!Directory.Exists(oldDir)) return; // Nothing to do
|
||||
if(Directory.Exists(newDir)) throw new ArgumentException("New Page already exists", "newPage");
|
||||
|
||||
try {
|
||||
Directory.Move(oldDir, newDir);
|
||||
RenameDownloadHitsItemPartialMatch(oldPage.FullName + ".", newPage.FullName + ".",
|
||||
GetFullPath(AttachmentDownloadsFile));
|
||||
}
|
||||
catch(IOException) { }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
2611
Core/Formatter.cs
Normal file
2611
Core/Formatter.cs
Normal file
File diff suppressed because it is too large
Load diff
155
Core/FormattingPipeline.cs
Normal file
155
Core/FormattingPipeline.cs
Normal file
|
@ -0,0 +1,155 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
using System.Web;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Contains methods for formatting content using a pipeline paradigm.
|
||||
/// </summary>
|
||||
public static class FormattingPipeline {
|
||||
|
||||
/// <summary>
|
||||
/// Gets the formatter providers list sorted by priority.
|
||||
/// </summary>
|
||||
/// <returns>The list.</returns>
|
||||
private static IList<IFormatterProviderV30> GetSortedFormatters() {
|
||||
List<IFormatterProviderV30> providers = new List<IFormatterProviderV30>(Collectors.FormatterProviderCollector.AllProviders);
|
||||
|
||||
// Sort by priority, then by name
|
||||
providers.Sort((x, y) => {
|
||||
int preliminaryResult = x.ExecutionPriority.CompareTo(y.ExecutionPriority);
|
||||
if(preliminaryResult != 0) return preliminaryResult;
|
||||
else return x.Information.Name.CompareTo(y.Information.Name);
|
||||
});
|
||||
|
||||
return providers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the Phases 1 and 2 of the formatting process.
|
||||
/// </summary>
|
||||
/// <param name="raw">The raw WikiMarkup to format.</param>
|
||||
/// <param name="forIndexing">A value indicating whether the formatting is being done for content indexing.</param>
|
||||
/// <param name="context">The formatting context.</param>
|
||||
/// <param name="current">The current Page, if any.</param>
|
||||
/// <returns>The formatted content.</returns>
|
||||
public static string FormatWithPhase1And2(string raw, bool forIndexing, FormattingContext context, PageInfo current) {
|
||||
string[] tempLinks;
|
||||
return FormatWithPhase1And2(raw, forIndexing, context, current, out tempLinks);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the Phases 1 and 2 of the formatting process.
|
||||
/// </summary>
|
||||
/// <param name="raw">The raw WikiMarkup to format.</param>
|
||||
/// <param name="forIndexing">A value indicating whether the formatting is being done for content indexing.</param>
|
||||
/// <param name="context">The formatting context.</param>
|
||||
/// <param name="current">The current Page, if any.</param>
|
||||
/// <param name="linkedPages">The Pages linked by the current Page.</param>
|
||||
/// <returns>The formatted content.</returns>
|
||||
public static string FormatWithPhase1And2(string raw, bool forIndexing, FormattingContext context, PageInfo current, out string[] linkedPages) {
|
||||
ContextInformation info = null;
|
||||
string username = SessionFacade.CurrentUsername;
|
||||
info = new ContextInformation(forIndexing, false, context, current, System.Threading.Thread.CurrentThread.CurrentCulture.Name, HttpContext.Current,
|
||||
username, SessionFacade.GetCurrentGroupNames());
|
||||
|
||||
IList<IFormatterProviderV30> providers = GetSortedFormatters();
|
||||
|
||||
// Phase 1
|
||||
foreach(IFormatterProviderV30 provider in providers) {
|
||||
if(provider.PerformPhase1) {
|
||||
try {
|
||||
raw = provider.Format(raw, info, FormattingPhase.Phase1);
|
||||
}
|
||||
catch(Exception ex) {
|
||||
Log.LogEntry("Provider " + provider.Information.Name + " failed to perform Phase1 (silently resuming from next provider): " + ex.ToString(), EntryType.Error, Log.SystemUsername);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
raw = Formatter.Format(raw, forIndexing, context, current, out linkedPages);
|
||||
|
||||
// Phase 2
|
||||
foreach(IFormatterProviderV30 provider in providers) {
|
||||
if(provider.PerformPhase2) {
|
||||
try {
|
||||
raw = provider.Format(raw, info, FormattingPhase.Phase2);
|
||||
}
|
||||
catch(Exception ex) {
|
||||
Log.LogEntry("Provider " + provider.Information.Name + " failed to perform Phase2 (silently resuming from next provider): " + ex.ToString(), EntryType.Error, Log.SystemUsername);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return raw;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the Phase 3 of the formatting process.
|
||||
/// </summary>
|
||||
/// <param name="raw">The raw WikiMarkup to format.</param>
|
||||
/// <param name="context">The formatting context.</param>
|
||||
/// <param name="current">The current Page, if any.</param>
|
||||
/// <returns>The formatted content.</returns>
|
||||
public static string FormatWithPhase3(string raw, FormattingContext context, PageInfo current) {
|
||||
raw = Formatter.FormatPhase3(raw, context, current);
|
||||
|
||||
ContextInformation info = null;
|
||||
string username = SessionFacade.CurrentUsername;
|
||||
info = new ContextInformation(false, false, context, current, System.Threading.Thread.CurrentThread.CurrentCulture.Name, HttpContext.Current,
|
||||
username, SessionFacade.GetCurrentGroupNames());
|
||||
|
||||
// Phase 3
|
||||
foreach(IFormatterProviderV30 provider in GetSortedFormatters()) {
|
||||
if(provider.PerformPhase3) {
|
||||
try {
|
||||
raw = provider.Format(raw, info, FormattingPhase.Phase3);
|
||||
}
|
||||
catch(Exception ex) {
|
||||
Log.LogEntry("Provider " + provider.Information.Name + " failed to perform Phase3 (silently resuming from next provider): " + ex.ToString(), EntryType.Error, Log.SystemUsername);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return raw;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepares the title of an item for display.
|
||||
/// </summary>
|
||||
/// <param name="title">The input title.</param>
|
||||
/// <param name="forIndexing">A value indicating whether the formatting is being done for content indexing.</param>
|
||||
/// <param name="context">The context information.</param>
|
||||
/// <param name="current">The current page, if any.</param>
|
||||
/// <returns>The prepared title, properly sanitized.</returns>
|
||||
public static string PrepareTitle(string title, bool forIndexing, FormattingContext context, PageInfo current) {
|
||||
string temp = title;
|
||||
ContextInformation info = new ContextInformation(forIndexing, false, context, current, System.Threading.Thread.CurrentThread.CurrentCulture.Name,
|
||||
HttpContext.Current, SessionFacade.GetCurrentUsername(), SessionFacade.GetCurrentGroupNames());
|
||||
|
||||
foreach(IFormatterProviderV30 prov in GetSortedFormatters()) {
|
||||
temp = prov.PrepareTitle(temp, info);
|
||||
}
|
||||
|
||||
return PrepareItemTitle(temp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepares the title of an item for safe display.
|
||||
/// </summary>
|
||||
/// <param name="title">The title.</param>
|
||||
/// <returns>The sanitized title.</returns>
|
||||
private static string PrepareItemTitle(string title) {
|
||||
return Formatter.StripHtml(title)
|
||||
.Replace("\"", """)
|
||||
.Replace("'", "'")
|
||||
.Replace("<", "<").Replace(">", ">")
|
||||
.Replace("[", "[").Replace("]", "]"); // This avoid endless loops in Formatter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
55
Core/Hash.cs
Normal file
55
Core/Hash.cs
Normal file
|
@ -0,0 +1,55 @@
|
|||
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using System.Web.Security;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Helps computing Hash codes.
|
||||
/// </summary>
|
||||
public static class Hash {
|
||||
|
||||
/// <summary>
|
||||
/// Computes the Hash code of a string.
|
||||
/// </summary>
|
||||
/// <param name="input">The string.</param>
|
||||
/// <returns>The Hash code.</returns>
|
||||
public static byte[] ComputeBytes(string input) {
|
||||
MD5 md5 = MD5CryptoServiceProvider.Create();
|
||||
return md5.ComputeHash(Encoding.ASCII.GetBytes(input));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the Hash code of a string and converts it into a Hex string.
|
||||
/// </summary>
|
||||
/// <param name="input">The string.</param>
|
||||
/// <returns>The Hash code, converted into a Hex string.</returns>
|
||||
public static string Compute(string input) {
|
||||
byte[] bytes = ComputeBytes(input);
|
||||
string result = "";
|
||||
for(int i = 0; i < bytes.Length; i++) {
|
||||
result += string.Format("{0:X2}", bytes[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <param name="otherData">The other data to mix into the input string.</param>
|
||||
/// <returns>The secured Hash of the Username.</returns>
|
||||
public static string ComputeSecurityHash(string username, string email, DateTime dateTime, string otherData) {
|
||||
// Use a salt (is this actually useful given that STW is opensource and everyone can see the salt?)
|
||||
return Compute(otherData + username + email + dateTime.ToString("yyyyMMddHHmmss") +"$kbfl?nfc4");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
1005
Core/Host.cs
Normal file
1005
Core/Host.cs
Normal file
File diff suppressed because it is too large
Load diff
20
Core/ITranslator.cs
Normal file
20
Core/ITranslator.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
|
||||
using System;
|
||||
|
||||
namespace ScrewTurn.Wiki.ImportWiki {
|
||||
|
||||
/// <summary>
|
||||
/// Exposes an interface for building import tools.
|
||||
/// </summary>
|
||||
public interface ITranslator {
|
||||
|
||||
/// <summary>
|
||||
/// Executes the translation.
|
||||
/// </summary>
|
||||
/// <param name="input">The input content.</param>
|
||||
/// <returns>The WikiMarkup.</returns>
|
||||
string Translate(string input);
|
||||
|
||||
}
|
||||
|
||||
}
|
491
Core/IndexStorer.cs
Normal file
491
Core/IndexStorer.cs
Normal file
|
@ -0,0 +1,491 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using ScrewTurn.Wiki.SearchEngine;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Stores index data to disk.
|
||||
/// </summary>
|
||||
/// <remarks>Instance and static members are <b>thread-safe</b>.</remarks>
|
||||
public class IndexStorer : IndexStorerBase {
|
||||
|
||||
private static readonly byte[] ReservedBytes = new byte[] { 2, 0, 0, 0, 0, 0, 0, 0 };
|
||||
private static readonly int Zero = 0;
|
||||
|
||||
private string documentsFile, wordsFile, mappingsFile;
|
||||
|
||||
private uint firstFreeDocumentId = 1;
|
||||
private uint firstFreeWordId = 1;
|
||||
|
||||
// Documents file binary format
|
||||
// Reserved(8bytes) Count(int) Entries...
|
||||
// ID(int) Name(string) Title(string) TypeTag(string) DateTime(long)
|
||||
|
||||
// Words file binary format
|
||||
// Reserved(8bytes) Count(int) Entries...
|
||||
// ID(int) Text(string)
|
||||
|
||||
// Mappings file binary format
|
||||
// Reserved(8bytes) Count(int) Entries...
|
||||
// WordID(int) DocumentID(int) FirstCharIndex(int) WordIndex(int) Location(int)
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IndexStorer" /> class.
|
||||
/// </summary>
|
||||
/// <param name="documentsFile">The file that contains the documents list.</param>
|
||||
/// <param name="wordsFile">The file that contains the words list.</param>
|
||||
/// <param name="mappingsFile">The file that contains the index mappings data.</param>
|
||||
/// <param name="index">The index to manage.</param>
|
||||
public IndexStorer(string documentsFile, string wordsFile, string mappingsFile, IInMemoryIndex index)
|
||||
: base(index) {
|
||||
|
||||
if(documentsFile == null) throw new ArgumentNullException("documentsFile");
|
||||
if(wordsFile == null) throw new ArgumentNullException("wordsFile");
|
||||
if(mappingsFile == null) throw new ArgumentNullException("mappingsFile");
|
||||
|
||||
if(documentsFile.Length == 0) throw new ArgumentException("Documents File cannot be empty", "documentsFile");
|
||||
if(wordsFile.Length == 0) throw new ArgumentException("Words File cannot be emtpy", "wordsFile");
|
||||
if(mappingsFile.Length == 0) throw new ArgumentException("Mappings File cannot be empty", "mappingsFile");
|
||||
|
||||
this.documentsFile = documentsFile;
|
||||
this.wordsFile = wordsFile;
|
||||
this.mappingsFile = mappingsFile;
|
||||
|
||||
InitFiles();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the approximate size, in bytes, of the search engine index.
|
||||
/// </summary>
|
||||
public override long Size {
|
||||
get {
|
||||
lock(this) {
|
||||
long size = 0;
|
||||
FileInfo fi;
|
||||
|
||||
fi = new FileInfo(documentsFile);
|
||||
size += fi.Length;
|
||||
|
||||
fi = new FileInfo(wordsFile);
|
||||
size += fi.Length;
|
||||
|
||||
fi = new FileInfo(mappingsFile);
|
||||
size += fi.Length;
|
||||
|
||||
return size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the index from the data store the first time.
|
||||
/// </summary>
|
||||
/// <param name="documents">The dumped documents.</param>
|
||||
/// <param name="words">The dumped words.</param>
|
||||
/// <param name="mappings">The dumped word mappings.</param>
|
||||
protected override void LoadIndexInternal(out DumpedDocument[] documents, out DumpedWord[] words, out DumpedWordMapping[] mappings) {
|
||||
uint maxDocumentId = 0;
|
||||
uint maxWordId = 0;
|
||||
|
||||
// 1. Load Documents
|
||||
using(FileStream fs = new FileStream(documentsFile, FileMode.Open, FileAccess.Read, FileShare.None)) {
|
||||
int count = ReadCount(fs);
|
||||
BinaryReader reader = new BinaryReader(fs, Encoding.UTF8);
|
||||
documents = new DumpedDocument[count];
|
||||
for(int i = 0; i < count; i++) {
|
||||
documents[i] = ReadDumpedDocument(reader);
|
||||
if(documents[i].ID > maxDocumentId) maxDocumentId = documents[i].ID;
|
||||
}
|
||||
firstFreeDocumentId = maxDocumentId + 1;
|
||||
}
|
||||
|
||||
// 2. Load Words
|
||||
using(FileStream fs = new FileStream(wordsFile, FileMode.Open, FileAccess.Read, FileShare.None)) {
|
||||
int count = ReadCount(fs);
|
||||
BinaryReader reader = new BinaryReader(fs, Encoding.UTF8);
|
||||
words = new DumpedWord[count];
|
||||
for(int i = 0; i < count; i++) {
|
||||
words[i] = ReadDumpedWord(reader);
|
||||
if(words[i].ID > maxWordId) maxWordId = words[i].ID;
|
||||
}
|
||||
firstFreeWordId = maxWordId + 1;
|
||||
}
|
||||
|
||||
// 3. Load Mappings
|
||||
using(FileStream fs = new FileStream(mappingsFile, FileMode.Open, FileAccess.Read, FileShare.None)) {
|
||||
int count = ReadCount(fs);
|
||||
BinaryReader reader = new BinaryReader(fs, Encoding.UTF8);
|
||||
mappings = new DumpedWordMapping[count];
|
||||
for(int i = 0; i < count; i++) {
|
||||
mappings[i] = ReadDumpedWordMapping(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the reserved bytes.
|
||||
/// </summary>
|
||||
/// <param name="reader">The <see cref="BinaryReader" /> to read from.</param>
|
||||
/// <returns><c>true</c> if read bytes are equal to expected bytes, <c>false</c> otherwise.</returns>
|
||||
private static bool ReadReserved(BinaryReader reader) {
|
||||
bool allEqual = true;
|
||||
for(int i = 0; i < ReservedBytes.Length; i++) {
|
||||
int r = reader.ReadByte();
|
||||
if(r != ReservedBytes[i]) allEqual = false;
|
||||
}
|
||||
return allEqual;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the data files, if needed.
|
||||
/// </summary>
|
||||
private void InitFiles() {
|
||||
if(!File.Exists(documentsFile)) {
|
||||
using(FileStream fs = new FileStream(documentsFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
|
||||
BinaryWriter writer = new BinaryWriter(fs, Encoding.UTF8);
|
||||
WriteHeader(writer);
|
||||
}
|
||||
}
|
||||
|
||||
if(!File.Exists(wordsFile)) {
|
||||
using(FileStream fs = new FileStream(wordsFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
|
||||
BinaryWriter writer = new BinaryWriter(fs, Encoding.UTF8);
|
||||
WriteHeader(writer);
|
||||
}
|
||||
}
|
||||
|
||||
if(!File.Exists(mappingsFile)) {
|
||||
using(FileStream fs = new FileStream(mappingsFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
|
||||
BinaryWriter writer = new BinaryWriter(fs, Encoding.UTF8);
|
||||
WriteHeader(writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the data storage.
|
||||
/// </summary>
|
||||
/// <param name="state">A state object passed from the index.</param>
|
||||
protected override void InitDataStore(object state) {
|
||||
using(FileStream fs = new FileStream(documentsFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
|
||||
BinaryWriter writer = new BinaryWriter(fs, Encoding.UTF8);
|
||||
WriteHeader(writer);
|
||||
}
|
||||
|
||||
using(FileStream fs = new FileStream(wordsFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
|
||||
BinaryWriter writer = new BinaryWriter(fs, Encoding.UTF8);
|
||||
WriteHeader(writer);
|
||||
}
|
||||
|
||||
using(FileStream fs = new FileStream(mappingsFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
|
||||
BinaryWriter writer = new BinaryWriter(fs, Encoding.UTF8);
|
||||
WriteHeader(writer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the binary file header.
|
||||
/// </summary>
|
||||
/// <param name="writer">The <see cref="BinaryWriter" /> to write into.</param>
|
||||
private static void WriteHeader(BinaryWriter writer) {
|
||||
writer.Write(ReservedBytes);
|
||||
writer.Write(Zero);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a <see cref="DumpedDocument" /> from a <see cref="BinaryReader" />.
|
||||
/// </summary>
|
||||
/// <param name="reader">The <see cref="BinaryReader" />.</param>
|
||||
/// <returns>The <see cref="DumpedDocument" />.</returns>
|
||||
private static DumpedDocument ReadDumpedDocument(BinaryReader reader) {
|
||||
uint id;
|
||||
string name, title, typeTag;
|
||||
DateTime dateTime;
|
||||
|
||||
id = reader.ReadUInt32();
|
||||
name = reader.ReadString();
|
||||
title = reader.ReadString();
|
||||
typeTag = reader.ReadString();
|
||||
dateTime = DateTime.FromBinary(reader.ReadInt64());
|
||||
|
||||
return new DumpedDocument(id, name, title, typeTag, dateTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a <see cref="DumpedWord" /> from a <see cref="BinaryReader" />.
|
||||
/// </summary>
|
||||
/// <param name="reader">The <see cref="BinaryReader" />.</param>
|
||||
/// <returns>The <see cref="DumpedWord" />.</returns>
|
||||
private static DumpedWord ReadDumpedWord(BinaryReader reader) {
|
||||
uint id;
|
||||
string text;
|
||||
|
||||
id = reader.ReadUInt32();
|
||||
text = reader.ReadString();
|
||||
|
||||
return new DumpedWord(id, text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a <see cref="DumpedWordMapping" /> from a <see cref="BinaryReader" />.
|
||||
/// </summary>
|
||||
/// <param name="reader">The <see cref="BinaryReader" />.</param>
|
||||
/// <returns>The <see cref="DumpedWordMapping" />.</returns>
|
||||
private static DumpedWordMapping ReadDumpedWordMapping(BinaryReader reader) {
|
||||
uint wordId;
|
||||
uint documentId;
|
||||
ushort firstCharIndex, wordIndex;
|
||||
byte location;
|
||||
|
||||
wordId = reader.ReadUInt32();
|
||||
documentId = reader.ReadUInt32();
|
||||
firstCharIndex = reader.ReadUInt16();
|
||||
wordIndex = reader.ReadUInt16();
|
||||
location = reader.ReadByte();
|
||||
|
||||
return new DumpedWordMapping(wordId, documentId, firstCharIndex, wordIndex, location);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the count in a <see cref="FileStream" />.
|
||||
/// </summary>
|
||||
/// <param name="fs">The <see cref="FileStream" />, at position <b>zero</b>.</param>
|
||||
/// <returns>The count.</returns>
|
||||
/// <remarks>The caller must properly seek the stream after calling the method.</remarks>
|
||||
private static int ReadCount(FileStream fs) {
|
||||
BinaryReader reader = new BinaryReader(fs, Encoding.UTF8);
|
||||
if(!ReadReserved(reader)) {
|
||||
throw new InvalidOperationException("Invalid index file header");
|
||||
}
|
||||
return reader.ReadInt32();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores new data into the data storage.
|
||||
/// </summary>
|
||||
/// <param name="data">The data to store.</param>
|
||||
/// <param name="state">A state object passed from the index.</param>
|
||||
/// <returns>The storer result, if any.</returns>
|
||||
/// <remarks>When saving a new document, the document ID in data.Mappings must be
|
||||
/// replaced with the currect document ID, generated by the concrete implementation of
|
||||
/// this method. data.Words should have IDs numbered from uint.MaxValue downwards.
|
||||
/// The method re-numbers the words appropriately.</remarks>
|
||||
protected override IndexStorerResult SaveData(DumpedChange data, object state) {
|
||||
IndexStorerResult result = new IndexStorerResult(null, null);
|
||||
|
||||
// 1. Save Document
|
||||
using(FileStream fs = new FileStream(documentsFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) {
|
||||
int count = ReadCount(fs);
|
||||
// Update count and append document
|
||||
BinaryWriter writer = new BinaryWriter(fs, Encoding.UTF8);
|
||||
fs.Seek(-4, SeekOrigin.Current);
|
||||
writer.Write(count + 1);
|
||||
writer.Seek(0, SeekOrigin.End);
|
||||
data.Document.ID = firstFreeDocumentId;
|
||||
WriteDumpedDocument(writer, data.Document);
|
||||
|
||||
result.DocumentID = firstFreeDocumentId;
|
||||
firstFreeDocumentId++;
|
||||
}
|
||||
|
||||
// 2. Save Words
|
||||
Dictionary<uint, WordId> wordIds = null;
|
||||
using(FileStream fs = new FileStream(wordsFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) {
|
||||
int count = ReadCount(fs);
|
||||
// Update count and append words
|
||||
BinaryWriter writer = new BinaryWriter(fs, Encoding.UTF8);
|
||||
fs.Seek(-4, SeekOrigin.Current);
|
||||
writer.Write(count + data.Words.Count);
|
||||
fs.Seek(0, SeekOrigin.End);
|
||||
|
||||
wordIds = new Dictionary<uint, WordId>(data.Words.Count);
|
||||
foreach(DumpedWord dw in data.Words) {
|
||||
wordIds.Add(dw.ID, new WordId(dw.Text, firstFreeWordId));
|
||||
dw.ID = firstFreeWordId;
|
||||
WriteDumpedWord(writer, dw);
|
||||
firstFreeWordId++;
|
||||
}
|
||||
result.WordIDs = new List<WordId>(wordIds.Values);
|
||||
}
|
||||
|
||||
// 3. Save Mappings
|
||||
using(FileStream fs = new FileStream(mappingsFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) {
|
||||
int count = ReadCount(fs);
|
||||
// Update count and append mappings
|
||||
BinaryWriter writer = new BinaryWriter(fs, Encoding.UTF8);
|
||||
fs.Seek(-4, SeekOrigin.Current);
|
||||
writer.Write(count + data.Mappings.Count);
|
||||
fs.Seek(0, SeekOrigin.End);
|
||||
foreach(DumpedWordMapping map in data.Mappings) {
|
||||
// Words are autonumbered from uint.MaxValue downwards by IndexBase so that
|
||||
// IndexStorer can identify the DumpedWordMappings easily and
|
||||
// fix the IDs with the ones actually stored
|
||||
WordId newMappingWordId;
|
||||
if(wordIds != null && wordIds.TryGetValue(map.WordID, out newMappingWordId)) {
|
||||
map.WordID = newMappingWordId.ID;
|
||||
}
|
||||
WriteDumpedWordMapping(writer,
|
||||
new DumpedWordMapping(map.WordID, result.DocumentID.Value,
|
||||
map.FirstCharIndex, map.WordIndex, map.Location));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a tempDumpedWord file name given an original name.
|
||||
/// </summary>
|
||||
/// <param name="file">The original name.</param>
|
||||
/// <returns>The tempDumpedWord file name.</returns>
|
||||
private static string GetTempFile(string file) {
|
||||
string folder = Path.GetDirectoryName(file);
|
||||
string name = Path.GetFileNameWithoutExtension(file) + "_Temp" + Path.GetExtension(file);
|
||||
return Path.Combine(folder, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes data from the data storage.
|
||||
/// </summary>
|
||||
/// <param name="data">The data to delete.</param>
|
||||
/// <param name="state">A state object passed from the index.</param>
|
||||
protected override void DeleteData(DumpedChange data, object state) {
|
||||
// Files are regenerated in a tempDumpedWord location and copied back
|
||||
string tempDocumentsFile = GetTempFile(documentsFile);
|
||||
string tempWordsFile = GetTempFile(wordsFile);
|
||||
string tempMappingsFile = GetTempFile(mappingsFile);
|
||||
|
||||
// 1. Remove Mappings
|
||||
using(FileStream fsi = new FileStream(mappingsFile, FileMode.Open, FileAccess.Read, FileShare.None)) {
|
||||
int count = ReadCount(fsi);
|
||||
int countLocation = (int)fsi.Position - 4;
|
||||
int writeCount = 0;
|
||||
BinaryReader reader = new BinaryReader(fsi, Encoding.UTF8);
|
||||
using(FileStream fso = new FileStream(tempMappingsFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
|
||||
BinaryWriter writer = new BinaryWriter(fso, Encoding.UTF8);
|
||||
WriteHeader(writer);
|
||||
DumpedWordMapping m;
|
||||
for(int i = 0; i < count; i++) {
|
||||
m = ReadDumpedWordMapping(reader);
|
||||
// If m is not contained in data.Mappings, store it in tempDumpedWord file
|
||||
if(!Find(m, data.Mappings)) {
|
||||
WriteDumpedWordMapping(writer, m);
|
||||
writeCount++;
|
||||
}
|
||||
}
|
||||
writer.Seek(countLocation, SeekOrigin.Begin);
|
||||
writer.Write(writeCount);
|
||||
}
|
||||
}
|
||||
// Replace the file
|
||||
File.Copy(tempMappingsFile, mappingsFile, true);
|
||||
File.Delete(tempMappingsFile);
|
||||
|
||||
// 2. Remove Words
|
||||
using(FileStream fsi = new FileStream(wordsFile, FileMode.Open, FileAccess.Read, FileShare.None)) {
|
||||
int count = ReadCount(fsi);
|
||||
int countLocation = (int)fsi.Position - 4;
|
||||
int writeCount = 0;
|
||||
BinaryReader reader = new BinaryReader(fsi, Encoding.UTF8);
|
||||
using(FileStream fso = new FileStream(tempWordsFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
|
||||
BinaryWriter writer = new BinaryWriter(fso, Encoding.UTF8);
|
||||
WriteHeader(writer);
|
||||
DumpedWord w;
|
||||
for(int i = 0; i < count; i++) {
|
||||
w = ReadDumpedWord(reader);
|
||||
// If w is not contained in data.Words, store it in tempDumpedWord file
|
||||
if(!Find(w, data.Words)) {
|
||||
WriteDumpedWord(writer, w);
|
||||
writeCount++;
|
||||
}
|
||||
}
|
||||
writer.Seek(countLocation, SeekOrigin.Begin);
|
||||
writer.Write(writeCount);
|
||||
}
|
||||
}
|
||||
// Replace the file
|
||||
File.Copy(tempWordsFile, wordsFile, true);
|
||||
File.Delete(tempWordsFile);
|
||||
|
||||
// 3. Remove Document
|
||||
using(FileStream fsi = new FileStream(documentsFile, FileMode.Open, FileAccess.Read, FileShare.None)) {
|
||||
int count = ReadCount(fsi);
|
||||
int countLocation = (int)fsi.Position - 4;
|
||||
BinaryReader reader = new BinaryReader(fsi, Encoding.UTF8);
|
||||
using(FileStream fso = new FileStream(tempDocumentsFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
|
||||
BinaryWriter writer = new BinaryWriter(fso, Encoding.UTF8);
|
||||
WriteHeader(writer);
|
||||
DumpedDocument d;
|
||||
for(int i = 0; i < count; i++) {
|
||||
d = ReadDumpedDocument(reader);
|
||||
// If d is not equal to data.Document (to be deleted), then copy it to the result file
|
||||
if(!EqualDumpedDocument(d, data.Document)) {
|
||||
WriteDumpedDocument(writer, d);
|
||||
}
|
||||
}
|
||||
writer.Seek(countLocation, SeekOrigin.Begin);
|
||||
writer.Write(count - 1);
|
||||
}
|
||||
}
|
||||
File.Copy(tempDocumentsFile, documentsFile, true);
|
||||
File.Delete(tempDocumentsFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a <see cref="DumpedDocument" /> to a <see cref="BinaryWriter" />.
|
||||
/// </summary>
|
||||
/// <param name="writer">The <see cref="BinaryWriter" />.</param>
|
||||
/// <param name="document">The <see cref="DumpedDocument" />.</param>
|
||||
private static void WriteDumpedDocument(BinaryWriter writer, DumpedDocument document) {
|
||||
writer.Write(document.ID);
|
||||
writer.Write(document.Name);
|
||||
writer.Write(document.Title);
|
||||
writer.Write(document.TypeTag);
|
||||
writer.Write(document.DateTime.ToBinary());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a <see cref="DumpedWord" /> to a <see cref="BinaryWriter" />.
|
||||
/// </summary>
|
||||
/// <param name="writer">The <see cref="BinaryWriter" />.</param>
|
||||
/// <param name="word">The <see cref="DumpedWord" />.</param>
|
||||
private static void WriteDumpedWord(BinaryWriter writer, DumpedWord word) {
|
||||
//if(word.Text.Length == 0) throw new InvalidOperationException();
|
||||
|
||||
writer.Write(word.ID);
|
||||
writer.Write(word.Text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a <see cref="DumpedWordMapping" /> to a <see cref="BinaryWriter" />.
|
||||
/// </summary>
|
||||
/// <param name="writer">The <see cref="BinaryWriter" />.</param>
|
||||
/// <param name="mapping">The <see cref="DumpedWordMapping" />.</param>
|
||||
private static void WriteDumpedWordMapping(BinaryWriter writer, DumpedWordMapping mapping) {
|
||||
writer.Write(mapping.WordID);
|
||||
writer.Write(mapping.DocumentID);
|
||||
writer.Write(mapping.FirstCharIndex);
|
||||
writer.Write(mapping.WordIndex);
|
||||
writer.Write(mapping.Location);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether two <see cref="DumpedDocument" />s are equal.
|
||||
/// </summary>
|
||||
/// <param name="d1">The first document.</param>
|
||||
/// <param name="d2">The second document.</param>
|
||||
/// <returns><c>true</c> if the documents are equal, <c>false</c> otherwise.</returns>
|
||||
private static bool EqualDumpedDocument(DumpedDocument d1, DumpedDocument d2) {
|
||||
// Only consider ID, Name and TypeTag
|
||||
//return d1.ID == d2.ID && d1.Name == d2.Name && d1.Title == d2.Title &&
|
||||
// d1.TypeTag == d2.TypeTag && d1.DateTime == d2.DateTime;
|
||||
return d1.ID == d2.ID && d1.Name == d2.Name && d1.TypeTag == d2.TypeTag;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
39
Core/LocalPageInfo.cs
Normal file
39
Core/LocalPageInfo.cs
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Local Page.
|
||||
/// </summary>
|
||||
public class LocalPageInfo : PageInfo {
|
||||
|
||||
private string file;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <b>PageInfo</b> class.
|
||||
/// </summary>
|
||||
/// <param name="fullName">The Full Name of the Page.</param>
|
||||
/// <param name="provider">The Pages Storage Provider that manages this Page.</param>
|
||||
/// <param name="creationDateTime">The creation Date/Time.</param>
|
||||
/// <param name="file">The relative path of the file used for data storage.</param>
|
||||
public LocalPageInfo(string fullName, IPagesStorageProviderV30 provider, DateTime creationDateTime, string file)
|
||||
: base(fullName, provider, creationDateTime) {
|
||||
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the relative path of the File used for data storage.
|
||||
/// </summary>
|
||||
public string File {
|
||||
get { return file; }
|
||||
set { file = value; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
48
Core/LocalProvidersTools.cs
Normal file
48
Core/LocalProvidersTools.cs
Normal file
|
@ -0,0 +1,48 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using System.Web.Configuration;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Implements tools for local providers.
|
||||
/// </summary>
|
||||
public static class LocalProvidersTools {
|
||||
|
||||
/// <summary>
|
||||
/// Checks a directory for write permissions.
|
||||
/// </summary>
|
||||
/// <param name="dir">The directory.</param>
|
||||
/// <returns><c>true</c> if the directory has write permissions, <c>false</c> otherwise.</returns>
|
||||
public static bool CheckWritePermissions(string dir) {
|
||||
string file = System.IO.Path.Combine(dir, "__StwTestFile.txt");
|
||||
|
||||
bool canWrite = true;
|
||||
|
||||
System.IO.FileStream fs = null;
|
||||
try {
|
||||
fs = System.IO.File.Create(file);
|
||||
fs.Write(Encoding.ASCII.GetBytes("Hello"), 0, 5);
|
||||
}
|
||||
catch {
|
||||
canWrite = false;
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
if(fs != null) fs.Close();
|
||||
System.IO.File.Delete(file);
|
||||
}
|
||||
catch {
|
||||
canWrite = false;
|
||||
}
|
||||
}
|
||||
|
||||
return canWrite;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
42
Core/LocalUserInfo.cs
Normal file
42
Core/LocalUserInfo.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Stores a Local UserInfo object.
|
||||
/// </summary>
|
||||
public class LocalUserInfo : UserInfo {
|
||||
|
||||
private string passwordHash;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <b>LocalUserInfo</b> class.
|
||||
/// </summary>
|
||||
/// <param name="username">The Username.</param>
|
||||
/// <param name="displayName">The display name.</param>
|
||||
/// <param name="email">The Email.</param>
|
||||
/// <param name="active">Specifies whether the Account is active or not.</param>
|
||||
/// <param name="dateTime">The creation DateTime.</param>
|
||||
/// <param name="provider">The Users Storage Provider that manages the User.</param>
|
||||
/// <param name="passwordHash">The Password Hash.</param>
|
||||
public LocalUserInfo(string username, string displayName, string email, bool active, DateTime dateTime,
|
||||
IUsersStorageProviderV30 provider, string passwordHash)
|
||||
: base(username, displayName, email, active, dateTime, provider) {
|
||||
this.passwordHash = passwordHash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Password Hash.
|
||||
/// </summary>
|
||||
public string PasswordHash {
|
||||
get { return passwordHash; }
|
||||
set { passwordHash = value; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
57
Core/Log.cs
Normal file
57
Core/Log.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using System.Web.Security;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Records and retrieves Log Entries.
|
||||
/// </summary>
|
||||
public static class Log {
|
||||
|
||||
/// <summary>
|
||||
/// The system username ('SYSTEM').
|
||||
/// </summary>
|
||||
public const string SystemUsername = "SYSTEM";
|
||||
|
||||
/// <summary>
|
||||
/// Writes an Entry in the Log.
|
||||
/// </summary>
|
||||
/// <param name="message">The Message.</param>
|
||||
/// <param name="type">The Type of the Entry.</param>
|
||||
/// <param name="user">The User that generated the Entry.</param>
|
||||
public static void LogEntry(string message, EntryType type, string user) {
|
||||
try {
|
||||
Settings.Provider.LogEntry(message, type, user);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads all the Log Entries (newest to oldest).
|
||||
/// </summary>
|
||||
/// <returns>The Entries.</returns>
|
||||
public static List<LogEntry> ReadEntries() {
|
||||
List<LogEntry> entries = new List<LogEntry>(Settings.Provider.GetLogEntries());
|
||||
entries.Reverse();
|
||||
return entries;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the Log.
|
||||
/// </summary>
|
||||
public static void ClearLog() {
|
||||
Settings.Provider.ClearLog();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
90
Core/MimeTypes.cs
Normal file
90
Core/MimeTypes.cs
Normal file
|
@ -0,0 +1,90 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Contains a list of MIME Types.
|
||||
/// </summary>
|
||||
public static class MimeTypes {
|
||||
|
||||
private static Dictionary<string, string> types;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the list of the MIME Types, with the most common media types.
|
||||
/// </summary>
|
||||
public static void Init() {
|
||||
types = new Dictionary<string, string>(100);
|
||||
|
||||
// Images
|
||||
types.Add("jpg", "image/jpeg");
|
||||
types.Add("jpeg", "image/jpeg");
|
||||
types.Add("jpe", "image/jpeg");
|
||||
types.Add("gif", "image/gif");
|
||||
types.Add("png", "image/png");
|
||||
types.Add("bmp", "image/bmp");
|
||||
types.Add("tif", "image/tiff");
|
||||
types.Add("tiff", "image/tiff");
|
||||
types.Add("svg", "image/svg+xml");
|
||||
types.Add("ico", "image/x-icon");
|
||||
|
||||
// Text
|
||||
types.Add("txt", "text/plain");
|
||||
types.Add("htm", "text/html");
|
||||
types.Add("html", "text/html");
|
||||
types.Add("xhtml", "text/xhtml");
|
||||
types.Add("xml", "text/xml");
|
||||
types.Add("xsl", "text/xsl");
|
||||
types.Add("dtd", "application/xml-dtd");
|
||||
types.Add("css", "text/css");
|
||||
types.Add("rtf", "text/rtf");
|
||||
|
||||
// Archives
|
||||
types.Add("zip", "application/zip");
|
||||
types.Add("tar", "application/x-tar");
|
||||
|
||||
// Multimedia
|
||||
types.Add("ogg", "application/ogg");
|
||||
types.Add("swf", "application/x-shockwave-flash");
|
||||
types.Add("mpga", "audio/mpeg");
|
||||
types.Add("mp2", "audio/mpeg");
|
||||
types.Add("mp3", "audio/mpeg");
|
||||
types.Add("m3u", "audio/x-mpegurl");
|
||||
types.Add("ram", "audio/x-pn-realaudio");
|
||||
types.Add("ra", "audio/x-pn-realaudio");
|
||||
types.Add("rm", "application/vnd.rn-realmedia");
|
||||
types.Add("wav", "application/x-wav");
|
||||
types.Add("mpg", "video/mpeg");
|
||||
types.Add("mpeg", "video/mpeg");
|
||||
types.Add("mpe", "video/mpeg");
|
||||
types.Add("mov", "video/quicktime");
|
||||
types.Add("qt", "video/quicktime");
|
||||
types.Add("avi", "video/x-msvideo");
|
||||
|
||||
// Office
|
||||
types.Add("doc", "application/msword");
|
||||
types.Add("xls", "application/vnd.ms-excel");
|
||||
types.Add("ppt", "application/vnd.ms-powerpoint");
|
||||
// Map Office 2007 formats using the information found here:
|
||||
// http://www.therightstuff.de/2006/12/16/Office+2007+File+Icons+For+Windows+SharePoint+Services+20+And+SharePoint+Portal+Server+2003.aspx
|
||||
types.Add("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
|
||||
types.Add("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
types.Add("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation");
|
||||
|
||||
// Other
|
||||
types.Add("pdf", "application/pdf");
|
||||
types.Add("ai", "application/postscript");
|
||||
types.Add("ps", "application/postscript");
|
||||
types.Add("eps", "application/postscript");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of the MIME Types.
|
||||
/// </summary>
|
||||
public static Dictionary<string, string> Types {
|
||||
get { return types; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
148
Core/NavigationPaths.cs
Normal file
148
Core/NavigationPaths.cs
Normal file
|
@ -0,0 +1,148 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Manages navigation paths.
|
||||
/// </summary>
|
||||
public static class NavigationPaths {
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of the Navigation Paths.
|
||||
/// </summary>
|
||||
/// <returns>The navigation paths, sorted by name.</returns>
|
||||
public static List<NavigationPath> GetAllNavigationPaths() {
|
||||
List<NavigationPath> allPaths = new List<NavigationPath>(30);
|
||||
|
||||
// Retrieve paths from every Pages provider
|
||||
foreach(IPagesStorageProviderV30 provider in Collectors.PagesProviderCollector.AllProviders) {
|
||||
allPaths.AddRange(provider.GetNavigationPaths(null));
|
||||
foreach(NamespaceInfo nspace in provider.GetNamespaces()) {
|
||||
allPaths.AddRange(provider.GetNavigationPaths(nspace));
|
||||
}
|
||||
}
|
||||
|
||||
allPaths.Sort(new NavigationPathComparer());
|
||||
|
||||
return allPaths;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of the Navigation Paths in a namespace.
|
||||
/// </summary>
|
||||
/// <param name="nspace">The namespace.</param>
|
||||
/// <returns>The navigation paths, sorted by name.</returns>
|
||||
public static List<NavigationPath> GetNavigationPaths(NamespaceInfo nspace) {
|
||||
List<NavigationPath> allPaths = new List<NavigationPath>(30);
|
||||
|
||||
// Retrieve paths from every Pages provider
|
||||
foreach(IPagesStorageProviderV30 provider in Collectors.PagesProviderCollector.AllProviders) {
|
||||
allPaths.AddRange(provider.GetNavigationPaths(nspace));
|
||||
}
|
||||
|
||||
allPaths.Sort(new NavigationPathComparer());
|
||||
|
||||
return allPaths;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a Navigation Path's Name.
|
||||
/// </summary>
|
||||
/// <param name="name">The Name.</param>
|
||||
/// <returns>True if the Navigation Path exists.</returns>
|
||||
public static bool Exists(string name) {
|
||||
return Find(name) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds and returns a Path.
|
||||
/// </summary>
|
||||
/// <param name="fullName">The full name.</param>
|
||||
/// <returns>The correct <see cref="T:NavigationPath" /> object or <c>null</c> if no path is found.</returns>
|
||||
public static NavigationPath Find(string fullName) {
|
||||
List<NavigationPath> allPaths = GetAllNavigationPaths();
|
||||
int idx = allPaths.BinarySearch(new NavigationPath(fullName, null), new NavigationPathComparer());
|
||||
if(idx >= 0) return allPaths[idx];
|
||||
else return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new Navigation Path.
|
||||
/// </summary>
|
||||
/// <param name="nspace">The target namespace (<c>null</c> for the root).</param>
|
||||
/// <param name="name">The Name.</param>
|
||||
/// <param name="pages">The Pages.</param>
|
||||
/// <param name="provider">The Provider to use for the new Navigation Path, or <c>null</c> for the default provider.</param>
|
||||
/// <returns>True if the Path is added successfully.</returns>
|
||||
public static bool AddNavigationPath(NamespaceInfo nspace, string name, List<PageInfo> pages, IPagesStorageProviderV30 provider) {
|
||||
string namespaceName = nspace != null ? nspace.Name : null;
|
||||
string fullName = NameTools.GetFullName(namespaceName, name);
|
||||
|
||||
if(Exists(fullName)) return false;
|
||||
|
||||
if(provider == null) provider = Collectors.PagesProviderCollector.GetProvider(Settings.DefaultPagesProvider);
|
||||
|
||||
NavigationPath newPath = provider.AddNavigationPath(namespaceName, name, pages.ToArray());
|
||||
if(newPath != null) Log.LogEntry("Navigation Path " + fullName + " added", EntryType.General, Log.SystemUsername);
|
||||
else Log.LogEntry("Creation failed for Navigation Path " + fullName, EntryType.Error, Log.SystemUsername);
|
||||
return newPath != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a Navigation Path.
|
||||
/// </summary>
|
||||
/// <param name="fullName">The full name of the path to remove.</param>
|
||||
/// <returns><c>true</c> if the path is removed, <c>false</c> otherwise.</returns>
|
||||
public static bool RemoveNavigationPath(string fullName) {
|
||||
NavigationPath path = Find(fullName);
|
||||
if(path == null) return false;
|
||||
|
||||
bool done = path.Provider.RemoveNavigationPath(path);
|
||||
if(done) Log.LogEntry("Navigation Path " + fullName + " removed", EntryType.General, Log.SystemUsername);
|
||||
else Log.LogEntry("Deletion failed for Navigation Path " + fullName, EntryType.Error, Log.SystemUsername);
|
||||
return done;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies a Navigation Path.
|
||||
/// </summary>
|
||||
/// <param name="fullName">The full name of the path to modify.</param>
|
||||
/// <param name="pages">The list of Pages.</param>
|
||||
/// <returns><c>true</c> if the path is modified, <c>false</c> otherwise.</returns>
|
||||
public static bool ModifyNavigationPath(string fullName, List<PageInfo> pages) {
|
||||
NavigationPath path = Find(fullName);
|
||||
if(path == null) return false;
|
||||
|
||||
NavigationPath newPath = path.Provider.ModifyNavigationPath(path, pages.ToArray());
|
||||
if(newPath != null) Log.LogEntry("Navigation Path " + fullName + " modified", EntryType.General, Log.SystemUsername);
|
||||
else Log.LogEntry("Modification failed for Navigation Path " + fullName, EntryType.Error, Log.SystemUsername);
|
||||
return newPath != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds all the Navigation Paths that include a Page.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page.</param>
|
||||
/// <returns>The list of Navigation Paths.</returns>
|
||||
public static string[] PathsPerPage(PageInfo page) {
|
||||
NamespaceInfo pageNamespace = Pages.FindNamespace(NameTools.GetNamespace(page.FullName));
|
||||
|
||||
List<string> result = new List<string>(10);
|
||||
List<NavigationPath> allPaths = GetNavigationPaths(pageNamespace);
|
||||
|
||||
for(int i = 0; i < allPaths.Count; i++) {
|
||||
List<string> pages = new List<string>(allPaths[i].Pages);
|
||||
if(pages.Contains(page.FullName)) {
|
||||
result.Add(allPaths[i].FullName);
|
||||
}
|
||||
}
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
126
Core/PageAttachmentDocument.cs
Normal file
126
Core/PageAttachmentDocument.cs
Normal file
|
@ -0,0 +1,126 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.SearchEngine;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Represents a page attachment document.
|
||||
/// </summary>
|
||||
public class PageAttachmentDocument : IDocument {
|
||||
|
||||
/// <summary>
|
||||
/// The type tag for a <see cref="T:PageAttachmentDocument" />.
|
||||
/// </summary>
|
||||
public const string StandardTypeTag = "A";
|
||||
|
||||
private uint id;
|
||||
private string name;
|
||||
private string title;
|
||||
private string typeTag = StandardTypeTag;
|
||||
private DateTime dateTime;
|
||||
private PageInfo page;
|
||||
private string provider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:PageAttachmentDocument" /> class.
|
||||
/// </summary>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <param name="name">The attachment name.</param>
|
||||
/// <param name="provider">The file provider.</param>
|
||||
/// <param name="dateTime">The modification date/time.</param>
|
||||
public PageAttachmentDocument(PageInfo page, string name, string provider, DateTime dateTime) {
|
||||
if(page == null) throw new ArgumentNullException("page");
|
||||
if(name == null) throw new ArgumentNullException("name");
|
||||
if(name.Length == 0) throw new ArgumentException("Name cannot be empty", "name");
|
||||
if(provider == null) throw new ArgumentNullException("provider");
|
||||
if(provider.Length == 0) throw new ArgumentException("Provider cannot be empty", "provider");
|
||||
|
||||
this.name = page.FullName + "|" + provider + "|" + name;
|
||||
id = Tools.HashDocumentNameForTemporaryIndex(this.name);
|
||||
title = name;
|
||||
this.dateTime = dateTime;
|
||||
this.page = page;
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:PageAttachmentDocument" /> class.
|
||||
/// </summary>
|
||||
/// <param name="doc">The dumped document.</param>
|
||||
public PageAttachmentDocument(DumpedDocument doc) {
|
||||
string[] fields = doc.Name.Split('|');
|
||||
|
||||
id = doc.ID;
|
||||
name = doc.Name;
|
||||
title = doc.Title;
|
||||
dateTime = doc.DateTime;
|
||||
provider = fields[0];
|
||||
page = Pages.FindPage(fields[1]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the globally unique ID of the document.
|
||||
/// </summary>
|
||||
public uint ID {
|
||||
get { return id; }
|
||||
set { id = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the globally-unique name of the document.
|
||||
/// </summary>
|
||||
public string Name {
|
||||
get { return name; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the title of the document, if any.
|
||||
/// </summary>
|
||||
public string Title {
|
||||
get { return title; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tag for the document type.
|
||||
/// </summary>
|
||||
public string TypeTag {
|
||||
get { return typeTag; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the document date/time.
|
||||
/// </summary>
|
||||
public DateTime DateTime {
|
||||
get { return dateTime; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the tokenization of the document content.
|
||||
/// </summary>
|
||||
/// <param name="content">The content to tokenize.</param>
|
||||
/// <returns>The extracted words and their positions (always an empty array).</returns>
|
||||
public WordInfo[] Tokenize(string content) {
|
||||
return ScrewTurn.Wiki.SearchEngine.Tools.Tokenize(content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the page.
|
||||
/// </summary>
|
||||
public PageInfo Page {
|
||||
get { return page; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the provider.
|
||||
/// </summary>
|
||||
public string Provider {
|
||||
get { return provider; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
1523
Core/Pages.cs
Normal file
1523
Core/Pages.cs
Normal file
File diff suppressed because it is too large
Load diff
3073
Core/PagesStorageProvider.cs
Normal file
3073
Core/PagesStorageProvider.cs
Normal file
File diff suppressed because it is too large
Load diff
145
Core/Preferences.cs
Normal file
145
Core/Preferences.cs
Normal file
|
@ -0,0 +1,145 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
using System.Globalization;
|
||||
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Allows access to current user's preferences.
|
||||
/// </summary>
|
||||
public static class Preferences {
|
||||
|
||||
/// <summary>
|
||||
/// Loads the language from a cookie.
|
||||
/// </summary>
|
||||
/// <returns>The language, or <c>null</c>.</returns>
|
||||
public static string LoadLanguageFromCookie() {
|
||||
HttpCookie cookie = HttpContext.Current.Request.Cookies[Settings.CultureCookieName];
|
||||
if(cookie != null) {
|
||||
string culture = cookie["C"];
|
||||
return culture;
|
||||
}
|
||||
else return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the language from the current user's data.
|
||||
/// </summary>
|
||||
/// <returns>The language, or <c>null</c>.</returns>
|
||||
public static string LoadLanguageFromUserData() {
|
||||
UserInfo currentUser = SessionFacade.GetCurrentUser();
|
||||
if(currentUser != null) {
|
||||
string culture = Users.GetUserData(currentUser, "Culture");
|
||||
return culture;
|
||||
}
|
||||
else return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the timezone from a cookie.
|
||||
/// </summary>
|
||||
/// <returns>The timezone, or <c>null</c>.</returns>
|
||||
public static int? LoadTimezoneFromCookie() {
|
||||
HttpCookie cookie = HttpContext.Current.Request.Cookies[Settings.CultureCookieName];
|
||||
if(cookie != null) {
|
||||
string timezone = cookie["T"];
|
||||
return int.Parse(timezone, CultureInfo.InvariantCulture);
|
||||
}
|
||||
else return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the timezone from the current user's data.
|
||||
/// </summary>
|
||||
/// <returns>The timezone, or <c>null</c>.</returns>
|
||||
public static int? LoadTimezoneFromUserData() {
|
||||
UserInfo currentUser = SessionFacade.GetCurrentUser();
|
||||
if(currentUser != null) {
|
||||
string timezone = Users.GetUserData(currentUser, "Timezone");
|
||||
if(timezone != null) return int.Parse(timezone, CultureInfo.InvariantCulture);
|
||||
else return null;
|
||||
}
|
||||
else return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves language and timezone preferences into a cookie.
|
||||
/// </summary>
|
||||
/// <param name="culture">The culture.</param>
|
||||
/// <param name="timezone">The timezone.</param>
|
||||
public static void SavePreferencesInCookie(string culture, int timezone) {
|
||||
HttpCookie cookie = new HttpCookie(Settings.CultureCookieName);
|
||||
cookie.Expires = DateTime.Now.AddYears(10);
|
||||
cookie.Path = Settings.CookiePath;
|
||||
cookie.Values.Add("C", culture);
|
||||
cookie.Values.Add("T", timezone.ToString(CultureInfo.InvariantCulture));
|
||||
HttpContext.Current.Response.Cookies.Add(cookie);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the language and timezone preferences cookie.
|
||||
/// </summary>
|
||||
public static void DeletePreferencesCookie() {
|
||||
HttpCookie cookie = new HttpCookie(Settings.CultureCookieName);
|
||||
cookie.Expires = DateTime.Now.AddYears(-1);
|
||||
cookie.Path = Settings.CookiePath;
|
||||
cookie.Values.Add("C", null);
|
||||
cookie.Values.Add("T", null);
|
||||
HttpContext.Current.Request.Cookies.Add(cookie);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves language and timezone preferences into the current user's data.
|
||||
/// </summary>
|
||||
/// <param name="culture">The culture.</param>
|
||||
/// <param name="timezone">The timezone.</param>
|
||||
/// <returns><c>true</c> if the data is stored, <c>false</c> otherwise.</returns>
|
||||
public static bool SavePreferencesInUserData(string culture, int timezone) {
|
||||
UserInfo user = SessionFacade.GetCurrentUser();
|
||||
if(user != null && !user.Provider.UsersDataReadOnly) {
|
||||
Users.SetUserData(user, "Culture", culture);
|
||||
Users.SetUserData(user, "Timezone", timezone.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if(user == null) {
|
||||
Log.LogEntry("Attempt to save user data when no user has logged in", EntryType.Warning, Log.SystemUsername);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aligns a date/time with the User's preferences (if any).
|
||||
/// </summary>
|
||||
/// <param name="dateTime">The date/time to align.</param>
|
||||
/// <returns>The aligned date/time.</returns>
|
||||
public static DateTime AlignWithTimezone(DateTime dateTime) {
|
||||
// First, look for hard-stored user's preferences
|
||||
// If they are not available, look at the cookie
|
||||
|
||||
int? tempShift = LoadTimezoneFromUserData();
|
||||
if(!tempShift.HasValue) tempShift = LoadTimezoneFromCookie();
|
||||
|
||||
int shift = tempShift.HasValue ? tempShift.Value : Settings.DefaultTimezone;
|
||||
return dateTime.ToUniversalTime().AddMinutes(shift + (dateTime.IsDaylightSavingTime() ? 60 : 0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aligns a date/time with the default timezone.
|
||||
/// </summary>
|
||||
/// <param name="dateTime">The date/time to align.</param>
|
||||
/// <returns>The aligned date/time.</returns>
|
||||
public static DateTime AlignWithServerTimezone(DateTime dateTime) {
|
||||
return dateTime.ToUniversalTime().AddMinutes(Settings.DefaultTimezone + (dateTime.IsDaylightSavingTime() ? 60 : 0));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
18
Core/Properties/AssemblyInfo.cs
Normal file
18
Core/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("ScrewTurn Wiki Core")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("b456637d-2218-43af-b6bf-c1b498c91630")]
|
71
Core/ProviderCollector.cs
Normal file
71
Core/ProviderCollector.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Implements a generic Provider Collector.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the Collector.</typeparam>
|
||||
public class ProviderCollector<T> {
|
||||
|
||||
private List<T> list;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the class.
|
||||
/// </summary>
|
||||
public ProviderCollector() {
|
||||
list = new List<T>(3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a Provider to the Collector.
|
||||
/// </summary>
|
||||
/// <param name="provider">The Provider to add.</param>
|
||||
public void AddProvider(T provider) {
|
||||
lock(this) {
|
||||
list.Add(provider);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a Provider from the Collector.
|
||||
/// </summary>
|
||||
/// <param name="provider">The Provider to remove.</param>
|
||||
public void RemoveProvider(T provider) {
|
||||
lock(this) {
|
||||
list.Remove(provider);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the Providers (copied array).
|
||||
/// </summary>
|
||||
public T[] AllProviders {
|
||||
get {
|
||||
lock(this) {
|
||||
return list.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a Provider, searching for its Type Name.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The Type Name.</param>
|
||||
/// <returns>The Provider, or null if the Provider was not found.</returns>
|
||||
public T GetProvider(string typeName) {
|
||||
lock(this) {
|
||||
for(int i = 0; i < list.Count; i++) {
|
||||
if(list[i].GetType().FullName.Equals(typeName)) return list[i];
|
||||
}
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
556
Core/ProviderLoader.cs
Normal file
556
Core/ProviderLoader.cs
Normal file
|
@ -0,0 +1,556 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
using System.Globalization;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Loads providers from assemblies.
|
||||
/// </summary>
|
||||
public static class ProviderLoader {
|
||||
|
||||
// These must be const because they are used in switch constructs
|
||||
internal const string UsersProviderInterfaceName = "ScrewTurn.Wiki.PluginFramework.IUsersStorageProviderV30";
|
||||
internal const string PagesProviderInterfaceName = "ScrewTurn.Wiki.PluginFramework.IPagesStorageProviderV30";
|
||||
internal const string FilesProviderInterfaceName = "ScrewTurn.Wiki.PluginFramework.IFilesStorageProviderV30";
|
||||
internal const string FormatterProviderInterfaceName = "ScrewTurn.Wiki.PluginFramework.IFormatterProviderV30";
|
||||
internal const string CacheProviderInterfaceName = "ScrewTurn.Wiki.PluginFramework.ICacheProviderV30";
|
||||
|
||||
internal static string SettingsStorageProviderAssemblyName = "";
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the read-only/read-write constraints of providers.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the provider.</typeparam>
|
||||
/// <param name="provider">The provider.</param>
|
||||
/// <exception cref="T:ProviderConstraintException">Thrown when a constraint is not fulfilled.</exception>
|
||||
private static void VerifyConstraints<T>(T provider) {
|
||||
if(typeof(T) == typeof(IUsersStorageProviderV30)) {
|
||||
// If the provider allows to write user accounts data, then group membership must be writeable too
|
||||
|
||||
IUsersStorageProviderV30 actualInstance = (IUsersStorageProviderV30)provider;
|
||||
if(!actualInstance.UserAccountsReadOnly && actualInstance.GroupMembershipReadOnly) {
|
||||
throw new ProviderConstraintException("If UserAccountsReadOnly is false, then also GroupMembershipReadOnly must be false");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to inizialize a provider.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the provider, which must implement <b>IProvider</b>.</typeparam>
|
||||
/// <param name="instance">The provider instance to initialize.</param>
|
||||
/// <param name="collectorEnabled">The collector for enabled providers.</param>
|
||||
/// <param name="collectorDisabled">The collector for disabled providers.</param>
|
||||
private static void Initialize<T>(T instance, ProviderCollector<T> collectorEnabled,
|
||||
ProviderCollector<T> collectorDisabled) where T : class, IProviderV30 {
|
||||
|
||||
if(collectorEnabled.GetProvider(instance.GetType().FullName) != null ||
|
||||
collectorDisabled.GetProvider(instance.GetType().FullName) != null) {
|
||||
|
||||
Log.LogEntry("Provider " + instance.Information.Name + " already in memory", EntryType.Warning, Log.SystemUsername);
|
||||
return;
|
||||
}
|
||||
|
||||
bool enabled = !IsDisabled(instance.GetType().FullName);
|
||||
try {
|
||||
if(enabled) {
|
||||
instance.Init(Host.Instance, LoadConfiguration(instance.GetType().FullName));
|
||||
}
|
||||
}
|
||||
catch(InvalidConfigurationException) {
|
||||
// Disable Provider
|
||||
enabled = false;
|
||||
Log.LogEntry("Unable to load provider " + instance.Information.Name + " (configuration rejected), disabling it", EntryType.Error, Log.SystemUsername);
|
||||
SaveStatus(instance.GetType().FullName, false);
|
||||
}
|
||||
catch {
|
||||
// Disable Provider
|
||||
enabled = false;
|
||||
Log.LogEntry("Unable to load provider " + instance.Information.Name + " (unknown error), disabling it", EntryType.Error, Log.SystemUsername);
|
||||
SaveStatus(instance.GetType().FullName, false);
|
||||
throw; // Exception is rethrown because it's not a normal condition
|
||||
}
|
||||
if(enabled) collectorEnabled.AddProvider(instance);
|
||||
else collectorDisabled.AddProvider(instance);
|
||||
|
||||
// Verify constraints
|
||||
VerifyConstraints<T>(instance);
|
||||
|
||||
Log.LogEntry("Provider " + instance.Information.Name + " loaded (" + (enabled ? "Enabled" : "Disabled") + ")", EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads all the Providers and initialises them.
|
||||
/// </summary>
|
||||
/// <param name="loadUsers">A value indicating whether to load users storage providers.</param>
|
||||
/// <param name="loadPages">A value indicating whether to load pages storage providers.</param>
|
||||
/// <param name="loadFiles">A value indicating whether to load files storage providers.</param>
|
||||
/// <param name="loadFormatters">A value indicating whether to load formatter providers.</param>
|
||||
/// <param name="loadCache">A value indicating whether to load cache providers.</param>
|
||||
public static void FullLoad(bool loadUsers, bool loadPages, bool loadFiles, bool loadFormatters, bool loadCache) {
|
||||
string[] pluginAssemblies = Settings.Provider.ListPluginAssemblies();
|
||||
|
||||
List<IUsersStorageProviderV30> users = new List<IUsersStorageProviderV30>(2);
|
||||
List<IUsersStorageProviderV30> dUsers = new List<IUsersStorageProviderV30>(2);
|
||||
List<IPagesStorageProviderV30> pages = new List<IPagesStorageProviderV30>(2);
|
||||
List<IPagesStorageProviderV30> dPages = new List<IPagesStorageProviderV30>(2);
|
||||
List<IFilesStorageProviderV30> files = new List<IFilesStorageProviderV30>(2);
|
||||
List<IFilesStorageProviderV30> dFiles = new List<IFilesStorageProviderV30>(2);
|
||||
List<IFormatterProviderV30> forms = new List<IFormatterProviderV30>(2);
|
||||
List<IFormatterProviderV30> dForms = new List<IFormatterProviderV30>(2);
|
||||
List<ICacheProviderV30> cache = new List<ICacheProviderV30>(2);
|
||||
List<ICacheProviderV30> dCache = new List<ICacheProviderV30>(2);
|
||||
|
||||
for(int i = 0; i < pluginAssemblies.Length; i++) {
|
||||
IFilesStorageProviderV30[] d;
|
||||
IUsersStorageProviderV30[] u;
|
||||
IPagesStorageProviderV30[] p;
|
||||
IFormatterProviderV30[] f;
|
||||
ICacheProviderV30[] c;
|
||||
LoadFrom(pluginAssemblies[i], out u, out p, out d, out f, out c);
|
||||
if(loadFiles) files.AddRange(d);
|
||||
if(loadUsers) users.AddRange(u);
|
||||
if(loadPages) pages.AddRange(p);
|
||||
if(loadFormatters) forms.AddRange(f);
|
||||
if(loadCache) cache.AddRange(c);
|
||||
}
|
||||
|
||||
// Init and add to the Collectors, starting from files providers
|
||||
for(int i = 0; i < files.Count; i++) {
|
||||
Initialize<IFilesStorageProviderV30>(files[i], Collectors.FilesProviderCollector, Collectors.DisabledFilesProviderCollector);
|
||||
}
|
||||
|
||||
for(int i = 0; i < users.Count; i++) {
|
||||
Initialize<IUsersStorageProviderV30>(users[i], Collectors.UsersProviderCollector, Collectors.DisabledUsersProviderCollector);
|
||||
}
|
||||
|
||||
for(int i = 0; i < pages.Count; i++) {
|
||||
Initialize<IPagesStorageProviderV30>(pages[i], Collectors.PagesProviderCollector, Collectors.DisabledPagesProviderCollector);
|
||||
}
|
||||
|
||||
for(int i = 0; i < forms.Count; i++) {
|
||||
Initialize<IFormatterProviderV30>(forms[i], Collectors.FormatterProviderCollector, Collectors.DisabledFormatterProviderCollector);
|
||||
}
|
||||
|
||||
for(int i = 0; i < cache.Count; i++) {
|
||||
Initialize<ICacheProviderV30>(cache[i], Collectors.CacheProviderCollector, Collectors.DisabledCacheProviderCollector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the Configuration data of a Provider.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The Type Name of the Provider.</param>
|
||||
/// <returns>The Configuration, if available, otherwise an empty string.</returns>
|
||||
public static string LoadConfiguration(string typeName) {
|
||||
return Settings.Provider.GetPluginConfiguration(typeName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the Configuration data of a Provider.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The Type Name of the Provider.</param>
|
||||
/// <param name="config">The Configuration data to save.</param>
|
||||
public static void SaveConfiguration(string typeName, string config) {
|
||||
Settings.Provider.SetPluginConfiguration(typeName, config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the Status of a Provider.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The Type Name of the Provider.</param>
|
||||
/// <param name="enabled">A value specifying whether or not the Provider is enabled.</param>
|
||||
public static void SaveStatus(string typeName, bool enabled) {
|
||||
Settings.Provider.SetPluginStatus(typeName, enabled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value specifying whether or not a Provider is disabled.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The Type Name of the Provider.</param>
|
||||
/// <returns>True if the Provider is disabled.</returns>
|
||||
public static bool IsDisabled(string typeName) {
|
||||
return !Settings.Provider.GetPluginStatus(typeName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads Providers from an assembly.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The path of the Assembly to load the Providers from.</param>
|
||||
public static int LoadFromAuto(string assembly) {
|
||||
IUsersStorageProviderV30[] users;
|
||||
IPagesStorageProviderV30[] pages;
|
||||
IFilesStorageProviderV30[] files;
|
||||
IFormatterProviderV30[] forms;
|
||||
ICacheProviderV30[] cache;
|
||||
LoadFrom(assembly, out users, out pages, out files, out forms, out cache);
|
||||
|
||||
int count = 0;
|
||||
|
||||
// Init and add to the Collectors, starting from files providers
|
||||
for(int i = 0; i < files.Length; i++) {
|
||||
Initialize<IFilesStorageProviderV30>(files[i], Collectors.FilesProviderCollector, Collectors.DisabledFilesProviderCollector);
|
||||
count++;
|
||||
}
|
||||
|
||||
for(int i = 0; i < users.Length; i++) {
|
||||
Initialize<IUsersStorageProviderV30>(users[i], Collectors.UsersProviderCollector, Collectors.DisabledUsersProviderCollector);
|
||||
count++;
|
||||
}
|
||||
|
||||
for(int i = 0; i < pages.Length; i++) {
|
||||
Initialize<IPagesStorageProviderV30>(pages[i], Collectors.PagesProviderCollector, Collectors.DisabledPagesProviderCollector);
|
||||
count++;
|
||||
}
|
||||
|
||||
for(int i = 0; i < forms.Length; i++) {
|
||||
Initialize<IFormatterProviderV30>(forms[i], Collectors.FormatterProviderCollector, Collectors.DisabledFormatterProviderCollector);
|
||||
count++;
|
||||
}
|
||||
|
||||
for(int i = 0; i < cache.Length; i++) {
|
||||
Initialize<ICacheProviderV30>(cache[i], Collectors.CacheProviderCollector, Collectors.DisabledCacheProviderCollector);
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads Providers from an assembly.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The path of the Assembly to load the Providers from.</param>
|
||||
/// <param name="users">The Users Providers.</param>
|
||||
/// <param name="files">The Files Providers.</param>
|
||||
/// <param name="pages">The Pages Providers.</param>
|
||||
/// <param name="formatters">The Formatter Providers.</param>
|
||||
/// <param name="cache">The Cache Providers.</param>
|
||||
/// <remarks>The Components returned are <b>not</b> initialized.</remarks>
|
||||
public static void LoadFrom(string assembly, out IUsersStorageProviderV30[] users, out IPagesStorageProviderV30[] pages,
|
||||
out IFilesStorageProviderV30[] files, out IFormatterProviderV30[] formatters, out ICacheProviderV30[] cache) {
|
||||
|
||||
Assembly asm = null;
|
||||
try {
|
||||
//asm = Assembly.LoadFile(assembly);
|
||||
// This way the DLL is not locked and can be deleted at runtime
|
||||
asm = Assembly.Load(LoadAssemblyFromProvider(Path.GetFileName(assembly)));
|
||||
}
|
||||
catch {
|
||||
files = new IFilesStorageProviderV30[0];
|
||||
users = new IUsersStorageProviderV30[0];
|
||||
pages = new IPagesStorageProviderV30[0];
|
||||
formatters = new IFormatterProviderV30[0];
|
||||
cache = new ICacheProviderV30[0];
|
||||
|
||||
Log.LogEntry("Unable to load assembly " + Path.GetFileNameWithoutExtension(assembly), EntryType.Error, Log.SystemUsername);
|
||||
return;
|
||||
}
|
||||
|
||||
Type[] types = null;
|
||||
|
||||
try {
|
||||
types = asm.GetTypes();
|
||||
}
|
||||
catch(ReflectionTypeLoadException) {
|
||||
files = new IFilesStorageProviderV30[0];
|
||||
users = new IUsersStorageProviderV30[0];
|
||||
pages = new IPagesStorageProviderV30[0];
|
||||
formatters = new IFormatterProviderV30[0];
|
||||
cache = new ICacheProviderV30[0];
|
||||
|
||||
Log.LogEntry("Unable to load providers from (probably v2) assembly " + Path.GetFileNameWithoutExtension(assembly), EntryType.Error, Log.SystemUsername);
|
||||
return;
|
||||
}
|
||||
|
||||
List<IUsersStorageProviderV30> urs = new List<IUsersStorageProviderV30>();
|
||||
List<IPagesStorageProviderV30> pgs = new List<IPagesStorageProviderV30>();
|
||||
List<IFilesStorageProviderV30> fls = new List<IFilesStorageProviderV30>();
|
||||
List<IFormatterProviderV30> frs = new List<IFormatterProviderV30>();
|
||||
List<ICacheProviderV30> che = new List<ICacheProviderV30>();
|
||||
|
||||
Type[] interfaces;
|
||||
for(int i = 0; i < types.Length; i++) {
|
||||
// Avoid to load abstract classes as they cannot be instantiated
|
||||
if(types[i].IsAbstract) continue;
|
||||
|
||||
interfaces = types[i].GetInterfaces();
|
||||
foreach(Type iface in interfaces) {
|
||||
if(iface == typeof(IUsersStorageProviderV30)) {
|
||||
IUsersStorageProviderV30 tmpu = CreateInstance<IUsersStorageProviderV30>(asm, types[i]);
|
||||
if(tmpu != null) {
|
||||
urs.Add(tmpu);
|
||||
Collectors.FileNames[tmpu.GetType().FullName] = assembly;
|
||||
}
|
||||
}
|
||||
if(iface == typeof(IPagesStorageProviderV30)) {
|
||||
IPagesStorageProviderV30 tmpp = CreateInstance<IPagesStorageProviderV30>(asm, types[i]);
|
||||
if(tmpp != null) {
|
||||
pgs.Add(tmpp);
|
||||
Collectors.FileNames[tmpp.GetType().FullName] = assembly;
|
||||
}
|
||||
}
|
||||
if(iface == typeof(IFilesStorageProviderV30)) {
|
||||
IFilesStorageProviderV30 tmpd = CreateInstance<IFilesStorageProviderV30>(asm, types[i]);
|
||||
if(tmpd != null) {
|
||||
fls.Add(tmpd);
|
||||
Collectors.FileNames[tmpd.GetType().FullName] = assembly;
|
||||
}
|
||||
}
|
||||
if(iface == typeof(IFormatterProviderV30)) {
|
||||
IFormatterProviderV30 tmpf = CreateInstance<IFormatterProviderV30>(asm, types[i]);
|
||||
if(tmpf != null) {
|
||||
frs.Add(tmpf);
|
||||
Collectors.FileNames[tmpf.GetType().FullName] = assembly;
|
||||
}
|
||||
}
|
||||
if(iface == typeof(ICacheProviderV30)) {
|
||||
ICacheProviderV30 tmpc = CreateInstance<ICacheProviderV30>(asm, types[i]);
|
||||
if(tmpc != null) {
|
||||
che.Add(tmpc);
|
||||
Collectors.FileNames[tmpc.GetType().FullName] = assembly;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
users = urs.ToArray();
|
||||
pages = pgs.ToArray();
|
||||
files = fls.ToArray();
|
||||
formatters = frs.ToArray();
|
||||
cache = che.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of a type implementing a provider interface.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The provider interface type.</typeparam>
|
||||
/// <param name="asm">The assembly that contains the type.</param>
|
||||
/// <param name="type">The type to create an instance of.</param>
|
||||
/// <returns>The instance, or <c>null</c>.</returns>
|
||||
private static T CreateInstance<T>(Assembly asm, Type type) where T : class, IProviderV30 {
|
||||
T instance;
|
||||
try {
|
||||
instance = asm.CreateInstance(type.ToString()) as T;
|
||||
return instance;
|
||||
}
|
||||
catch {
|
||||
Log.LogEntry("Unable to create instance of " + type.ToString(), EntryType.Error, Log.SystemUsername);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the content of an assembly from disk.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The assembly file full path.</param>
|
||||
/// <returns>The content of the assembly, in a byte array form.</returns>
|
||||
private static byte[] LoadAssemblyFromDisk(string assembly) {
|
||||
return File.ReadAllBytes(assembly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the content of an assembly from the settings provider.
|
||||
/// </summary>
|
||||
/// <param name="assemblyName">The name of the assembly, such as "Assembly.dll".</param>
|
||||
/// <returns>The content fo the assembly.</returns>
|
||||
private static byte[] LoadAssemblyFromProvider(string assemblyName) {
|
||||
return Settings.Provider.RetrievePluginAssembly(assemblyName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the proper Setting Storage Provider, given its name.
|
||||
/// </summary>
|
||||
/// <param name="name">The fully qualified name (such as "Namespace.ProviderClass, MyAssembly"), or <c>null</c>/<b>String.Empty</b>/"<b>default</b>" for the default provider.</param>
|
||||
/// <returns>The settings storage provider.</returns>
|
||||
public static ISettingsStorageProviderV30 LoadSettingsStorageProvider(string name) {
|
||||
if(name == null || name.Length == 0 || string.Compare(name, "default", true, CultureInfo.InvariantCulture) == 0) {
|
||||
return new SettingsStorageProvider();
|
||||
}
|
||||
|
||||
ISettingsStorageProviderV30 result = null;
|
||||
|
||||
Exception inner = null;
|
||||
|
||||
if(name.Contains(",")) {
|
||||
string[] fields = name.Split(',');
|
||||
if(fields.Length == 2) {
|
||||
fields[0] = fields[0].Trim(' ', '"');
|
||||
fields[1] = fields[1].Trim(' ', '"');
|
||||
try {
|
||||
// assemblyName should be an absolute path or a relative path in bin or public\Plugins
|
||||
|
||||
Assembly asm;
|
||||
Type t;
|
||||
string assemblyName = fields[1];
|
||||
if(!assemblyName.ToLowerInvariant().EndsWith(".dll")) assemblyName += ".dll";
|
||||
|
||||
if(File.Exists(assemblyName)) {
|
||||
asm = Assembly.Load(LoadAssemblyFromDisk(assemblyName));
|
||||
t = asm.GetType(fields[0]);
|
||||
SettingsStorageProviderAssemblyName = Path.GetFileName(assemblyName);
|
||||
}
|
||||
else {
|
||||
string tentativePluginsPath = null;
|
||||
try {
|
||||
// Settings.PublicDirectory is only available when running the web app
|
||||
tentativePluginsPath = Path.Combine(Settings.PublicDirectory, "Plugins");
|
||||
tentativePluginsPath = Path.Combine(tentativePluginsPath, assemblyName);
|
||||
}
|
||||
catch { }
|
||||
|
||||
if(!string.IsNullOrEmpty(tentativePluginsPath) && File.Exists(tentativePluginsPath)) {
|
||||
asm = Assembly.Load(LoadAssemblyFromDisk(tentativePluginsPath));
|
||||
t = asm.GetType(fields[0]);
|
||||
SettingsStorageProviderAssemblyName = Path.GetFileName(tentativePluginsPath);
|
||||
}
|
||||
else {
|
||||
// Trim .dll
|
||||
t = Type.GetType(fields[0] + "," + assemblyName.Substring(0, assemblyName.Length - 4), true, true);
|
||||
SettingsStorageProviderAssemblyName = assemblyName;
|
||||
}
|
||||
}
|
||||
|
||||
result = t.GetConstructor(new Type[0]).Invoke(new object[0]) as ISettingsStorageProviderV30;
|
||||
}
|
||||
catch(Exception ex) {
|
||||
inner = ex;
|
||||
result = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(result == null) throw new ArgumentException("Could not load the specified Settings Storage Provider", inner);
|
||||
else return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads all settings storage providers available in all DLLs stored in a provider.
|
||||
/// </summary>
|
||||
/// <param name="repository">The input provider.</param>
|
||||
/// <returns>The providers found (not initialized).</returns>
|
||||
public static ISettingsStorageProviderV30[] LoadAllSettingsStorageProviders(ISettingsStorageProviderV30 repository) {
|
||||
// This method is actually a memory leak because it can be executed multimple times
|
||||
// Every time it loads a set of assemblies which cannot be unloaded (unless a separate AppDomain is used)
|
||||
|
||||
List<ISettingsStorageProviderV30> result = new List<ISettingsStorageProviderV30>();
|
||||
|
||||
foreach(string dll in repository.ListPluginAssemblies()) {
|
||||
byte[] asmBin = repository.RetrievePluginAssembly(dll);
|
||||
Assembly asm = Assembly.Load(asmBin);
|
||||
|
||||
Type[] types = null;
|
||||
try {
|
||||
types = asm.GetTypes();
|
||||
}
|
||||
catch(ReflectionTypeLoadException) {
|
||||
// Skip assembly
|
||||
Log.LogEntry("Unable to load providers from (probably v2) assembly " + Path.GetFileNameWithoutExtension(dll), EntryType.Error, Log.SystemUsername);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach(Type type in types) {
|
||||
// Avoid to load abstract classes as they cannot be instantiated
|
||||
if(type.IsAbstract) continue;
|
||||
|
||||
Type[] interfaces = type.GetInterfaces();
|
||||
|
||||
foreach(Type iface in interfaces) {
|
||||
if(iface == typeof(ISettingsStorageProviderV30)) {
|
||||
try {
|
||||
ISettingsStorageProviderV30 temp = asm.CreateInstance(type.ToString()) as ISettingsStorageProviderV30;
|
||||
if(temp != null) result.Add(temp);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to change a provider's configuration.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The provider.</param>
|
||||
/// <param name="configuration">The new configuration.</param>
|
||||
/// <param name="error">The error message, if any.</param>
|
||||
/// <returns><c>true</c> if the configuration is saved, <c>false</c> if the provider rejected it.</returns>
|
||||
public static bool TryChangeConfiguration(string typeName, string configuration, out string error) {
|
||||
error = null;
|
||||
|
||||
bool enabled, canDisable;
|
||||
IProviderV30 provider = Collectors.FindProvider(typeName, out enabled, out canDisable);
|
||||
|
||||
try {
|
||||
provider.Init(Host.Instance, configuration);
|
||||
}
|
||||
catch(InvalidConfigurationException icex) {
|
||||
error = icex.Message;
|
||||
return false;
|
||||
}
|
||||
|
||||
SaveConfiguration(typeName, configuration);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables a provider.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The provider to disable.</param>
|
||||
public static void DisableProvider(string typeName) {
|
||||
bool enabled, canDisable;
|
||||
IProviderV30 provider = Collectors.FindProvider(typeName, out enabled, out canDisable);
|
||||
if(enabled && canDisable) {
|
||||
provider.Shutdown();
|
||||
Collectors.TryDisable(typeName);
|
||||
SaveStatus(typeName, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables a provider.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The provider to enable.</param>
|
||||
public static void EnableProvider(string typeName) {
|
||||
bool enabled, canDisable;
|
||||
IProviderV30 provider = Collectors.FindProvider(typeName, out enabled, out canDisable);
|
||||
if(!enabled) {
|
||||
provider.Init(Host.Instance, LoadConfiguration(typeName));
|
||||
Collectors.TryEnable(typeName);
|
||||
SaveStatus(typeName, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unloads a provider from memory.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The provider to unload.</param>
|
||||
public static void UnloadProvider(string typeName) {
|
||||
DisableProvider(typeName);
|
||||
Collectors.TryUnload(typeName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines an exception thrown when a constraint is not fulfilled by a provider.
|
||||
/// </summary>
|
||||
public class ProviderConstraintException : Exception {
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:ProviderConstraintException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
public ProviderConstraintException(string message)
|
||||
: base(message) { }
|
||||
|
||||
}
|
||||
|
||||
}
|
131
Core/ProviderUpdater.cs
Normal file
131
Core/ProviderUpdater.cs
Normal file
|
@ -0,0 +1,131 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
using System.Net;
|
||||
using System.IO;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Handles the updating of providers.
|
||||
/// </summary>
|
||||
public class ProviderUpdater {
|
||||
|
||||
private List<string> visitedUrls;
|
||||
|
||||
private ISettingsStorageProviderV30 settingsProvider;
|
||||
private List<IProviderV30> providers;
|
||||
private Dictionary<string, string> fileNamesForProviders;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:ProviderUpdater" /> class.
|
||||
/// </summary>
|
||||
/// <param name="settingsProvider">The settings storage provider.</param>
|
||||
/// <param name="fileNamesForProviders">A provider->file dictionary.</param>
|
||||
/// <param name="providers">The providers to update.</param>
|
||||
public ProviderUpdater(ISettingsStorageProviderV30 settingsProvider,
|
||||
Dictionary<string, string> fileNamesForProviders,
|
||||
params IProviderV30[][] providers) {
|
||||
|
||||
if(settingsProvider == null) throw new ArgumentNullException("settingsProvider");
|
||||
if(fileNamesForProviders == null) throw new ArgumentNullException("fileNamesForProviders");
|
||||
if(providers == null) throw new ArgumentNullException("providers");
|
||||
if(providers.Length == 0) throw new ArgumentException("Providers cannot be empty", "providers");
|
||||
|
||||
this.settingsProvider = settingsProvider;
|
||||
this.fileNamesForProviders = fileNamesForProviders;
|
||||
|
||||
this.providers = new List<IProviderV30>(20);
|
||||
foreach(IProviderV30[] group in providers) {
|
||||
this.providers.AddRange(group);
|
||||
}
|
||||
|
||||
visitedUrls = new List<string>(10);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates all the providers.
|
||||
/// </summary>
|
||||
/// <returns>The number of updated DLLs.</returns>
|
||||
public int UpdateAll() {
|
||||
Log.LogEntry("Starting automatic providers update", EntryType.General, Log.SystemUsername);
|
||||
|
||||
int updatedDlls = 0;
|
||||
|
||||
foreach(IProviderV30 prov in providers) {
|
||||
if(string.IsNullOrEmpty(prov.Information.UpdateUrl)) continue;
|
||||
|
||||
string newVersion;
|
||||
string newDllUrl;
|
||||
UpdateStatus status = Tools.GetUpdateStatus(prov.Information.UpdateUrl,
|
||||
prov.Information.Version, out newVersion, out newDllUrl);
|
||||
|
||||
if(status == UpdateStatus.NewVersionFound && !string.IsNullOrEmpty(newDllUrl)) {
|
||||
// Update is possible
|
||||
|
||||
// Case insensitive check
|
||||
if(!visitedUrls.Contains(newDllUrl.ToLowerInvariant())) {
|
||||
string dllName = null;
|
||||
|
||||
if(!fileNamesForProviders.TryGetValue(prov.GetType().FullName, out dllName)) {
|
||||
Log.LogEntry("Could not determine DLL name for provider " + prov.GetType().FullName, EntryType.Error, Log.SystemUsername);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Download DLL and install
|
||||
if(DownloadAndUpdateDll(prov, newDllUrl, dllName)) {
|
||||
visitedUrls.Add(newDllUrl.ToLowerInvariant());
|
||||
updatedDlls++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Skip DLL (already updated)
|
||||
Log.LogEntry("Skipping provider " + prov.GetType().FullName + ": DLL already updated", EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.LogEntry("Automatic providers update completed: updated " + updatedDlls.ToString() + " DLLs", EntryType.General, Log.SystemUsername);
|
||||
|
||||
return updatedDlls;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads and updates a DLL.
|
||||
/// </summary>
|
||||
/// <param name="provider">The provider.</param>
|
||||
/// <param name="url">The URL of the new DLL.</param>
|
||||
/// <param name="filename">The file name of the DLL.</param>
|
||||
private bool DownloadAndUpdateDll(IProviderV30 provider, string url, string filename) {
|
||||
try {
|
||||
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
|
||||
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
|
||||
|
||||
if(response.StatusCode != HttpStatusCode.OK) {
|
||||
Log.LogEntry("Update failed for provider " + provider.GetType().FullName + ": Status Code=" + response.StatusCode.ToString(), EntryType.Error, Log.SystemUsername);
|
||||
response.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
BinaryReader reader = new BinaryReader(response.GetResponseStream());
|
||||
byte[] content = reader.ReadBytes((int)response.ContentLength);
|
||||
reader.Close();
|
||||
|
||||
bool done = settingsProvider.StorePluginAssembly(filename, content);
|
||||
if(done) Log.LogEntry("Provider " + provider.GetType().FullName + " updated", EntryType.General, Log.SystemUsername);
|
||||
else Log.LogEntry("Update failed for provider " + provider.GetType().FullName + ": could not store assembly", EntryType.Error, Log.SystemUsername);
|
||||
|
||||
return done;
|
||||
}
|
||||
catch(Exception ex) {
|
||||
Log.LogEntry("Update failed for provider " + provider.GetType().FullName + ": " + ex.ToString(), EntryType.Error, Log.SystemUsername);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
44
Core/RecentChanges.cs
Normal file
44
Core/RecentChanges.cs
Normal file
|
@ -0,0 +1,44 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Manages the Wiki's Recent Changes.
|
||||
/// </summary>
|
||||
public static class RecentChanges {
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the changes, sorted by date/time ascending.
|
||||
/// </summary>
|
||||
public static RecentChange[] GetAllChanges() {
|
||||
RecentChange[] changes = Settings.Provider.GetRecentChanges();
|
||||
|
||||
RecentChange[] myCopy = new RecentChange[changes.Length];
|
||||
Array.Copy(changes, myCopy, changes.Length);
|
||||
|
||||
Array.Sort(myCopy, (x, y) => { return x.DateTime.CompareTo(y.DateTime); });
|
||||
|
||||
return myCopy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new change.
|
||||
/// </summary>
|
||||
/// <param name="page">The page name.</param>
|
||||
/// <param name="title">The page title.</param>
|
||||
/// <param name="messageSubject">The message subject.</param>
|
||||
/// <param name="dateTime">The date/time.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="change">The change.</param>
|
||||
/// <param name="descr">The description (optional).</param>
|
||||
public static void AddChange(string page, string title, string messageSubject, DateTime dateTime, string user, Change change, string descr) {
|
||||
Settings.Provider.AddRecentChange(page, title, messageSubject, dateTime, user, change, descr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
61
Core/Redirections.cs
Normal file
61
Core/Redirections.cs
Normal file
|
@ -0,0 +1,61 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Manages information about Page Redirections.
|
||||
/// </summary>
|
||||
public static class Redirections {
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new Redirection.
|
||||
/// </summary>
|
||||
/// <param name="source">The source Page.</param>
|
||||
/// <param name="destination">The destination Page.</param>
|
||||
/// <returns>True if the Redirection is added, false otherwise.</returns>
|
||||
/// <remarks>The method prevents circular and multi-level redirection.</remarks>
|
||||
public static void AddRedirection(PageInfo source, PageInfo destination) {
|
||||
if(source == null) throw new ArgumentNullException("source");
|
||||
if(destination == null) throw new ArgumentNullException("destination");
|
||||
|
||||
Cache.Provider.AddRedirection(source.FullName, destination.FullName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the destination Page.
|
||||
/// </summary>
|
||||
/// <param name="page">The source Page.</param>
|
||||
/// <returns>The destination Page, or null.</returns>
|
||||
public static PageInfo GetDestination(PageInfo page) {
|
||||
if(page == null) throw new ArgumentNullException("page");
|
||||
|
||||
string destination = Cache.Provider.GetRedirectionDestination(page.FullName);
|
||||
if(string.IsNullOrEmpty(destination)) return null;
|
||||
else return Pages.FindPage(destination);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes any occurrence of a Page from the redirection table, both on sources and destinations.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page to wipe-out.</param>
|
||||
/// <remarks>This method is useful when removing a Page.</remarks>
|
||||
public static void WipePageOut(PageInfo page) {
|
||||
if(page == null) throw new ArgumentNullException("page");
|
||||
|
||||
Cache.Provider.RemovePageFromRedirections(page.FullName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the Redirection table.
|
||||
/// </summary>
|
||||
public static void Clear() {
|
||||
Cache.Provider.ClearRedirections();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
1127
Core/ReverseFormatter.cs
Normal file
1127
Core/ReverseFormatter.cs
Normal file
File diff suppressed because it is too large
Load diff
210
Core/SearchTools.cs
Normal file
210
Core/SearchTools.cs
Normal file
|
@ -0,0 +1,210 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
using ScrewTurn.Wiki.SearchEngine;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Implements tools for searching through the wiki.
|
||||
/// </summary>
|
||||
public static class SearchTools {
|
||||
|
||||
/// <summary>
|
||||
/// Searches for pages with name or title similar to a specified value.
|
||||
/// </summary>
|
||||
/// <param name="name">The name to look for (<c>null</c> for the root).</param>
|
||||
/// <param name="nspace">The namespace to search into.</param>
|
||||
/// <returns>The similar pages, if any.</returns>
|
||||
public static PageInfo[] SearchSimilarPages(string name, string nspace) {
|
||||
if(string.IsNullOrEmpty(nspace)) nspace = null;
|
||||
|
||||
SearchResultCollection searchResults = Search(name, false, false, SearchOptions.AtLeastOneWord);
|
||||
|
||||
List<PageInfo> result = new List<PageInfo>(20);
|
||||
|
||||
foreach(SearchResult res in searchResults) {
|
||||
PageDocument pageDoc = res.Document as PageDocument;
|
||||
if(pageDoc != null) {
|
||||
string pageNamespace = NameTools.GetNamespace(pageDoc.PageInfo.FullName);
|
||||
if(string.IsNullOrEmpty(pageNamespace)) pageNamespace = null;
|
||||
|
||||
if(pageNamespace == nspace) {
|
||||
result.Add(pageDoc.PageInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Search page names for matches
|
||||
List<PageInfo> allPages = Pages.GetPages(Pages.FindNamespace(nspace));
|
||||
PageNameComparer comp = new PageNameComparer();
|
||||
string currentName = name.ToLowerInvariant();
|
||||
foreach(PageInfo page in allPages) {
|
||||
if(NameTools.GetLocalName(page.FullName).ToLowerInvariant().Contains(currentName)) {
|
||||
if(result.Find(delegate(PageInfo p) { return comp.Compare(p, page) == 0; }) == null) {
|
||||
result.Add(page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a search in the wiki.
|
||||
/// </summary>
|
||||
/// <param name="query">The search query.</param>
|
||||
/// <param name="fullText">A value indicating whether to perform a full-text search.</param>
|
||||
/// <param name="searchFilesAndAttachments">A value indicating whether to search through files and attachments.</param>
|
||||
/// <param name="options">The search options.</param>
|
||||
/// <returns>The results collection.</returns>
|
||||
public static SearchResultCollection Search(string query, bool fullText, bool searchFilesAndAttachments, SearchOptions options) {
|
||||
|
||||
// First, search regular page content...
|
||||
List<SearchResultCollection> allCollections = new List<SearchResultCollection>(3);
|
||||
|
||||
foreach(IPagesStorageProviderV30 prov in Collectors.PagesProviderCollector.AllProviders) {
|
||||
SearchResultCollection currentResults = prov.PerformSearch(new SearchParameters(query, options));
|
||||
|
||||
if(!fullText) {
|
||||
// All non title-related matches must be removed
|
||||
SearchResultCollection filteredResults = new SearchResultCollection(10);
|
||||
|
||||
foreach(SearchResult res in currentResults) {
|
||||
foreach(WordInfo word in res.Matches) {
|
||||
if(word.Location == WordLocation.Title) {
|
||||
filteredResults.Add(res);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allCollections.Add(filteredResults);
|
||||
}
|
||||
else allCollections.Add(currentResults);
|
||||
}
|
||||
|
||||
// ... normalize relevance based on the number of providers
|
||||
float providerNormalizationFactor = 1F / (float)Collectors.PagesProviderCollector.AllProviders.Length;
|
||||
foreach(SearchResultCollection coll in allCollections) {
|
||||
foreach(SearchResult result in coll) {
|
||||
result.Relevance.NormalizeAfterFinalization(providerNormalizationFactor);
|
||||
}
|
||||
}
|
||||
|
||||
if(searchFilesAndAttachments) {
|
||||
// ... then build a temporary index for files and attachments...
|
||||
StandardIndex temporaryIndex = new StandardIndex();
|
||||
uint tempDocumentId = 1;
|
||||
uint tempWordId = 1;
|
||||
temporaryIndex.IndexChanged += delegate(object sender, IndexChangedEventArgs e) {
|
||||
if(e.Change == IndexChangeType.DocumentAdded) {
|
||||
List<WordId> ids = null;
|
||||
if(e.ChangeData.Words != null) {
|
||||
ids = new List<WordId>(20);
|
||||
foreach(DumpedWord d in e.ChangeData.Words) {
|
||||
ids.Add(new WordId(d.Text, tempWordId));
|
||||
tempWordId++;
|
||||
}
|
||||
}
|
||||
e.Result = new IndexStorerResult(tempDocumentId, ids);
|
||||
tempDocumentId++;
|
||||
}
|
||||
};
|
||||
temporaryIndex.SetBuildDocumentDelegate(DetectFileOrAttachment);
|
||||
|
||||
foreach(IFilesStorageProviderV30 prov in Collectors.FilesProviderCollector.AllProviders) {
|
||||
TraverseDirectories(temporaryIndex, prov, null);
|
||||
|
||||
string[] pagesWithAttachments = prov.GetPagesWithAttachments();
|
||||
foreach(string page in pagesWithAttachments) {
|
||||
// Store attachments for the current page in the index
|
||||
PageInfo pageInfo = Pages.FindPage(page);
|
||||
|
||||
// pageInfo can be null if the index is corrupted
|
||||
if(pageInfo != null) {
|
||||
foreach(string attachment in prov.ListPageAttachments(pageInfo)) {
|
||||
FileDetails details = prov.GetPageAttachmentDetails(pageInfo, attachment);
|
||||
temporaryIndex.StoreDocument(new PageAttachmentDocument(pageInfo,
|
||||
attachment, prov.GetType().FullName, details.LastModified),
|
||||
new string[0], "", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ... then search in the temporary index and normalize relevance
|
||||
SearchResultCollection filesAndAttachments = temporaryIndex.Search(new SearchParameters(query, options));
|
||||
providerNormalizationFactor = 1F / (float)Collectors.FilesProviderCollector.AllProviders.Length;
|
||||
foreach(SearchResult result in filesAndAttachments) {
|
||||
result.Relevance.NormalizeAfterFinalization(providerNormalizationFactor);
|
||||
}
|
||||
|
||||
allCollections.Add(filesAndAttachments);
|
||||
}
|
||||
|
||||
return CombineCollections(allCollections);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detects the document in a dumped instance for files and attachments.
|
||||
/// </summary>
|
||||
/// <param name="doc">The dumped document instance.</param>
|
||||
/// <returns>The proper document instance.</returns>
|
||||
private static IDocument DetectFileOrAttachment(DumpedDocument doc) {
|
||||
if(doc.TypeTag == FileDocument.StandardTypeTag) {
|
||||
return new FileDocument(doc);
|
||||
}
|
||||
else if(doc.TypeTag == PageAttachmentDocument.StandardTypeTag) {
|
||||
return new PageAttachmentDocument(doc);
|
||||
}
|
||||
else throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traverses a directory tree, indexing all files.
|
||||
/// </summary>
|
||||
/// <param name="index">The output index.</param>
|
||||
/// <param name="provider">The provider.</param>
|
||||
/// <param name="currentDir">The current directory.</param>
|
||||
private static void TraverseDirectories(InMemoryIndexBase index, IFilesStorageProviderV30 provider, string currentDir) {
|
||||
// Store files in the index
|
||||
foreach(string file in provider.ListFiles(currentDir)) {
|
||||
FileDetails details = provider.GetFileDetails(file);
|
||||
index.StoreDocument(new FileDocument(file, provider.GetType().FullName, details.LastModified),
|
||||
new string[0], "", null);
|
||||
}
|
||||
|
||||
// Recursively process all sub-directories
|
||||
foreach(string directory in provider.ListDirectories(currentDir)) {
|
||||
TraverseDirectories(index, provider, directory);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combines a set of <see cref="T:SearchResultCollection" />s into a single object.
|
||||
/// </summary>
|
||||
/// <param name="collections">The collections.</param>
|
||||
/// <returns>The resulting <see cref="T:SearchResultCollection" />.</returns>
|
||||
private static SearchResultCollection CombineCollections(List<SearchResultCollection> collections) {
|
||||
List<SearchResult> tempResults = new List<SearchResult>(100);
|
||||
|
||||
foreach(SearchResultCollection coll in collections) {
|
||||
tempResults.AddRange(coll);
|
||||
}
|
||||
|
||||
tempResults.Sort(delegate(SearchResult x, SearchResult y) { return y.Relevance.Value.CompareTo(x.Relevance.Value); });
|
||||
|
||||
SearchResultCollection resultCollection = new SearchResultCollection(50);
|
||||
foreach(SearchResult singleResult in tempResults) {
|
||||
resultCollection.Add(singleResult);
|
||||
}
|
||||
|
||||
return resultCollection;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
194
Core/SessionFacade.cs
Normal file
194
Core/SessionFacade.cs
Normal file
|
@ -0,0 +1,194 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Web.SessionState;
|
||||
using System.Web;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Exposes in a strongly-typed fashion the Session variables.
|
||||
/// </summary>
|
||||
public static class SessionFacade {
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current Session object.
|
||||
/// </summary>
|
||||
private static HttpSessionState Session {
|
||||
get { return HttpContext.Current.Session; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Login Key.
|
||||
/// </summary>
|
||||
public static string LoginKey {
|
||||
get { return Session != null ? (string)Session["LoginKey"] : null; }
|
||||
set { if(Session != null) Session["LoginKey"] = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current username, or <c>null</c>.
|
||||
/// </summary>
|
||||
public static string CurrentUsername {
|
||||
get { return Session != null ? Session["Username"] as string : null; }
|
||||
set { if(Session != null) Session["Username"] = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current user, if any.
|
||||
/// </summary>
|
||||
/// <returns>The current user, or <c>null</c>.</returns>
|
||||
public static UserInfo GetCurrentUser() {
|
||||
if(Session != null) {
|
||||
string sessionId = Session.SessionID;
|
||||
|
||||
UserInfo current = SessionCache.GetCurrentUser(sessionId);
|
||||
if(current != null) return current;
|
||||
else {
|
||||
string un = CurrentUsername;
|
||||
if(string.IsNullOrEmpty(un)) return null;
|
||||
else if(object.ReferenceEquals(un, AnonymousUsername) || un == AnonymousUsername) return Users.GetAdministratorAccount();
|
||||
else {
|
||||
current = Users.FindUser(un);
|
||||
SessionCache.SetCurrentUser(sessionId, current);
|
||||
return current;
|
||||
}
|
||||
}
|
||||
}
|
||||
else return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The username for anonymous users.
|
||||
/// </summary>
|
||||
public const string AnonymousUsername = "|";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current username for security checks purposes only.
|
||||
/// </summary>
|
||||
/// <returns>The current username, or <b>AnonymousUsername</b> if anonymous.</returns>
|
||||
public static string GetCurrentUsername() {
|
||||
string un = CurrentUsername;
|
||||
if(string.IsNullOrEmpty(un)) return AnonymousUsername;
|
||||
else return un;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current user groups.
|
||||
/// </summary>
|
||||
public static UserGroup[] GetCurrentGroups() {
|
||||
if(Session != null) {
|
||||
string sessionId = Session.SessionID;
|
||||
UserGroup[] groups = SessionCache.GetCurrentGroups(sessionId);
|
||||
|
||||
if(groups == null || groups.Length == 0) {
|
||||
UserInfo current = GetCurrentUser();
|
||||
if(current != null) {
|
||||
groups = new UserGroup[current.Groups.Length];
|
||||
for(int i = 0; i < groups.Length; i++) {
|
||||
groups[i] = Users.FindUserGroup(current.Groups[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
groups = new UserGroup[] { Users.FindUserGroup(Settings.AnonymousGroup) };
|
||||
}
|
||||
|
||||
SessionCache.SetCurrentGroups(sessionId, groups);
|
||||
}
|
||||
|
||||
return groups;
|
||||
}
|
||||
else return new UserGroup[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current group names.
|
||||
/// </summary>
|
||||
/// <returns>The group names.</returns>
|
||||
public static string[] GetCurrentGroupNames() {
|
||||
return Array.ConvertAll(GetCurrentGroups(), delegate(UserGroup g) { return g.Name; });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Breadcrumbs Manager.
|
||||
/// </summary>
|
||||
public static BreadcrumbsManager Breadcrumbs {
|
||||
get { return new BreadcrumbsManager(); }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements a session data cache whose lifetime is only limited to one request.
|
||||
/// </summary>
|
||||
public static class SessionCache {
|
||||
|
||||
private static Dictionary<string, UserInfo> currentUsers = new Dictionary<string, UserInfo>(100);
|
||||
private static Dictionary<string, UserGroup[]> currentGroups = new Dictionary<string, UserGroup[]>(100);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current user, if any, of a session.
|
||||
/// </summary>
|
||||
/// <param name="sessionId">The session ID.</param>
|
||||
/// <returns>The current user, or <c>null</c>.</returns>
|
||||
public static UserInfo GetCurrentUser(string sessionId) {
|
||||
lock(currentUsers) {
|
||||
UserInfo result = null;
|
||||
currentUsers.TryGetValue(sessionId, out result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current user of a session.
|
||||
/// </summary>
|
||||
/// <param name="sessionId">The session ID.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
public static void SetCurrentUser(string sessionId, UserInfo user) {
|
||||
lock(currentUsers) {
|
||||
currentUsers[sessionId] = user;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current groups, if any, of a session.
|
||||
/// </summary>
|
||||
/// <param name="sessionId">The session ID.</param>
|
||||
/// <returns>The groups, or <b>null.</b></returns>
|
||||
public static UserGroup[] GetCurrentGroups(string sessionId) {
|
||||
lock(currentGroups) {
|
||||
UserGroup[] result = null;
|
||||
currentGroups.TryGetValue(sessionId, out result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current groups of a session.
|
||||
/// </summary>
|
||||
/// <param name="sessionId">The session ID.</param>
|
||||
/// <param name="groups">The groups.</param>
|
||||
public static void SetCurrentGroups(string sessionId, UserGroup[] groups) {
|
||||
lock(currentGroups) {
|
||||
currentGroups[sessionId] = groups;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all cached data of a session.
|
||||
/// </summary>
|
||||
/// <param name="sessionId">The session ID.</param>
|
||||
public static void ClearData(string sessionId) {
|
||||
lock(currentUsers) {
|
||||
currentUsers.Remove(sessionId);
|
||||
}
|
||||
lock(currentGroups) {
|
||||
currentGroups.Remove(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
1233
Core/Settings.cs
Normal file
1233
Core/Settings.cs
Normal file
File diff suppressed because it is too large
Load diff
1239
Core/SettingsStorageProvider.cs
Normal file
1239
Core/SettingsStorageProvider.cs
Normal file
File diff suppressed because it is too large
Load diff
137
Core/Snippets.cs
Normal file
137
Core/Snippets.cs
Normal file
|
@ -0,0 +1,137 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Manages snippets.
|
||||
/// </summary>
|
||||
public static class Snippets {
|
||||
|
||||
/// <summary>
|
||||
/// Gets the complete list of the Snippets.
|
||||
/// </summary>
|
||||
/// <returns>The snippets, sorted by name.</returns>
|
||||
public static List<Snippet> GetSnippets() {
|
||||
List<Snippet> allSnippets = new List<Snippet>(50);
|
||||
|
||||
// Retrieve all snippets from Pages Provider
|
||||
foreach(IPagesStorageProviderV30 provider in Collectors.PagesProviderCollector.AllProviders) {
|
||||
allSnippets.AddRange(provider.GetSnippets());
|
||||
}
|
||||
|
||||
allSnippets.Sort(new SnippetNameComparer());
|
||||
|
||||
return allSnippets;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a Snippet.
|
||||
/// </summary>
|
||||
/// <param name="name">The Name of the Snippet to find.</param>
|
||||
/// <returns>The Snippet or null if it is not found.</returns>
|
||||
public static Snippet Find(string name) {
|
||||
List<Snippet> allSnippets = GetSnippets();
|
||||
|
||||
int result = allSnippets.BinarySearch(new Snippet(name, "", null), new SnippetNameComparer());
|
||||
|
||||
if(allSnippets.Count > 0 && result >= 0) return allSnippets[result];
|
||||
else return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Snippet.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the Snippet.</param>
|
||||
/// <param name="content">The content of the Snippet.</param>
|
||||
/// <param name="provider">The Provider to use to store the Snippet (<c>null</c> for the default provider).</param>
|
||||
/// <returns>True if the Snippets has been addedd successfully.</returns>
|
||||
public static bool AddSnippet(string name, string content, IPagesStorageProviderV30 provider) {
|
||||
if(Find(name) != null) return false;
|
||||
|
||||
if(provider == null) provider = Collectors.PagesProviderCollector.GetProvider(Settings.DefaultPagesProvider);
|
||||
|
||||
Snippet newSnippet = provider.AddSnippet(name, content);
|
||||
|
||||
if(newSnippet != null) {
|
||||
Log.LogEntry("Snippet " + name + " created", EntryType.General, Log.SystemUsername);
|
||||
Content.ClearPseudoCache();
|
||||
Content.InvalidateAllPages();
|
||||
}
|
||||
else Log.LogEntry("Creation failed for Snippet " + name, EntryType.Error, Log.SystemUsername);
|
||||
|
||||
return newSnippet != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a Snippet.
|
||||
/// </summary>
|
||||
/// <param name="snippet">The Snippet to remove.</param>
|
||||
/// <returns>True if the Snippet has been removed successfully.</returns>
|
||||
public static bool RemoveSnippet(Snippet snippet) {
|
||||
bool done = snippet.Provider.RemoveSnippet(snippet.Name);
|
||||
|
||||
if(done) {
|
||||
Log.LogEntry("Snippet " + snippet.Name + " deleted", EntryType.General, Log.SystemUsername);
|
||||
Content.ClearPseudoCache();
|
||||
Content.InvalidateAllPages();
|
||||
}
|
||||
else Log.LogEntry("Deletion failed for Snippet " + snippet.Name, EntryType.Error, Log.SystemUsername);
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies the Content of a Snippet.
|
||||
/// </summary>
|
||||
/// <param name="snippet">The Snippet to update.</param>
|
||||
/// <param name="content">The new Content.</param>
|
||||
/// <returns>True if the Snippet has been updated successfully.</returns>
|
||||
public static bool ModifySnippet(Snippet snippet, string content) {
|
||||
Snippet newSnippet = snippet.Provider.ModifySnippet(snippet.Name, content);
|
||||
|
||||
if(newSnippet != null) {
|
||||
Log.LogEntry("Snippet " + snippet.Name + " updated", EntryType.General, Log.SystemUsername);
|
||||
Content.ClearPseudoCache();
|
||||
Content.InvalidateAllPages();
|
||||
}
|
||||
else Log.LogEntry("Modification failed for Snippet " + snippet.Name, EntryType.Error, Log.SystemUsername);
|
||||
|
||||
return newSnippet != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The regular expression to use for extracting parameters.
|
||||
/// </summary>
|
||||
public static readonly Regex ParametersRegex = new Regex("\\?[a-zA-Z0-9_-]+\\?", RegexOptions.Compiled | RegexOptions.CultureInvariant);
|
||||
|
||||
/// <summary>
|
||||
/// Counts the parameters in a snippet.
|
||||
/// </summary>
|
||||
/// <param name="snippet">The snippet.</param>
|
||||
/// <returns>The number of parameters.</returns>
|
||||
public static int CountParameters(Snippet snippet) {
|
||||
return ExtractParameterNames(snippet).Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the parameters in a snippet.
|
||||
/// </summary>
|
||||
/// <param name="snippet">The snippet.</param>
|
||||
/// <returns>The parameter names.</returns>
|
||||
public static string[] ExtractParameterNames(Snippet snippet) {
|
||||
List<string> parms = new List<string>();
|
||||
foreach(Match m in ParametersRegex.Matches(snippet.Content)) {
|
||||
string value = m.Value.Substring(1, m.Value.Length - 2);
|
||||
if(m.Success && !parms.Contains(value)) parms.Add(value);
|
||||
}
|
||||
return parms.ToArray();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
436
Core/StartupTools.cs
Normal file
436
Core/StartupTools.cs
Normal file
|
@ -0,0 +1,436 @@
|
|||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Resources;
|
||||
using System.Web.Configuration;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Provides tools for starting and shutting down the wiki engine.
|
||||
/// </summary>
|
||||
public static class StartupTools {
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Settings Storage Provider configuration string from web.config.
|
||||
/// </summary>
|
||||
/// <returns>The configuration string.</returns>
|
||||
public static string GetSettingsStorageProviderConfiguration() {
|
||||
string config = WebConfigurationManager.AppSettings["SettingsStorageProviderConfig"];
|
||||
if(config != null) return config;
|
||||
else return "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the DLLs into the settings storage provider, if appropriate.
|
||||
/// </summary>
|
||||
/// <param name="provider">The provider.</param>
|
||||
/// <param name="settingsProviderAsmName">The file name of the assembly that contains the current Settings Storage Provider.</param>
|
||||
private static void UpdateDllsIntoSettingsProvider(ISettingsStorageProviderV30 provider, string settingsProviderAsmName) {
|
||||
// Look into public\Plugins (hardcoded)
|
||||
string fullPath = Path.Combine(Settings.PublicDirectory, "Plugins");
|
||||
|
||||
if(!Directory.Exists(fullPath)) return;
|
||||
|
||||
string[] dlls = Directory.GetFiles(fullPath, "*.dll");
|
||||
string[] installedDlls = provider.ListPluginAssemblies();
|
||||
|
||||
foreach(string dll in dlls) {
|
||||
bool found = false;
|
||||
string filename = Path.GetFileName(dll);
|
||||
foreach(string instDll in installedDlls) {
|
||||
if(instDll.ToLowerInvariant() == filename.ToLowerInvariant()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!found && filename.ToLowerInvariant() == settingsProviderAsmName.ToLowerInvariant()) {
|
||||
found = true;
|
||||
}
|
||||
|
||||
if(found) {
|
||||
// Update DLL
|
||||
provider.StorePluginAssembly(filename, File.ReadAllBytes(dll));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs all needed startup operations.
|
||||
/// </summary>
|
||||
public static void Startup() {
|
||||
// Load Host
|
||||
Host.Instance = new Host();
|
||||
|
||||
// Load config
|
||||
ISettingsStorageProviderV30 ssp = ProviderLoader.LoadSettingsStorageProvider(WebConfigurationManager.AppSettings["SettingsStorageProvider"]);
|
||||
ssp.Init(Host.Instance, GetSettingsStorageProviderConfiguration());
|
||||
Collectors.SettingsProvider = ssp;
|
||||
//Settings.Instance = new Settings(ssp);
|
||||
|
||||
if(!(ssp is SettingsStorageProvider)) {
|
||||
// Update DLLs from public\Plugins
|
||||
UpdateDllsIntoSettingsProvider(ssp, ProviderLoader.SettingsStorageProviderAssemblyName);
|
||||
}
|
||||
|
||||
// Initialize authorization managers
|
||||
//AuthReader.Instance = new AuthReader(Settings.Provider);
|
||||
//AuthWriter.Instance = new AuthWriter(Settings.Provider);
|
||||
//AuthChecker.Instance = new AuthChecker(Settings.Provider);
|
||||
|
||||
if(ssp.GetMetaDataItem(MetaDataItem.AccountActivationMessage, null) == "")
|
||||
ssp.SetMetaDataItem(MetaDataItem.AccountActivationMessage, null, Defaults.AccountActivationMessageContent);
|
||||
if(ssp.GetMetaDataItem(MetaDataItem.EditNotice, null) == "")
|
||||
ssp.SetMetaDataItem(MetaDataItem.EditNotice, null, Defaults.EditNoticeContent);
|
||||
if(ssp.GetMetaDataItem(MetaDataItem.Footer, null) == "")
|
||||
ssp.SetMetaDataItem(MetaDataItem.Footer, null, Defaults.FooterContent);
|
||||
if(ssp.GetMetaDataItem(MetaDataItem.Header, null) == "")
|
||||
ssp.SetMetaDataItem(MetaDataItem.Header, null, Defaults.HeaderContent);
|
||||
if(ssp.GetMetaDataItem(MetaDataItem.PasswordResetProcedureMessage, null) == "")
|
||||
ssp.SetMetaDataItem(MetaDataItem.PasswordResetProcedureMessage, null, Defaults.PasswordResetProcedureMessageContent);
|
||||
if(ssp.GetMetaDataItem(MetaDataItem.Sidebar, null) == "")
|
||||
ssp.SetMetaDataItem(MetaDataItem.Sidebar, null, Defaults.SidebarContent);
|
||||
if(ssp.GetMetaDataItem(MetaDataItem.PageChangeMessage, null) == "")
|
||||
ssp.SetMetaDataItem(MetaDataItem.PageChangeMessage, null, Defaults.PageChangeMessage);
|
||||
if(ssp.GetMetaDataItem(MetaDataItem.DiscussionChangeMessage, null) == "")
|
||||
ssp.SetMetaDataItem(MetaDataItem.DiscussionChangeMessage, null, Defaults.DiscussionChangeMessage);
|
||||
if(ssp.GetMetaDataItem(MetaDataItem.ApproveDraftMessage, null) == "") {
|
||||
ssp.SetMetaDataItem(MetaDataItem.ApproveDraftMessage, null, Defaults.ApproveDraftMessage);
|
||||
}
|
||||
|
||||
// Load MIME Types
|
||||
MimeTypes.Init();
|
||||
|
||||
// Load Providers
|
||||
Collectors.FileNames = new System.Collections.Generic.Dictionary<string, string>(10);
|
||||
Collectors.UsersProviderCollector = new ProviderCollector<IUsersStorageProviderV30>();
|
||||
Collectors.PagesProviderCollector = new ProviderCollector<IPagesStorageProviderV30>();
|
||||
Collectors.FilesProviderCollector = new ProviderCollector<IFilesStorageProviderV30>();
|
||||
Collectors.FormatterProviderCollector = new ProviderCollector<IFormatterProviderV30>();
|
||||
Collectors.CacheProviderCollector = new ProviderCollector<ICacheProviderV30>();
|
||||
Collectors.DisabledUsersProviderCollector = new ProviderCollector<IUsersStorageProviderV30>();
|
||||
Collectors.DisabledPagesProviderCollector = new ProviderCollector<IPagesStorageProviderV30>();
|
||||
Collectors.DisabledFilesProviderCollector = new ProviderCollector<IFilesStorageProviderV30>();
|
||||
Collectors.DisabledFormatterProviderCollector = new ProviderCollector<IFormatterProviderV30>();
|
||||
Collectors.DisabledCacheProviderCollector = new ProviderCollector<ICacheProviderV30>();
|
||||
|
||||
// Load built-in providers
|
||||
|
||||
// Files storage providers have to be loaded BEFORE users storage providers in order to properly set permissions
|
||||
FilesStorageProvider f = new FilesStorageProvider();
|
||||
if(!ProviderLoader.IsDisabled(f.GetType().FullName)) {
|
||||
f.Init(Host.Instance, "");
|
||||
Collectors.FilesProviderCollector.AddProvider(f);
|
||||
Log.LogEntry("Provider " + f.Information.Name + " loaded (Enabled)", EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
else {
|
||||
Collectors.DisabledFilesProviderCollector.AddProvider(f);
|
||||
Log.LogEntry("Provider " + f.Information.Name + " loaded (Disabled)", EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
|
||||
UsersStorageProvider u = new UsersStorageProvider();
|
||||
if(!ProviderLoader.IsDisabled(u.GetType().FullName)) {
|
||||
u.Init(Host.Instance, "");
|
||||
Collectors.UsersProviderCollector.AddProvider(u);
|
||||
Log.LogEntry("Provider " + u.Information.Name + " loaded (Enabled)", EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
else {
|
||||
Collectors.DisabledUsersProviderCollector.AddProvider(u);
|
||||
Log.LogEntry("Provider " + u.Information.Name + " loaded (Disabled)", EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
|
||||
// Load Users (pages storage providers might need access to users/groups data for upgrading from 2.0 to 3.0)
|
||||
ProviderLoader.FullLoad(true, false, false, false, false);
|
||||
//Users.Instance = new Users();
|
||||
bool groupsCreated = VerifyAndCreateDefaultGroups();
|
||||
|
||||
PagesStorageProvider p = new PagesStorageProvider();
|
||||
if(!ProviderLoader.IsDisabled(p.GetType().FullName)) {
|
||||
p.Init(Host.Instance, "");
|
||||
Collectors.PagesProviderCollector.AddProvider(p);
|
||||
Log.LogEntry("Provider " + p.Information.Name + " loaded (Enabled)", EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
else {
|
||||
Collectors.DisabledPagesProviderCollector.AddProvider(p);
|
||||
Log.LogEntry("Provider " + p.Information.Name + " loaded (Disabled)", EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
|
||||
CacheProvider c = new CacheProvider();
|
||||
if(!ProviderLoader.IsDisabled(c.GetType().FullName)) {
|
||||
c.Init(Host.Instance, "");
|
||||
Collectors.CacheProviderCollector.AddProvider(c);
|
||||
Log.LogEntry("Provider " + c.Information.Name + " loaded (Enabled)", EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
else {
|
||||
Collectors.DisabledCacheProviderCollector.AddProvider(c);
|
||||
Log.LogEntry("Provider " + c.Information.Name + " loaded (Disabled)", EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
|
||||
// Load all other providers
|
||||
ProviderLoader.FullLoad(false, true, true, true, true);
|
||||
|
||||
if(groupsCreated) {
|
||||
// It is necessary to set default permissions for file management
|
||||
UserGroup administratorsGroup = Users.FindUserGroup(Settings.AdministratorsGroup);
|
||||
UserGroup anonymousGroup = Users.FindUserGroup(Settings.AnonymousGroup);
|
||||
UserGroup usersGroup = Users.FindUserGroup(Settings.UsersGroup);
|
||||
|
||||
SetAdministratorsGroupDefaultPermissions(administratorsGroup);
|
||||
SetUsersGroupDefaultPermissions(usersGroup);
|
||||
SetAnonymousGroupDefaultPermissions(anonymousGroup);
|
||||
}
|
||||
|
||||
// Init cache
|
||||
//Cache.Instance = new Cache(Collectors.CacheProviderCollector.GetProvider(Settings.DefaultCacheProvider));
|
||||
if(Collectors.CacheProviderCollector.GetProvider(Settings.DefaultCacheProvider) == null) {
|
||||
Log.LogEntry("Default Cache Provider was not loaded, backing to integrated provider", EntryType.Error, Log.SystemUsername);
|
||||
Settings.DefaultCacheProvider = typeof(CacheProvider).FullName;
|
||||
Collectors.TryEnable(Settings.DefaultCacheProvider);
|
||||
}
|
||||
|
||||
// Load Snippets and templates
|
||||
//Snippets.Instance = new Snippets();
|
||||
//Templates.Instance = new Templates();
|
||||
|
||||
// Load Pages
|
||||
//Pages.Instance = new Pages();
|
||||
|
||||
// Load Nav. Paths
|
||||
//NavigationPaths.Instance = new NavigationPaths();
|
||||
|
||||
// Create Collisions class
|
||||
//Collisions.Instance = new Collisions();
|
||||
|
||||
// Create Redirections class
|
||||
//Redirections.Instance = new Redirections();
|
||||
|
||||
// Create the Main Page, if needed
|
||||
if(Pages.FindPage(Settings.DefaultPage) == null) CreateMainPage();
|
||||
|
||||
Log.LogEntry("ScrewTurn Wiki is ready", EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the existence of the default user groups and creates them if necessary.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if the groups were created, <c>false</c> otherwise.</returns>
|
||||
private static bool VerifyAndCreateDefaultGroups() {
|
||||
UserGroup administratorsGroup = Users.FindUserGroup(Settings.AdministratorsGroup);
|
||||
UserGroup anonymousGroup = Users.FindUserGroup(Settings.AnonymousGroup);
|
||||
UserGroup usersGroup = Users.FindUserGroup(Settings.UsersGroup);
|
||||
|
||||
// Create default groups if they don't exist already, initializing permissions
|
||||
|
||||
bool aGroupWasCreated = false;
|
||||
|
||||
if(administratorsGroup == null) {
|
||||
Users.AddUserGroup(Settings.AdministratorsGroup, "Built-in Administrators");
|
||||
administratorsGroup = Users.FindUserGroup(Settings.AdministratorsGroup);
|
||||
|
||||
aGroupWasCreated = true;
|
||||
}
|
||||
|
||||
if(usersGroup == null) {
|
||||
Users.AddUserGroup(Settings.UsersGroup, "Built-in Users");
|
||||
usersGroup = Users.FindUserGroup(Settings.UsersGroup);
|
||||
|
||||
aGroupWasCreated = true;
|
||||
}
|
||||
|
||||
if(anonymousGroup == null) {
|
||||
Users.AddUserGroup(Settings.AnonymousGroup, "Built-in Anonymous Users");
|
||||
anonymousGroup = Users.FindUserGroup(Settings.AnonymousGroup);
|
||||
|
||||
aGroupWasCreated = true;
|
||||
}
|
||||
|
||||
if(aGroupWasCreated) {
|
||||
ImportPageDiscussionPermissions();
|
||||
}
|
||||
|
||||
return aGroupWasCreated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the main page.
|
||||
/// </summary>
|
||||
private static void CreateMainPage() {
|
||||
Pages.CreatePage(null as string, Settings.DefaultPage);
|
||||
Pages.ModifyPage(Pages.FindPage(Settings.DefaultPage), "Main Page", Log.SystemUsername,
|
||||
DateTime.Now, "", Defaults.MainPageContent, null, null, SaveMode.Normal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs shutdown operations, such as shutting-down Providers.
|
||||
/// </summary>
|
||||
public static void Shutdown() {
|
||||
foreach(IFormatterProviderV30 provider in Collectors.FormatterProviderCollector.AllProviders) {
|
||||
provider.Shutdown();
|
||||
}
|
||||
foreach(IPagesStorageProviderV30 provider in Collectors.PagesProviderCollector.AllProviders) {
|
||||
provider.Shutdown();
|
||||
}
|
||||
foreach(IUsersStorageProviderV30 provider in Collectors.UsersProviderCollector.AllProviders) {
|
||||
provider.Shutdown();
|
||||
}
|
||||
foreach(IFilesStorageProviderV30 provider in Collectors.FilesProviderCollector.AllProviders) {
|
||||
provider.Shutdown();
|
||||
}
|
||||
foreach(ICacheProviderV30 provider in Collectors.CacheProviderCollector.AllProviders) {
|
||||
provider.Shutdown();
|
||||
}
|
||||
Settings.Provider.Shutdown();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default permissions for the administrators group, properly importing version 2.0 values.
|
||||
/// </summary>
|
||||
/// <param name="administrators">The administrators group.</param>
|
||||
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
|
||||
public static bool SetAdministratorsGroupDefaultPermissions(UserGroup administrators) {
|
||||
// Administrators can do any operation
|
||||
return AuthWriter.SetPermissionForGlobals(AuthStatus.Grant, Actions.FullControl, administrators);
|
||||
|
||||
// Settings.ConfigVisibleToAdmins is not imported on purpose
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default permissions for the users group, properly importing version 2.0 values.
|
||||
/// </summary>
|
||||
/// <param name="users">The users group.</param>
|
||||
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
|
||||
public static bool SetUsersGroupDefaultPermissions(UserGroup users) {
|
||||
bool done = true;
|
||||
|
||||
// Set namespace-related permissions
|
||||
if(Settings.UsersCanCreateNewPages) {
|
||||
done &= AuthWriter.SetPermissionForNamespace(AuthStatus.Grant, null, Actions.ForNamespaces.CreatePages, users);
|
||||
}
|
||||
else done &= AuthWriter.SetPermissionForNamespace(AuthStatus.Grant, null, Actions.ForNamespaces.ModifyPages, users);
|
||||
done &= AuthWriter.SetPermissionForNamespace(AuthStatus.Grant, null, Actions.ForNamespaces.PostDiscussion, users);
|
||||
if(Settings.UsersCanCreateNewCategories || Settings.UsersCanManagePageCategories) {
|
||||
done &= AuthWriter.SetPermissionForNamespace(AuthStatus.Grant, null, Actions.ForNamespaces.ManageCategories, users);
|
||||
}
|
||||
|
||||
done &= SetupFileManagementPermissions(users);
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default permissions for the anonymous users group, properly importing version 2.0 values.
|
||||
/// </summary>
|
||||
/// <param name="anonymous">The anonymous users group.</param>
|
||||
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
|
||||
public static bool SetAnonymousGroupDefaultPermissions(UserGroup anonymous) {
|
||||
bool done = true;
|
||||
|
||||
// Properly import Private/Public Mode wiki
|
||||
if(Settings.PrivateAccess) {
|
||||
// Nothing to do, because without any explicit grant, Anonymous users cannot do anything
|
||||
}
|
||||
else if(Settings.PublicAccess) {
|
||||
// Public access, allow modification and propagate file management permissions if they were allowed for anonymous users
|
||||
done &= AuthWriter.SetPermissionForNamespace(AuthStatus.Grant, null, Actions.ForNamespaces.ModifyPages, anonymous);
|
||||
done &= AuthWriter.SetPermissionForNamespace(AuthStatus.Grant, null, Actions.ForNamespaces.DownloadAttachments, anonymous);
|
||||
if(Settings.UsersCanCreateNewPages) {
|
||||
done &= AuthWriter.SetPermissionForNamespace(AuthStatus.Grant, null, Actions.ForNamespaces.CreatePages, anonymous);
|
||||
}
|
||||
if(Settings.UsersCanCreateNewCategories || Settings.UsersCanManagePageCategories) {
|
||||
done &= AuthWriter.SetPermissionForNamespace(AuthStatus.Grant, null, Actions.ForNamespaces.ManageCategories, anonymous);
|
||||
}
|
||||
if(Settings.FileManagementInPublicAccessAllowed) {
|
||||
SetupFileManagementPermissions(anonymous);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Standard configuration, only allow read permissions
|
||||
done &= AuthWriter.SetPermissionForNamespace(AuthStatus.Grant, null, Actions.ForNamespaces.ReadPages, anonymous);
|
||||
done &= AuthWriter.SetPermissionForNamespace(AuthStatus.Grant, null, Actions.ForNamespaces.ReadDiscussion, anonymous);
|
||||
done &= AuthWriter.SetPermissionForNamespace(AuthStatus.Grant, null, Actions.ForNamespaces.DownloadAttachments, anonymous);
|
||||
|
||||
foreach(IFilesStorageProviderV30 prov in Collectors.FilesProviderCollector.AllProviders) {
|
||||
done &= AuthWriter.SetPermissionForDirectory(AuthStatus.Grant, prov, "/", Actions.ForDirectories.DownloadFiles, anonymous);
|
||||
}
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets file management permissions for the users or anonymous users group, importing version 2.0 values.
|
||||
/// </summary>
|
||||
/// <param name="group">The group.</param>
|
||||
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
|
||||
private static bool SetupFileManagementPermissions(UserGroup group) {
|
||||
bool done = true;
|
||||
|
||||
if(Settings.UsersCanViewFiles) {
|
||||
done &= AuthWriter.SetPermissionForNamespace(AuthStatus.Grant, null, Actions.ForNamespaces.DownloadAttachments, group);
|
||||
foreach(IFilesStorageProviderV30 prov in Collectors.FilesProviderCollector.AllProviders) {
|
||||
done &= AuthWriter.SetPermissionForDirectory(AuthStatus.Grant, prov, "/", Actions.ForDirectories.DownloadFiles, group);
|
||||
}
|
||||
}
|
||||
if(Settings.UsersCanUploadFiles) {
|
||||
done &= AuthWriter.SetPermissionForNamespace(AuthStatus.Grant, null, Actions.ForNamespaces.UploadAttachments, group);
|
||||
foreach(IFilesStorageProviderV30 prov in Collectors.FilesProviderCollector.AllProviders) {
|
||||
done &= AuthWriter.SetPermissionForDirectory(AuthStatus.Grant, prov, "/", Actions.ForDirectories.UploadFiles, group);
|
||||
done &= AuthWriter.SetPermissionForDirectory(AuthStatus.Grant, prov, "/", Actions.ForDirectories.CreateDirectories, group);
|
||||
}
|
||||
}
|
||||
if(Settings.UsersCanDeleteFiles) {
|
||||
done &= AuthWriter.SetPermissionForNamespace(AuthStatus.Grant, null, Actions.ForNamespaces.DeleteAttachments, group);
|
||||
foreach(IFilesStorageProviderV30 prov in Collectors.FilesProviderCollector.AllProviders) {
|
||||
done &= AuthWriter.SetPermissionForDirectory(AuthStatus.Grant, prov, "/", Actions.ForDirectories.DeleteFiles, group);
|
||||
done &= AuthWriter.SetPermissionForDirectory(AuthStatus.Grant, prov, "/", Actions.ForDirectories.DeleteDirectories, group);
|
||||
}
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Imports version 2.0 page discussion settings and properly propagates them to user groups and single pages, when needed.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if the operation succeeded, <c>false</c> otherwise.</returns>
|
||||
private static bool ImportPageDiscussionPermissions() {
|
||||
// Notes
|
||||
// Who can read pages, can read discussions
|
||||
// Who can modify pages, can post messages and read discussions
|
||||
// Who can manage pages, can manage discussions and post messages
|
||||
|
||||
// Possible values: page|normal|locked|public
|
||||
string value = Settings.DiscussionPermissions.ToLowerInvariant();
|
||||
|
||||
UserGroup usersGroup = Users.FindUserGroup(Settings.UsersGroup);
|
||||
UserGroup anonymousGroup = Users.FindUserGroup(Settings.AnonymousGroup);
|
||||
|
||||
bool done = true;
|
||||
|
||||
switch(value) {
|
||||
case "page":
|
||||
// Nothing to do
|
||||
break;
|
||||
case "normal":
|
||||
// Allow Users to post messages
|
||||
done &= AuthWriter.SetPermissionForNamespace(AuthStatus.Grant, null, Actions.ForNamespaces.PostDiscussion, usersGroup);
|
||||
break;
|
||||
case "locked":
|
||||
// Deny Users to post messages
|
||||
done &= AuthWriter.SetPermissionForNamespace(AuthStatus.Deny, null, Actions.ForNamespaces.PostDiscussion, usersGroup);
|
||||
break;
|
||||
case "public":
|
||||
// Allow Users and Anonymous Users to post messages
|
||||
done &= AuthWriter.SetPermissionForNamespace(AuthStatus.Grant, null, Actions.ForNamespaces.PostDiscussion, usersGroup);
|
||||
done &= AuthWriter.SetPermissionForNamespace(AuthStatus.Grant, null, Actions.ForNamespaces.PostDiscussion, anonymousGroup);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
56
Core/SubjectInfo.cs
Normal file
56
Core/SubjectInfo.cs
Normal file
|
@ -0,0 +1,56 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Describes the subject of an ACL entry.
|
||||
/// </summary>
|
||||
public class SubjectInfo {
|
||||
|
||||
private string name;
|
||||
private SubjectType type;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:SubjectInfo" /> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="type">The type.</param>
|
||||
public SubjectInfo(string name, SubjectType type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
public string Name {
|
||||
get { return name; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type.
|
||||
/// </summary>
|
||||
public SubjectType Type {
|
||||
get { return type; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists legal values for the type of a subject.
|
||||
/// </summary>
|
||||
public enum SubjectType {
|
||||
/// <summary>
|
||||
/// A user.
|
||||
/// </summary>
|
||||
User,
|
||||
/// <summary>
|
||||
/// A group.
|
||||
/// </summary>
|
||||
Group
|
||||
}
|
||||
|
||||
}
|
96
Core/Templates.cs
Normal file
96
Core/Templates.cs
Normal file
|
@ -0,0 +1,96 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Manages content templates.
|
||||
/// </summary>
|
||||
public static class Templates {
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the content templates.
|
||||
/// </summary>
|
||||
/// <returns>The content templates, sorted by name.</returns>
|
||||
public static List<ContentTemplate> GetTemplates() {
|
||||
List<ContentTemplate> result = new List<ContentTemplate>(20);
|
||||
|
||||
// Retrieve templates from all providers
|
||||
foreach(IPagesStorageProviderV30 prov in Collectors.PagesProviderCollector.AllProviders) {
|
||||
result.AddRange(prov.GetContentTemplates());
|
||||
}
|
||||
|
||||
result.Sort(new ContentTemplateNameComparer());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a content template.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the template to find.</param>
|
||||
/// <returns>The content template, or <c>null</c>.</returns>
|
||||
public static ContentTemplate Find(string name) {
|
||||
List<ContentTemplate> templates = GetTemplates();
|
||||
|
||||
int index = templates.BinarySearch(new ContentTemplate(name, "", null), new ContentTemplateNameComparer());
|
||||
|
||||
if(templates.Count > 0 && index >= 0) return templates[index];
|
||||
else return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new content template.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the template.</param>
|
||||
/// <param name="content">The content of the template.</param>
|
||||
/// <param name="provider">The target provider (<c>null</c> for the default provider).</param>
|
||||
/// <returns><c>true</c> if the template is added, <c>false</c> otherwise.</returns>
|
||||
public static bool AddTemplate(string name, string content, IPagesStorageProviderV30 provider) {
|
||||
if(Find(name) != null) return false;
|
||||
|
||||
if(provider == null) provider = Collectors.PagesProviderCollector.GetProvider(Settings.DefaultPagesProvider);
|
||||
|
||||
ContentTemplate result = provider.AddContentTemplate(name, content);
|
||||
|
||||
if(result != null) Log.LogEntry("Content Template " + name + " created", EntryType.General, Log.SystemUsername);
|
||||
else Log.LogEntry("Creation failed for Content Template " + name, EntryType.Error, Log.SystemUsername);
|
||||
|
||||
return result != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a content template.
|
||||
/// </summary>
|
||||
/// <param name="template">The template to remove.</param>
|
||||
/// <returns><c>true</c> if the template is removed, <c>false</c> otherwise.</returns>
|
||||
public static bool RemoveTemplate(ContentTemplate template) {
|
||||
bool done = template.Provider.RemoveContentTemplate(template.Name);
|
||||
|
||||
if(done) Log.LogEntry("Content Template " + template.Name + " deleted", EntryType.General, Log.SystemUsername);
|
||||
else Log.LogEntry("Deletion failed for Content Template " + template.Name, EntryType.Error, Log.SystemUsername);
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies a content template.
|
||||
/// </summary>
|
||||
/// <param name="template">The template to modify.</param>
|
||||
/// <param name="content">The new content of the template.</param>
|
||||
/// <returns><c>true</c> if the template is modified, <c>false</c> otherwise.</returns>
|
||||
public static bool ModifyTemplate(ContentTemplate template, string content) {
|
||||
ContentTemplate result = template.Provider.ModifyContentTemplate(template.Name, content);
|
||||
|
||||
if(result != null) Log.LogEntry("Content Template " + template.Name + " updated", EntryType.General, Log.SystemUsername);
|
||||
else Log.LogEntry("Update failed for Content Template " + template.Name, EntryType.Error, Log.SystemUsername);
|
||||
|
||||
return result != null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
573
Core/Tools.cs
Normal file
573
Core/Tools.cs
Normal file
|
@ -0,0 +1,573 @@
|
|||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
308
Core/Translator.cs
Normal file
308
Core/Translator.cs
Normal file
|
@ -0,0 +1,308 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ScrewTurn.Wiki.ImportWiki {
|
||||
|
||||
/// <summary>
|
||||
/// Implements a translator tool for importing MediaWiki data.
|
||||
/// </summary>
|
||||
public class Translator : ScrewTurn.Wiki.ImportWiki.ITranslator {
|
||||
|
||||
private Regex noWiki = new Regex(@"<nowiki>(.|\n|\r)*?</nowiki>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Executes the translation.
|
||||
/// </summary>
|
||||
/// <param name="input">The input content.</param>
|
||||
/// <returns>The WikiMarkup.</returns>
|
||||
public string Translate(string input) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append(input);
|
||||
|
||||
Regex firsttitle = new Regex(@"(\={2,5}).+?\1");
|
||||
Regex doubleSquare = new Regex(@"\[{2}.+?\]{2}");
|
||||
Regex exlink = new Regex(@"(\[(\w+):\/\/([^\]]+)\])|(?<!\"")((?<Protocol>\w+):\/\/(?<Domain>[\w.]+\/?)\S*)(?!\"")");
|
||||
Regex mailto = new Regex(@"(\[mailto\:([\w\d\.\@]+)\])|(?<!\"")(mailto\:([\w\d\.\@]+))(?!\"")");
|
||||
Regex image = new Regex(@"\[Image\:.+?\]");
|
||||
Regex pre = new Regex(@"<pre>((.|\n|\r)*?</pre>)?");
|
||||
Regex newlinespace = new Regex(@"^\ .+", RegexOptions.Multiline);
|
||||
Regex transclusion = new Regex(@"(\{{2})(.|\n)+?(\}{2})");
|
||||
Regex math = new Regex(@"<math>(.|\n|\r)*?</math>");
|
||||
Regex references = new Regex(@"<ref>(.|\n|\r)*?</ref>");
|
||||
Regex references1 = new Regex(@"\<references\/\>");
|
||||
Regex redirect = new Regex(@"\#REDIRECT\ \[{2}.+\]{2}");
|
||||
Match match;
|
||||
|
||||
List<int> noWikiBegin = new List<int>(), noWikiEnd = new List<int>();
|
||||
|
||||
/*ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
|
||||
if (sb.ToString().Contains("__NOTOC__"))
|
||||
{
|
||||
sb.Remove(sb.ToString().IndexOf("__NOTOC__"), 9);
|
||||
if (sb.ToString().Contains("{{compactTOC}}"))
|
||||
{
|
||||
sb.Remove(sb.ToString().IndexOf("{{compactTOC}}"),14);
|
||||
sb.Insert(sb.ToString().IndexOf("{{compactTOC}}"), "\n{TOC}\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
match = firsttitle.Match(sb.ToString());
|
||||
if(match.Success)
|
||||
sb.Insert(match.Index, "\n{TOC}\n");
|
||||
}*/
|
||||
|
||||
sb.Replace("\r", "");
|
||||
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
|
||||
///////////////////////////////////////////////
|
||||
//////BEGIN of unsupported formatting-tag//////
|
||||
///////////////////////////////////////////////
|
||||
match = math.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end)) {
|
||||
sb.Replace(match.Value, @"<span style=""background-color:red; color:white""><nowiki><esc><math>" + match.Value.Substring(6, match.Length - 13) + @"</math></esc></nowiki></span> ");
|
||||
match = math.Match(sb.ToString());
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
}
|
||||
else
|
||||
match = math.Match(sb.ToString(), end);
|
||||
}
|
||||
|
||||
match = references.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end)) {
|
||||
sb.Replace(match.Value, @"<span style=""background-color:red; color:white""><nowiki><esc><ref>" + match.Value.Substring(5, match.Length - 11) + @"</ref></esc></nowiki></span> ");
|
||||
match = references.Match(sb.ToString());
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
}
|
||||
else
|
||||
match = references.Match(sb.ToString(), end);
|
||||
}
|
||||
|
||||
match = references1.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end)) {
|
||||
sb.Replace(match.Value, @"<span style=""background-color:red; color:white""><nowiki><esc><references/></esc></nowiki></span> ");
|
||||
match = references1.Match(sb.ToString());
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
}
|
||||
else
|
||||
match = references1.Match(sb.ToString(), end);
|
||||
}
|
||||
|
||||
match = redirect.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end)) {
|
||||
sb.Replace(match.Value, @"<span style=""background-color:red; color:white""><nowiki><esc>" + match.Value + @"</esc></nowiki></span> ");
|
||||
match = redirect.Match(sb.ToString());
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
}
|
||||
else
|
||||
match = redirect.Match(sb.ToString(), end);
|
||||
}
|
||||
|
||||
match = transclusion.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end)) {
|
||||
string s = @"<span style=""background-color:red; color:white""><nowiki>" + match.Value + @"</nowiki></span> ";
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, s);
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
match = transclusion.Match(sb.ToString(), match.Index + s.Length);
|
||||
}
|
||||
match = transclusion.Match(sb.ToString(), end);
|
||||
}
|
||||
//////////////////////////////////////////
|
||||
/////END of unsupported formetter-tag/////
|
||||
//////////////////////////////////////////
|
||||
|
||||
match = pre.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end)) {
|
||||
string s = "{{{{<nowiki>" + match.Value.Replace("<pre>", "").Replace("</pre>", "") + @"</nowiki>}}}}";
|
||||
sb.Replace(match.Value, s);
|
||||
match = pre.Match(sb.ToString(), match.Index + s.Length);
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
}
|
||||
else
|
||||
match = pre.Match(sb.ToString(), end);
|
||||
}
|
||||
|
||||
match = exlink.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end)) {
|
||||
if(match.Value[0] == '[') {
|
||||
string[] split = match.Value.Split(new char[] { ' ' }, 2);
|
||||
if(split.Length == 2) {
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, split[0] + "|" + split[1]);
|
||||
match = exlink.Match(sb.ToString(), match.Index + match.Length);
|
||||
}
|
||||
else
|
||||
match = exlink.Match(sb.ToString(), match.Index + match.Length);
|
||||
}
|
||||
else {
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, "[" + match.Value + "]");
|
||||
match = exlink.Match(sb.ToString(), match.Index + match.Length + 1);
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
}
|
||||
}
|
||||
else
|
||||
match = exlink.Match(sb.ToString(), end);
|
||||
}
|
||||
|
||||
match = mailto.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end)) {
|
||||
string str = match.Value.Replace(@"mailto:", "");
|
||||
if(str[0] == '[') {
|
||||
string[] split = str.Split(new char[] { ' ' }, 2);
|
||||
if(split.Length == 2) {
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Length, split[0] + "|" + split[1]);
|
||||
}
|
||||
/*else
|
||||
{
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, split[0]);
|
||||
}*/
|
||||
match = mailto.Match(sb.ToString(), match.Index + str.Length);
|
||||
}
|
||||
else {
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, "[" + str.Substring(0, str.Length) + "]");
|
||||
match = mailto.Match(sb.ToString(), match.Index + str.Length + 1);
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
}
|
||||
}
|
||||
else
|
||||
match = mailto.Match(sb.ToString(), end);
|
||||
}
|
||||
|
||||
match = image.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end)) {
|
||||
string str = match.Value.Remove(0, 7);
|
||||
str = str.Remove(str.Length - 1);
|
||||
|
||||
string name = "";
|
||||
string location = "";
|
||||
string caption = "";
|
||||
string[] splits = str.Split(new char[] { '|' });
|
||||
name = splits[0];
|
||||
for(int i = 0; i < splits.Length; i++) {
|
||||
if(splits[i] == "right" || splits[i] == "left" || splits[i] == "center" || splits[i] == "none")
|
||||
location = splits[i];
|
||||
else if(splits[i].Contains("px")) { }
|
||||
else if(splits[i] == "thumb" || splits[i] == "thumbnail" || splits[i] == "frame") { }
|
||||
else
|
||||
caption = splits[i] + "|";
|
||||
}
|
||||
if(location == "right" || location == "left") {
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, "[image" + location + "|" + caption + "{UP}" + name + "]");
|
||||
}
|
||||
else {
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, "[imageauto|" + caption + "{UP}" + name + "]");
|
||||
}
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
}
|
||||
match = image.Match(sb.ToString(), end);
|
||||
}
|
||||
|
||||
match = doubleSquare.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end)) {
|
||||
if(match.Value.Contains(":")) {
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, match.Value.Replace(':', '_').Replace("/", "_").Replace(@"\", "_").Replace('?', '_'));
|
||||
}
|
||||
else {
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, match.Value.Substring(1, match.Length - 2).Replace("/", "_").Replace(@"\", "_").Replace('?', '_'));
|
||||
}
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
}
|
||||
match = doubleSquare.Match(sb.ToString(), end);
|
||||
}
|
||||
|
||||
bool first = true;
|
||||
match = newlinespace.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end)) {
|
||||
string s = "";
|
||||
if(first)
|
||||
s += "{{{{" + match.Value.Substring(1, match.Value.Length - 1);
|
||||
else
|
||||
s += match.Value.Substring(1, match.Value.Length - 1);
|
||||
if(sb.Length > match.Index + match.Length + 1) {
|
||||
if(sb[match.Index + match.Length] == '\n' && sb[match.Index + match.Length + 1] == ' ') {
|
||||
first = false;
|
||||
}
|
||||
else {
|
||||
s += "}}}}";
|
||||
first = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
s += "}}}}";
|
||||
first = true;
|
||||
}
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, s);
|
||||
match = newlinespace.Match(sb.ToString(), match.Index + s.Length);
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
}
|
||||
else
|
||||
match = newlinespace.Match(sb.ToString(), end);
|
||||
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private void ComputeNoWiki(string text, ref List<int> noWikiBegin, ref List<int> noWikiEnd) {
|
||||
Match match;
|
||||
noWikiBegin.Clear();
|
||||
noWikiEnd.Clear();
|
||||
|
||||
match = noWiki.Match(text);
|
||||
while(match.Success) {
|
||||
noWikiBegin.Add(match.Index);
|
||||
noWikiEnd.Add(match.Index + match.Length - 1);
|
||||
match = noWiki.Match(text, match.Index + match.Length);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsNoWikied(int index, List<int> noWikiBegin, List<int> noWikiEnd, out int end) {
|
||||
for(int i = 0; i < noWikiBegin.Count; i++) {
|
||||
if(index >= noWikiBegin[i] && index <= noWikiEnd[i]) {
|
||||
end = noWikiEnd[i];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
end = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
419
Core/TranslatorFlex.cs
Normal file
419
Core/TranslatorFlex.cs
Normal file
|
@ -0,0 +1,419 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ScrewTurn.Wiki.ImportWiki {
|
||||
|
||||
/// <summary>
|
||||
/// Implements a translator tool for importing FlexWiki data.
|
||||
/// </summary>
|
||||
public class TranslatorFlex : ScrewTurn.Wiki.ImportWiki.ITranslator {
|
||||
|
||||
private Regex noWiki = new Regex(@"\<nowiki\>(.|\s)+?\<\/nowiki\>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private Regex noFlex = new Regex(@"\<noflex\>(.|\s)+?\<\/noflex\>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Executes the translation.
|
||||
/// </summary>
|
||||
/// <param name="input">The input content.</param>
|
||||
/// <returns>The WikiMarkup.</returns>
|
||||
public string Translate(string input) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append(input);
|
||||
List<int> noWikiBegin = new List<int>(), noWikiEnd = new List<int>(), noFlexBegin = new List<int>(), noFlexEnd = new List<int>();
|
||||
|
||||
Match match;
|
||||
Regex doubleDoubleBrackets = new Regex(@"\""{2}.+?\""{2}");
|
||||
Regex italic = new Regex(@"_.+?_");
|
||||
Regex bold = new Regex(@"\*.+?\*");
|
||||
Regex underline = new Regex(@"\+.+?\+");
|
||||
Regex strikethrough = new Regex(@"\-.*?\-");
|
||||
Regex head = new Regex(@"^!{1,6}.+", RegexOptions.Multiline);
|
||||
Regex wikiTalk = new Regex(@"@@.+?@@");
|
||||
Regex code = new Regex(@"@.+?@");
|
||||
Regex pascalCase = new Regex(@"(\""[^""]+?\""\:)?([A-Z][a-z]+){2,}(\.([A-Z][a-z]+){2,})?");
|
||||
Regex camelCase = new Regex(@"(\""[^""]+?\""\:)?(([A-Z][a-z]+){2,}\.)?\[.+?\]");
|
||||
Regex exlink = new Regex(@"(\""[^""]+?\""\:)?(?<Protocol>\w+):\/\/(?<Domain>[\w.]+\/?)\S*");
|
||||
Regex email = new Regex(@"(\""[^""]+?\""\:)?mailto\:.+");
|
||||
Regex lists = new Regex(@"^(\t|\s{8})+(\*|(1\.))", RegexOptions.Multiline);
|
||||
Regex table = new Regex(@"^\|{2}", RegexOptions.Multiline);
|
||||
Regex newlinespace = new Regex(@"^\ .+", RegexOptions.Multiline);
|
||||
|
||||
sb.Replace("\r", "");
|
||||
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
|
||||
match = doubleDoubleBrackets.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end))
|
||||
match = doubleDoubleBrackets.Match(sb.ToString(), end);
|
||||
else {
|
||||
string s = "<nowiki>" + match.Value.Substring(2, match.Length - 4) + "</nowiki>";
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, s);
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
match = doubleDoubleBrackets.Match(sb.ToString(), match.Index + s.Length);
|
||||
}
|
||||
}
|
||||
|
||||
match = exlink.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end))
|
||||
match = exlink.Match(sb.ToString(), end);
|
||||
else {
|
||||
string s;
|
||||
string[] split = match.Value.Split(new char[] { '"' });
|
||||
if(split.Length == 1) {
|
||||
if(match.Value.EndsWith(".jpeg", StringComparison.CurrentCultureIgnoreCase) | match.Value.EndsWith(".gif", StringComparison.CurrentCultureIgnoreCase) | match.Value.EndsWith(".jpg", StringComparison.CurrentCultureIgnoreCase)) {
|
||||
string imgName = match.Value.Substring(match.Value.LastIndexOf('/') + 1, match.Length - match.Value.LastIndexOf('/') - 1);
|
||||
s = "<noflex>[image|" + imgName + "|{UP}" + imgName + "]</noflex>";
|
||||
}
|
||||
else s = "[" + match.Value + "]";
|
||||
}
|
||||
else {
|
||||
if(split[1].EndsWith(".jpeg", StringComparison.CurrentCultureIgnoreCase) | split[1].EndsWith(".gif", StringComparison.CurrentCultureIgnoreCase) | split[1].EndsWith(".jpg", StringComparison.CurrentCultureIgnoreCase)) {
|
||||
string imgName = split[1].Substring(split[1].LastIndexOf('/') + 1, split[1].Length - split[1].LastIndexOf('/') - 1);
|
||||
s = "<noflex>[image|" + imgName + "|{UP}" + imgName + "|" + split[2].Substring(1, split[2].Length - 1) + "]</noflex>";
|
||||
}
|
||||
else s = "<noflex>[" + split[2].Substring(1, split[2].Length - 1) + "|" + split[1] + "]</noflex>";
|
||||
}
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, s);
|
||||
ComputeNoFlex(sb.ToString(), ref noFlexBegin, ref noFlexEnd);
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
match = exlink.Match(sb.ToString(), match.Index + s.Length);
|
||||
}
|
||||
}
|
||||
|
||||
match = email.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end))
|
||||
match = email.Match(sb.ToString(), end);
|
||||
else {
|
||||
string mailLink = "";
|
||||
if(match.Value.StartsWith("mailto:")) mailLink = "[" + match.Value.Replace("mailto:", "") + "]";
|
||||
else mailLink = "<noflex>[" + match.Value.Split(new char[] { ':' }, 2)[1].Replace("mailto:", "") + "|" + match.Value.Split(new char[] { ':' }, 2)[0].Substring(1, match.Value.Split(new char[] { ':' }, 2)[0].Length - 2) + "]</noflex>";
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, mailLink);
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
match = email.Match(sb.ToString(), match.Index + mailLink.Length);
|
||||
}
|
||||
}
|
||||
|
||||
match = bold.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end))
|
||||
match = bold.Match(sb.ToString(), end);
|
||||
else {
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, "'''" + match.Value.Substring(1, match.Length - 2) + @"'''");
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
match = bold.Match(sb.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
match = underline.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end))
|
||||
match = underline.Match(sb.ToString(), end);
|
||||
else {
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, "__" + match.Value.Substring(1, match.Length - 2) + @"__");
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
match = underline.Match(sb.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
match = strikethrough.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end))
|
||||
match = strikethrough.Match(sb.ToString(), end);
|
||||
else {
|
||||
string s = "";
|
||||
if(match.Value == "---") s = "---";
|
||||
else if(match.Value == "--") s = "-";
|
||||
else s = "--" + match.Value.Substring(1, match.Length - 2) + @"--";
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, s);
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
match = strikethrough.Match(sb.ToString(), match.Index + s.Length);
|
||||
}
|
||||
}
|
||||
|
||||
match = head.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end))
|
||||
match = head.Match(sb.ToString(), end);
|
||||
else {
|
||||
//Count the number of ! in order to put a corresponding
|
||||
//number of =
|
||||
int count = 1;
|
||||
for(int i = 0; i < match.Length; i++) {
|
||||
if(match.Value[i] == '!')
|
||||
count++;
|
||||
}
|
||||
if(match.Value.EndsWith("!")) count--;
|
||||
string s = "";
|
||||
for(int i = 0; i < count; i++)
|
||||
s += "=";
|
||||
s += match.Value.Substring(count - 1, match.Length - (count - 1) - 1);
|
||||
for(int i = 0; i < count; i++)
|
||||
s += "=";
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, s);
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
match = head.Match(sb.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
match = wikiTalk.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noFlexBegin, noFlexEnd, out end))
|
||||
match = wikiTalk.Match(sb.ToString(), end);
|
||||
else {
|
||||
string s = @"<span style=""background-color:red; color:white""><nowiki>" + match.Value + @"</nowiki></span>";
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, s);
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
match = wikiTalk.Match(sb.ToString(), match.Index + s.Length);
|
||||
}
|
||||
}
|
||||
|
||||
match = code.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end))
|
||||
match = code.Match(sb.ToString(), end);
|
||||
else {
|
||||
string s = "{{<nowiki>" + match.Value.Substring(1, match.Length - 2) + @"</nowiki>}}";
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, s);
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
match = code.Match(sb.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
match = camelCase.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end))
|
||||
match = camelCase.Match(sb.ToString(), end);
|
||||
else {
|
||||
string s;
|
||||
string[] split = match.Value.Split(new char[] { ':' }, 2);
|
||||
if(split.Length == 1) {
|
||||
string[] split1 = match.Value.Split(new char[] { '.' }, 2);
|
||||
if(split1.Length == 1) s = match.Value;
|
||||
else s = split1[1];
|
||||
}
|
||||
else {
|
||||
string[] split1 = split[1].Split(new char[] { '.' }, 2);
|
||||
if(split1.Length == 1) s = "[" + split[1].Substring(1, split[1].Length - 2) + "|" + split[0].Substring(1, split[0].Length - 2) + "]";
|
||||
else s = "[" + split1[1].Substring(0, split1[1].Length - 1) + "|" + split[0].Substring(1, split[0].Length - 2) + "]";
|
||||
}
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, s);
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
match = camelCase.Match(sb.ToString(), match.Index + s.Length);
|
||||
}
|
||||
}
|
||||
|
||||
match = pascalCase.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end))
|
||||
match = pascalCase.Match(sb.ToString(), end);
|
||||
else {
|
||||
string s;
|
||||
string[] split = match.Value.Split(new char[] { ':' }, 2);
|
||||
if(split.Length == 1) {
|
||||
string[] split1 = match.Value.Split(new char[] { '.' }, 2);
|
||||
if(split1.Length == 1) s = "[" + match.Value + "]";
|
||||
else s = "[" + split1[1] + "]";
|
||||
}
|
||||
else {
|
||||
string[] split1 = split[1].Split(new char[] { '.' });
|
||||
if(split1.Length == 1) s = "[" + split[1] + "|" + split[0].Substring(1, split[0].Length - 2) + "]";
|
||||
else s = "[" + split1[1] + "|" + split[0].Substring(1, split[0].Length - 2) + "]";
|
||||
}
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, s);
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
match = pascalCase.Match(sb.ToString(), match.Index + s.Length);
|
||||
}
|
||||
}
|
||||
|
||||
match = lists.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end))
|
||||
match = lists.Match(sb.ToString(), end);
|
||||
else {
|
||||
string s = "";
|
||||
char kindOfList;
|
||||
int i = 0;
|
||||
char[] ca = match.Value.ToCharArray();
|
||||
while(ca[i] == ' ')
|
||||
i++;
|
||||
if(i > 0) {
|
||||
kindOfList = ca[i];
|
||||
i = i / 8;
|
||||
}
|
||||
else {
|
||||
while(ca[i] == '\t')
|
||||
i++;
|
||||
kindOfList = ca[i];
|
||||
}
|
||||
for(int k = 1; k <= i; k++) {
|
||||
if(kindOfList == '*') s += "*";
|
||||
if(kindOfList == '1') s += "#";
|
||||
}
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, s);
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
match = lists.Match(sb.ToString(), match.Index + s.Length);
|
||||
}
|
||||
}
|
||||
|
||||
bool tableBegin = true;
|
||||
match = table.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end))
|
||||
match = table.Match(sb.ToString(), end);
|
||||
else {
|
||||
string s = "";
|
||||
int indexEndOfLine = sb.ToString().IndexOf("\r\n", match.Index);
|
||||
if(indexEndOfLine < 0) indexEndOfLine = sb.Length;
|
||||
string[] split = sb.ToString().Substring(match.Index + 2, indexEndOfLine - match.Index - 4).Split(new string[] { "||" }, StringSplitOptions.None);
|
||||
if(tableBegin) s = "{|\r\n| " + split[0];
|
||||
else s = "|-\r\n| " + split[0];
|
||||
for(int i = 1; i < split.Length; i++)
|
||||
s += " || " + split[i];
|
||||
if(indexEndOfLine != sb.Length) {
|
||||
if(sb.ToString().Substring(indexEndOfLine + 2, 2) == "||") tableBegin = false;
|
||||
else {
|
||||
tableBegin = true;
|
||||
s += "\r\n|}";
|
||||
}
|
||||
}
|
||||
else {
|
||||
tableBegin = true;
|
||||
s += "\r\n|}";
|
||||
}
|
||||
sb.Remove(match.Index, indexEndOfLine - match.Index);
|
||||
sb.Insert(match.Index, s);
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
match = table.Match(sb.ToString(), match.Index + s.Length);
|
||||
}
|
||||
}
|
||||
|
||||
match = italic.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end))
|
||||
match = italic.Match(sb.ToString(), end);
|
||||
else {
|
||||
if(IsNoFlexed(match.Index, noFlexBegin, noFlexEnd, out end))
|
||||
match = italic.Match(sb.ToString(), end);
|
||||
else {
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, "''" + match.Value.Substring(1, match.Length - 2) + @"''");
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
match = italic.Match(sb.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool first = true;
|
||||
match = newlinespace.Match(sb.ToString());
|
||||
while(match.Success) {
|
||||
int end;
|
||||
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end))
|
||||
match = newlinespace.Match(sb.ToString(), end);
|
||||
else {
|
||||
string s = "";
|
||||
if(first)
|
||||
s += "{{{{" + match.Value.Substring(1, match.Value.Length - 1);
|
||||
else
|
||||
s += match.Value.Substring(1, match.Value.Length - 1);
|
||||
if(sb.Length > match.Index + match.Length + 1) {
|
||||
if(sb[match.Index + match.Length] == '\n' && sb[match.Index + match.Length + 1] == ' ') {
|
||||
first = false;
|
||||
}
|
||||
else {
|
||||
s += "}}}}";
|
||||
first = true;
|
||||
}
|
||||
}
|
||||
sb.Remove(match.Index, match.Length);
|
||||
sb.Insert(match.Index, s);
|
||||
match = newlinespace.Match(sb.ToString(), match.Index + s.Length);
|
||||
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return sb.ToString().Replace("<noflex>", "").Replace("</noflex>", "");
|
||||
}
|
||||
|
||||
private void ComputeNoWiki(string text, ref List<int> noWikiBegin, ref List<int> noWikiEnd) {
|
||||
Match match;
|
||||
noWikiBegin.Clear();
|
||||
noWikiEnd.Clear();
|
||||
|
||||
match = noWiki.Match(text);
|
||||
while(match.Success) {
|
||||
noWikiBegin.Add(match.Index);
|
||||
noWikiEnd.Add(match.Index + match.Length - 1);
|
||||
match = noWiki.Match(text, match.Index + match.Length);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsNoWikied(int index, List<int> noWikiBegin, List<int> noWikiEnd, out int end) {
|
||||
for(int i = 0; i < noWikiBegin.Count; i++) {
|
||||
if(index >= noWikiBegin[i] && index <= noWikiEnd[i]) {
|
||||
end = noWikiEnd[i];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
end = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void ComputeNoFlex(string text, ref List<int> noFlexBegin, ref List<int> noFlexEnd) {
|
||||
Match match;
|
||||
noFlexBegin.Clear();
|
||||
noFlexEnd.Clear();
|
||||
|
||||
match = noFlex.Match(text);
|
||||
while(match.Success) {
|
||||
noFlexBegin.Add(match.Index);
|
||||
noFlexEnd.Add(match.Index + match.Length - 1);
|
||||
match = noFlex.Match(text, match.Index + match.Length);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsNoFlexed(int index, List<int> noFlexBegin, List<int> noFlexEnd, out int end) {
|
||||
for(int i = 0; i < noFlexBegin.Count; i++) {
|
||||
if(index >= noFlexBegin[i] && index <= noFlexEnd[i]) {
|
||||
end = noFlexEnd[i];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
end = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
145
Core/UrlTools.cs
Normal file
145
Core/UrlTools.cs
Normal file
|
@ -0,0 +1,145 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Web;
|
||||
using System.Text;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Implements useful URL-handling tools.
|
||||
/// </summary>
|
||||
public static class UrlTools {
|
||||
|
||||
/// <summary>
|
||||
/// Properly routes the current virtual request to a physical ASP.NET page.
|
||||
/// </summary>
|
||||
public static void RouteCurrentRequest() {
|
||||
// Extract the physical page name, e.g. MainPage, Edit or Category
|
||||
string pageName = Path.GetFileNameWithoutExtension(HttpContext.Current.Request.PhysicalPath);
|
||||
// Exctract the extension, e.g. .ashx or .aspx
|
||||
string ext = Path.GetExtension(HttpContext.Current.Request.PhysicalPath).ToLowerInvariant();
|
||||
// Remove trailing dot, .ashx -> ashx
|
||||
if(ext.Length > 0) ext = ext.Substring(1);
|
||||
|
||||
// IIS7+Integrated Pipeline handles all requests through the ASP.NET engine
|
||||
// All non-interesting files are not processed, such as GIF, CSS, etc.
|
||||
if(ext != "ashx" && ext != "aspx") return;
|
||||
|
||||
// Extract the current namespace, if any
|
||||
string nspace = GetCurrentNamespace();
|
||||
if(!string.IsNullOrEmpty(nspace)) pageName = pageName.Substring(nspace.Length + 1); // Trim Namespace. from pageName
|
||||
|
||||
//HttpContext.Current.Response.Filter =
|
||||
// new ScrewTurn.Wiki.RelativeUrlFilterStream(HttpContext.Current.Response.Filter, nspace);
|
||||
|
||||
string queryString = ""; // Empty or begins with ampersand, not question mark
|
||||
try {
|
||||
// This might throw exceptions if 3rd-party modules interfer with the request pipeline
|
||||
queryString = HttpContext.Current.Request.Url.Query.Replace("?", "&");
|
||||
}
|
||||
catch { }
|
||||
|
||||
if(ext.Equals("ashx")) {
|
||||
// Content page requested, process it via Default.aspx
|
||||
if(!queryString.Contains("NS=")) {
|
||||
HttpContext.Current.RewritePath("~/Default.aspx?Page=" + Tools.UrlEncode(pageName) + "&NS=" + Tools.UrlEncode(nspace) + queryString);
|
||||
}
|
||||
else {
|
||||
HttpContext.Current.RewritePath("~/Default.aspx?Page=" + Tools.UrlEncode(pageName) + queryString);
|
||||
}
|
||||
}
|
||||
else if(ext.Equals("aspx")) {
|
||||
// System page requested, redirect to the root of the application
|
||||
// For example: http://www.server.com/Namespace.Edit.aspx?Page=MainPage -> http://www.server.com/Edit.aspx?Page=MainPage&NS=Namespace
|
||||
if(!string.IsNullOrEmpty(nspace)) { // Needed to avoid infinite loops
|
||||
if(!queryString.Contains("NS=")) {
|
||||
HttpContext.Current.RewritePath("~/" + Tools.UrlEncode(pageName) + "." + ext + "?NS=" + Tools.UrlEncode(nspace) + queryString);
|
||||
}
|
||||
else {
|
||||
if(queryString.Length > 1) queryString = "?" + queryString.Substring(1);
|
||||
HttpContext.Current.RewritePath("~/" + Tools.UrlEncode(pageName) + "." + ext + queryString);
|
||||
}
|
||||
}
|
||||
}
|
||||
// else nothing to do
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the current namespace from the URL, such as <i>/App/Namespace.Edit.aspx</i>.
|
||||
/// </summary>
|
||||
/// <returns>The current namespace, or an empty string. <c>null</c> if the URL format is not specified.</returns>
|
||||
public static string GetCurrentNamespace() {
|
||||
string filename = Path.GetFileNameWithoutExtension(HttpContext.Current.Request.Path); // e.g. MainPage or Edit or Namespace.MainPage or Namespace.Edit
|
||||
|
||||
// Use dot to split the filename
|
||||
string[] fields = filename.Split('.');
|
||||
|
||||
if(fields.Length != 1 && fields.Length != 2) return null; // Unrecognized format
|
||||
if(fields.Length == 1) return ""; // Just page name
|
||||
else return fields[0]; // Namespace.Page
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Redirects the current response to the specified URL, properly appending the current namespace if any.
|
||||
/// </summary>
|
||||
/// <param name="target">The target URL.</param>
|
||||
public static void Redirect(string target) {
|
||||
string nspace = HttpContext.Current.Request["NS"];
|
||||
if(nspace == null || nspace.Length == 0) HttpContext.Current.Response.Redirect(target);
|
||||
else HttpContext.Current.Response.Redirect(target + (target.Contains("?") ? "&" : "?") + "NS=" + Tools.UrlEncode(nspace));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a URL properly prepending the namespace to the URL.
|
||||
/// </summary>
|
||||
/// <param name="chunks">The chunks used to build the URL.</param>
|
||||
/// <returns>The complete URL.</returns>
|
||||
public static string BuildUrl(params string[] chunks) {
|
||||
if(chunks == null) throw new ArgumentNullException("chunks");
|
||||
if(chunks.Length == 0) return ""; // Shortcut
|
||||
|
||||
StringBuilder temp = new StringBuilder(chunks.Length * 10);
|
||||
foreach(string chunk in chunks) {
|
||||
temp.Append(chunk);
|
||||
}
|
||||
|
||||
string tempString = temp.ToString();
|
||||
|
||||
if(tempString.StartsWith("++")) return tempString.Substring(2);
|
||||
|
||||
string nspace = HttpContext.Current.Request["NS"];
|
||||
if(string.IsNullOrEmpty(nspace)) nspace = null;
|
||||
if(nspace == null) nspace = GetCurrentNamespace();
|
||||
if(string.IsNullOrEmpty(nspace)) nspace = null;
|
||||
|
||||
if(nspace != null) {
|
||||
string tempStringLower = tempString.ToLowerInvariant();
|
||||
if((tempStringLower.Contains(".ashx") || tempStringLower.Contains(".aspx")) && !tempString.StartsWith(Tools.UrlEncode(nspace) + ".")) temp.Insert(0, nspace + ".");
|
||||
}
|
||||
|
||||
return temp.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a URL properly appendind the <b>NS</b> parameter if appropriate.
|
||||
/// </summary>
|
||||
/// <param name="destination">The destination <see cref="T:StringBuilder"/>.</param>
|
||||
/// <param name="chunks">The chunks to append.</param>
|
||||
public static void BuildUrl(StringBuilder destination, params string[] chunks) {
|
||||
if(destination == null) throw new ArgumentNullException("destination");
|
||||
|
||||
destination.Append(BuildUrl(chunks));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Redirects to the default page of the current namespace.
|
||||
/// </summary>
|
||||
public static void RedirectHome() {
|
||||
Redirect(BuildUrl(Settings.DefaultPage, Settings.PageExtension));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
921
Core/Users.cs
Normal file
921
Core/Users.cs
Normal file
|
@ -0,0 +1,921 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
using System.Web;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Manages all the User Accounts data.
|
||||
/// </summary>
|
||||
public static class Users {
|
||||
|
||||
private static UserInfo adminAccount = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the built-in administrator account.
|
||||
/// </summary>
|
||||
/// <returns>The account.</returns>
|
||||
public static UserInfo GetAdministratorAccount() {
|
||||
if(adminAccount == null) {
|
||||
adminAccount = new UserInfo("admin", "Administrator", Settings.ContactEmail, true, DateTime.MinValue, null);
|
||||
adminAccount.Groups = new string[] { Settings.AdministratorsGroup };
|
||||
}
|
||||
|
||||
return adminAccount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The user data key pointing to page changes notification entries.
|
||||
/// </summary>
|
||||
private const string PageChangesKey = "PageChanges";
|
||||
|
||||
/// <summary>
|
||||
/// The user data key pointing to discussion messages notification entries.
|
||||
/// </summary>
|
||||
private const string DiscussionMessagesKey = "DiscussionMessages";
|
||||
|
||||
/// <summary>
|
||||
/// The user data key pointing to page changes notification entries for whole namespace.
|
||||
/// </summary>
|
||||
private const string NamespacePageChangesKey = "NamespacePageChanges";
|
||||
|
||||
/// <summary>
|
||||
/// The user data key pointing to discussion messages notification entries for whole namespaces.
|
||||
/// </summary>
|
||||
private const string NamespaceDiscussionMessagesKey = "NamespaceDiscussionMessages";
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the Users that the providers declare to manage.
|
||||
/// </summary>
|
||||
/// <returns>The users, sorted by username.</returns>
|
||||
public static List<UserInfo> GetUsers() {
|
||||
List<UserInfo> allUsers = new List<UserInfo>(1000);
|
||||
|
||||
// Retrieve all the users from the Users Providers
|
||||
int count = 0;
|
||||
foreach(IUsersStorageProviderV30 provider in Collectors.UsersProviderCollector.AllProviders) {
|
||||
count++;
|
||||
allUsers.AddRange(provider.GetUsers());
|
||||
}
|
||||
|
||||
if(count > 1) {
|
||||
allUsers.Sort(new UsernameComparer());
|
||||
}
|
||||
|
||||
return allUsers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a user.
|
||||
/// </summary>
|
||||
/// <param name="username">The username.</param>
|
||||
/// <returns>The user, or <c>null</c>.</returns>
|
||||
public static UserInfo FindUser(string username) {
|
||||
if(username == "admin") return GetAdministratorAccount();
|
||||
|
||||
// Try default provider first
|
||||
IUsersStorageProviderV30 defaultProvider = Collectors.UsersProviderCollector.GetProvider(Settings.DefaultUsersProvider);
|
||||
UserInfo temp = defaultProvider.GetUser(username);
|
||||
if(temp != null) return temp;
|
||||
|
||||
// The try other providers
|
||||
temp = null;
|
||||
IUsersStorageProviderV30[] providers = Collectors.UsersProviderCollector.AllProviders;
|
||||
foreach(IUsersStorageProviderV30 p in providers) {
|
||||
IUsersStorageProviderV30 extProv = p as IUsersStorageProviderV30;
|
||||
if(extProv != null && extProv != defaultProvider) {
|
||||
temp = extProv.GetUser(username);
|
||||
if(temp != null) return temp;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a user by email.
|
||||
/// </summary>
|
||||
/// <param name="email">The email address.</param>
|
||||
/// <returns>The user, or <c>null</c>.</returns>
|
||||
public static UserInfo FindUserByEmail(string email) {
|
||||
// Try default provider first
|
||||
IUsersStorageProviderV30 defaultProvider = Collectors.UsersProviderCollector.GetProvider(Settings.DefaultUsersProvider);
|
||||
UserInfo temp = defaultProvider.GetUserByEmail(email);
|
||||
if(temp != null) return temp;
|
||||
|
||||
// The try other providers
|
||||
temp = null;
|
||||
IUsersStorageProviderV30[] providers = Collectors.UsersProviderCollector.AllProviders;
|
||||
foreach(IUsersStorageProviderV30 p in providers) {
|
||||
IUsersStorageProviderV30 extProv = p as IUsersStorageProviderV30;
|
||||
if(extProv != null && extProv != defaultProvider) {
|
||||
temp = extProv.GetUserByEmail(email);
|
||||
if(temp != null) return temp;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets user data.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="key">The data key.</param>
|
||||
/// <returns>The data, or <c>null</c> if either the user or the key is not found.</returns>
|
||||
public static string GetUserData(UserInfo user, string key) {
|
||||
if(user == null) return null;
|
||||
if(string.IsNullOrEmpty(key)) return null;
|
||||
if(user.Username == "admin") return null;
|
||||
|
||||
return user.Provider.RetrieveUserData(user, key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets user data.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="key">The data key.</param>
|
||||
/// <param name="data">The data value.</param>
|
||||
/// <returns><c>true</c> if the data is stored, <c>false</c> otherwise.</returns>
|
||||
public static bool SetUserData(UserInfo user, string key, string data) {
|
||||
if(user == null) return false;
|
||||
if(string.IsNullOrEmpty(key)) return false;
|
||||
if(user.Username == "admin") return false;
|
||||
if(user.Provider.UsersDataReadOnly) return false;
|
||||
|
||||
bool done = user.Provider.StoreUserData(user, key, data);
|
||||
|
||||
if(done) Log.LogEntry("User data stored for " + user.Username, EntryType.General, Log.SystemUsername);
|
||||
else Log.LogEntry("Could not store user data for " + user.Username, EntryType.Error, Log.SystemUsername);
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new User.
|
||||
/// </summary>
|
||||
/// <param name="username">The Username.</param>
|
||||
/// <param name="displayName">The display name.</param>
|
||||
/// <param name="password">The Password (plain text).</param>
|
||||
/// <param name="email">The Email address.</param>
|
||||
/// <param name="active">A value specifying whether or not the account is active.</param>
|
||||
/// <param name="provider">The Provider. If null, the default provider is used.</param>
|
||||
/// <returns>True if the User has been created successfully.</returns>
|
||||
public static bool AddUser(string username, string displayName, string password, string email, bool active, IUsersStorageProviderV30 provider) {
|
||||
if(FindUser(username) != null) return false;
|
||||
if(provider == null) provider = Collectors.UsersProviderCollector.GetProvider(Settings.DefaultUsersProvider);
|
||||
|
||||
if(provider.UserAccountsReadOnly) return false;
|
||||
|
||||
UserInfo u = provider.AddUser(username, displayName, password, email, active, DateTime.Now);
|
||||
if(u == null) {
|
||||
Log.LogEntry("User creation failed for " + username, EntryType.Error, Log.SystemUsername);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
Log.LogEntry("User " + username + " created", EntryType.General, Log.SystemUsername);
|
||||
Host.Instance.OnUserAccountActivity(u, UserAccountActivity.AccountAdded);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a new User.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to modify.</param>
|
||||
/// <param name="displayName">The display name.</param>
|
||||
/// <param name="password">The Password (plain text, <c>null</c> or empty for no change).</param>
|
||||
/// <param name="email">The Email address.</param>
|
||||
/// <param name="active">A value specifying whether or not the account is active.</param>
|
||||
/// <returns>True if the User has been created successfully.</returns>
|
||||
public static bool ModifyUser(UserInfo user, string displayName, string password, string email, bool active) {
|
||||
if(user.Provider.UserAccountsReadOnly) return false;
|
||||
|
||||
bool done = user.Provider.ModifyUser(user, displayName, password, email, active) != null;
|
||||
|
||||
if(done) {
|
||||
Log.LogEntry("User " + user.Username + " updated", EntryType.General, Log.SystemUsername);
|
||||
Host.Instance.OnUserAccountActivity(user, UserAccountActivity.AccountModified);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
Log.LogEntry("User update failed for " + user.Username, EntryType.Error, Log.SystemUsername);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a User.
|
||||
/// </summary>
|
||||
/// <param name="user">The User to remove.</param>
|
||||
/// <returns>True if the User has been removed successfully.</returns>
|
||||
public static bool RemoveUser(UserInfo user) {
|
||||
if(user.Provider.UserAccountsReadOnly) return false;
|
||||
|
||||
bool done = user.Provider.RemoveUser(user);
|
||||
if(done) {
|
||||
Log.LogEntry("User " + user.Username + " removed", EntryType.General, Log.SystemUsername);
|
||||
Host.Instance.OnUserAccountActivity(user, UserAccountActivity.AccountRemoved);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
Log.LogEntry("User deletion failed for " + user.Username, EntryType.Error, Log.SystemUsername);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the Password of a User.
|
||||
/// </summary>
|
||||
/// <param name="user">The User to change the password of.</param>
|
||||
/// <param name="newPassword">The new Password (plain text).</param>
|
||||
/// <returns><c>true</c> if the Password has been changed successfully, <c>false</c> otherwise.</returns>
|
||||
public static bool ChangePassword(UserInfo user, string newPassword) {
|
||||
return ModifyUser(user, user.DisplayName, newPassword, user.Email, user.Active);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the password reset message to a user.
|
||||
/// </summary>
|
||||
/// <param name="username">The username.</param>
|
||||
/// <param name="email">The email.</param>
|
||||
/// <param name="dateTime">The user registration date/time.</param>
|
||||
public static void SendPasswordResetMessage(string username, string email, DateTime dateTime) {
|
||||
string mainLink = Settings.MainUrl + "Login.aspx?ResetCode=" + Tools.ComputeSecurityHash(username, email, dateTime) + "&Username=" + Tools.UrlEncode(username);
|
||||
string body = Settings.Provider.GetMetaDataItem(MetaDataItem.PasswordResetProcedureMessage, null).Replace("##USERNAME##",
|
||||
username).Replace("##LINK##", mainLink).Replace("##WIKITITLE##",
|
||||
Settings.WikiTitle).Replace("##EMAILADDRESS##", Settings.ContactEmail);
|
||||
|
||||
EmailTools.AsyncSendEmail(email, Settings.SenderEmail,
|
||||
Settings.WikiTitle + " - " + Exchanger.ResourceExchanger.GetResource("ResetPassword"), body, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the Email address of a User.
|
||||
/// </summary>
|
||||
/// <param name="user">The User to change the Email address of.</param>
|
||||
/// <param name="newEmail">The new Email address.</param>
|
||||
/// <returns><c>true</c> if the Email address has been changed successfully, <c>false</c> otherwise.</returns>
|
||||
public static bool ChangeEmail(UserInfo user, string newEmail) {
|
||||
return ModifyUser(user, user.DisplayName, null, newEmail, user.Active);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the Active/Inactive status of a User.
|
||||
/// </summary>
|
||||
/// <param name="user">The User.</param>
|
||||
/// <param name="active">The status.</param>
|
||||
/// <returns>True if the User's status has been changed successfully.</returns>
|
||||
public static bool SetActivationStatus(UserInfo user, bool active) {
|
||||
return ModifyUser(user, user.DisplayName, null, user.Email, active);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the user groups.
|
||||
/// </summary>
|
||||
/// <returns>All the user groups, sorted by name.</returns>
|
||||
public static List<UserGroup> GetUserGroups() {
|
||||
List<UserGroup> result = new List<UserGroup>(50);
|
||||
|
||||
int count = 0;
|
||||
foreach(IUsersStorageProviderV30 prov in Collectors.UsersProviderCollector.AllProviders) {
|
||||
count++;
|
||||
result.AddRange(prov.GetUserGroups());
|
||||
}
|
||||
|
||||
if(count > 1) {
|
||||
result.Sort(new UserGroupComparer());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the user groups in a provider.
|
||||
/// </summary>
|
||||
/// <param name="provider">The provider.</param>
|
||||
/// <returns>The user groups, sorted by name.</returns>
|
||||
public static List<UserGroup> GetUserGroups(IUsersStorageProviderV30 provider) {
|
||||
return new List<UserGroup>(provider.GetUserGroups());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the user groups a user is member of.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>All the user groups the user is member of, sorted by name.</returns>
|
||||
public static List<UserGroup> GetUserGroupsForUser(UserInfo user) {
|
||||
UserGroup[] allGroups = user.Provider.GetUserGroups();
|
||||
|
||||
List<UserGroup> result = new List<UserGroup>(allGroups.Length);
|
||||
|
||||
StringComparer comp = StringComparer.OrdinalIgnoreCase;
|
||||
|
||||
foreach(UserGroup group in allGroups) {
|
||||
if(Array.Find(user.Groups, delegate(string g) {
|
||||
return comp.Compare(g, group.Name) == 0;
|
||||
}) != null) {
|
||||
result.Add(group);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a user group.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the user group to find.</param>
|
||||
/// <returns>The <see cref="T:UserGroup" /> object or <c>null</c> if no data is found.</returns>
|
||||
public static UserGroup FindUserGroup(string name) {
|
||||
List<UserGroup> allGroups = GetUserGroups();
|
||||
int index = allGroups.BinarySearch(new UserGroup(name, "", null), new UserGroupComparer());
|
||||
|
||||
if(index < 0) return null;
|
||||
else return allGroups[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new user group to a specific provider.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the group.</param>
|
||||
/// <param name="description">The description of the group.</param>
|
||||
/// <param name="provider">The target provider.</param>
|
||||
/// <returns><c>true</c> if the groups is added, <c>false</c> otherwise.</returns>
|
||||
public static bool AddUserGroup(string name, string description, IUsersStorageProviderV30 provider) {
|
||||
if(provider == null) provider = Collectors.UsersProviderCollector.GetProvider(Settings.DefaultUsersProvider);
|
||||
|
||||
if(provider.UserGroupsReadOnly) return false;
|
||||
|
||||
if(FindUserGroup(name) != null) return false;
|
||||
|
||||
UserGroup result = provider.AddUserGroup(name, description);
|
||||
|
||||
if(result != null) {
|
||||
Host.Instance.OnUserGroupActivity(result, UserGroupActivity.GroupAdded);
|
||||
Log.LogEntry("User Group " + name + " created", EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
else Log.LogEntry("Creation failed for User Group " + name, EntryType.Error, Log.SystemUsername);
|
||||
|
||||
return result != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new user group to the default provider.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the group.</param>
|
||||
/// <param name="description">The description of the group.</param>
|
||||
/// <returns><c>true</c> if the groups is added, <c>false</c> otherwise.</returns>
|
||||
public static bool AddUserGroup(string name, string description) {
|
||||
return AddUserGroup(name, description,
|
||||
Collectors.UsersProviderCollector.GetProvider(Settings.DefaultUsersProvider));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies a user group.
|
||||
/// </summary>
|
||||
/// <param name="group">The user group to modify.</param>
|
||||
/// <param name="description">The new description.</param>
|
||||
/// <returns><c>true</c> if the user group is modified, <c>false</c> otherwise.</returns>
|
||||
public static bool ModifyUserGroup(UserGroup group, string description) {
|
||||
if(group.Provider.UserGroupsReadOnly) return false;
|
||||
|
||||
UserGroup result = group.Provider.ModifyUserGroup(group, description);
|
||||
|
||||
if(result != null) {
|
||||
Host.Instance.OnUserGroupActivity(result, UserGroupActivity.GroupModified);
|
||||
Log.LogEntry("User Group " + group.Name + " updated", EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
else Log.LogEntry("Update failed for User Group " + result.Name, EntryType.Error, Log.SystemUsername);
|
||||
|
||||
return result != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a user group.
|
||||
/// </summary>
|
||||
/// <param name="group">The user group to remove.</param>
|
||||
/// <returns><c>true</c> if the user group is removed, <c>false</c> otherwise.</returns>
|
||||
public static bool RemoveUserGroup(UserGroup group) {
|
||||
if(group.Provider.UserGroupsReadOnly) return false;
|
||||
|
||||
bool done = group.Provider.RemoveUserGroup(group);
|
||||
|
||||
if(done) {
|
||||
Host.Instance.OnUserGroupActivity(group, UserGroupActivity.GroupRemoved);
|
||||
Log.LogEntry("User Group " + group.Name + " deleted", EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
else Log.LogEntry("Deletion failed for User Group " + group.Name, EntryType.Error, Log.SystemUsername);
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the group memberships of a user account.
|
||||
/// </summary>
|
||||
/// <param name="user">The user account.</param>
|
||||
/// <param name="groups">The groups the user account is member of.</param>
|
||||
/// <returns><c>true</c> if the membership is set, <c>false</c> otherwise.</returns>
|
||||
public static bool SetUserMembership(UserInfo user, string[] groups) {
|
||||
if(user.Provider.GroupMembershipReadOnly) return false;
|
||||
|
||||
UserInfo result = user.Provider.SetUserMembership(user, groups);
|
||||
|
||||
if(result != null) {
|
||||
Host.Instance.OnUserAccountActivity(result, UserAccountActivity.AccountMembershipChanged);
|
||||
Log.LogEntry("Group membership set for User " + user.Username, EntryType.General, Log.SystemUsername);
|
||||
}
|
||||
else Log.LogEntry("Could not set group membership for User " + user.Username, EntryType.Error, Log.SystemUsername);
|
||||
|
||||
return result != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the correct link of a User.
|
||||
/// </summary>
|
||||
/// <param name="username">The Username.</param>
|
||||
/// <returns>The User link.</returns>
|
||||
public static string UserLink(string username) {
|
||||
return UserLink(username, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the correct link of a User.
|
||||
/// </summary>
|
||||
/// <param name="username">The Username.</param>
|
||||
/// <param name="newWindow">A value indicating whether to open the link in a new window.</param>
|
||||
/// <returns>The User link.</returns>
|
||||
public static string UserLink(string username, bool newWindow) {
|
||||
if(username != null && (username.EndsWith("+" + Log.SystemUsername) || username == Log.SystemUsername)) return username;
|
||||
|
||||
UserInfo u = FindUser(username);
|
||||
if(u == null && username.Equals("admin")) u = new UserInfo("admin", null, Settings.ContactEmail, true, DateTime.Now, null);
|
||||
if(u != null) {
|
||||
return @"<a " +
|
||||
(newWindow ? "target=\"_blank\" " : "") +
|
||||
@"href=""" + UrlTools.BuildUrl("User.aspx?Username=", Tools.UrlEncode(u.Username)) + @""">" +
|
||||
GetDisplayName(u) + "</a>";
|
||||
}
|
||||
else return username;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display name of a user.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>The display name.</returns>
|
||||
public static string GetDisplayName(UserInfo user) {
|
||||
if(string.IsNullOrEmpty(user.DisplayName)) return user.Username;
|
||||
else return user.DisplayName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to automatically login a user using the current HttpContext,
|
||||
/// through any provider that supports the operation.
|
||||
/// </summary>
|
||||
/// <param name="context">The current HttpContext.</param>
|
||||
/// <returns>The correct UserInfo, or <c>null</c>.</returns>
|
||||
public static UserInfo TryAutoLogin(HttpContext context) {
|
||||
// Try default provider first
|
||||
IUsersStorageProviderV30 defaultProvider =
|
||||
Collectors.UsersProviderCollector.GetProvider(Settings.DefaultUsersProvider) as IUsersStorageProviderV30;
|
||||
|
||||
if(defaultProvider != null) {
|
||||
UserInfo temp = defaultProvider.TryAutoLogin(context);
|
||||
if(temp != null) return temp;
|
||||
}
|
||||
|
||||
// Then try all other providers
|
||||
IUsersStorageProviderV30[] providers = Collectors.UsersProviderCollector.AllProviders;
|
||||
foreach(IUsersStorageProviderV30 p in providers) {
|
||||
IUsersStorageProviderV30 extProv = p as IUsersStorageProviderV30;
|
||||
if(extProv != null && extProv != defaultProvider) {
|
||||
UserInfo temp = extProv.TryAutoLogin(context);
|
||||
if(temp != null) return temp;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to manually login a user using all the available methods.
|
||||
/// </summary>
|
||||
/// <param name="username">The username.</param>
|
||||
/// <param name="password">The password.</param>
|
||||
/// <returns>The correct UserInfo, or <c>null</c>.</returns>
|
||||
public static UserInfo TryLogin(string username, string password) {
|
||||
if(username == "admin" && password == Settings.MasterPassword) {
|
||||
return GetAdministratorAccount();
|
||||
}
|
||||
|
||||
// Try default provider first
|
||||
IUsersStorageProviderV30 defaultProvider =
|
||||
Collectors.UsersProviderCollector.GetProvider(Settings.DefaultUsersProvider) as IUsersStorageProviderV30;
|
||||
|
||||
if(defaultProvider != null) {
|
||||
UserInfo temp = defaultProvider.TryManualLogin(username, password);
|
||||
if(temp != null) return temp;
|
||||
}
|
||||
|
||||
// Then try all other providers
|
||||
IUsersStorageProviderV30[] providers = Collectors.UsersProviderCollector.AllProviders;
|
||||
foreach(IUsersStorageProviderV30 p in providers) {
|
||||
IUsersStorageProviderV30 extProv = p as IUsersStorageProviderV30;
|
||||
if(extProv != null && extProv != defaultProvider) {
|
||||
UserInfo temp = extProv.TryManualLogin(username, password);
|
||||
if(temp != null) return temp;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to login a user through the cookie-stored authentication data.
|
||||
/// </summary>
|
||||
/// <param name="username">The username.</param>
|
||||
/// <param name="loginKey">The login key.</param>
|
||||
/// <returns>The correct UserInfo object, or <c>null</c>.</returns>
|
||||
public static UserInfo TryCookieLogin(string username, string loginKey) {
|
||||
if(username == "admin" && loginKey == ComputeLoginKey(username, Settings.ContactEmail, DateTime.MinValue)) {
|
||||
// Just return, no notification to providers because the "admin" account is fictitious
|
||||
return GetAdministratorAccount();
|
||||
}
|
||||
|
||||
UserInfo user = FindUser(username);
|
||||
|
||||
if(user != null && user.Active) {
|
||||
if(loginKey == ComputeLoginKey(user.Username, user.Email, user.DateTime)) {
|
||||
// Notify provider
|
||||
user.Provider.NotifyCookieLogin(user);
|
||||
return user;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies to the proper provider that a user has logged out.
|
||||
/// </summary>
|
||||
/// <param name="username">The username.</param>
|
||||
public static void NotifyLogout(string username) {
|
||||
UserInfo user = FindUser(username);
|
||||
if(user != null) {
|
||||
IUsersStorageProviderV30 prov = user.Provider as IUsersStorageProviderV30;
|
||||
if(prov != null) prov.NotifyLogout(user);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copmputes the login key.
|
||||
/// </summary>
|
||||
/// <param name="username">The username.</param>
|
||||
/// <param name="email">The email.</param>
|
||||
/// <param name="dateTime">The registration date/time.</param>
|
||||
/// <returns>The login key.</returns>
|
||||
public static string ComputeLoginKey(string username, string email, DateTime dateTime) {
|
||||
if(username == null) throw new ArgumentNullException("username");
|
||||
if(email == null) throw new ArgumentNullException("email");
|
||||
|
||||
return Tools.ComputeSecurityHash(username, email, dateTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the email notification status for a page.
|
||||
/// </summary>
|
||||
/// <param name="user">The user for which to set the notification status.</param>
|
||||
/// <param name="page">The page subject of the notification.</param>
|
||||
/// <param name="pageChanges">A value indicating whether page changes should be notified.</param>
|
||||
/// <param name="discussionMessages">A value indicating whether discussion messages should be notified.</param>
|
||||
/// <returns><c>true</c> if the notification is set, <c>false</c> otherwise.</returns>
|
||||
public static bool SetEmailNotification(UserInfo user, PageInfo page, bool pageChanges, bool discussionMessages) {
|
||||
if(user == null || page == null) return false;
|
||||
|
||||
// Get user's data
|
||||
// Depending on the status of pageChanges and discussionMessages,
|
||||
// either remove existing entries (if any) or add new entries
|
||||
// In the process, remove entries that refer to inexistent pages
|
||||
|
||||
// Format
|
||||
// Page1:Page2:Page3
|
||||
|
||||
string pageChangeData = user.Provider.RetrieveUserData(user, PageChangesKey);
|
||||
string discussionMessagesData = user.Provider.RetrieveUserData(user, DiscussionMessagesKey);
|
||||
|
||||
if(pageChangeData == null) pageChangeData = "";
|
||||
if(discussionMessagesData == null) discussionMessagesData = "";
|
||||
|
||||
string[] pageChangesEntries = pageChangeData.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
string[] discussionMessagesEntries = discussionMessagesData.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
List<string> pageChangesResult = new List<string>(pageChangesEntries.Length + 1);
|
||||
List<string> discussionMessagesResult = new List<string>(discussionMessagesEntries.Length + 1);
|
||||
|
||||
string lowercasePage = page.FullName.ToLowerInvariant();
|
||||
|
||||
bool added = false;
|
||||
foreach(string entry in pageChangesEntries) {
|
||||
if(entry.ToLowerInvariant() == lowercasePage) {
|
||||
if(pageChanges) {
|
||||
pageChangesResult.Add(entry);
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
else if(Pages.FindPage(entry) != null) pageChangesResult.Add(entry);
|
||||
}
|
||||
if(!added && pageChanges) pageChangesResult.Add(page.FullName);
|
||||
|
||||
added = false;
|
||||
foreach(string entry in discussionMessagesEntries) {
|
||||
if(entry.ToLowerInvariant() == lowercasePage) {
|
||||
if(discussionMessages) {
|
||||
discussionMessagesResult.Add(entry);
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
else if(Pages.FindPage(entry) != null) discussionMessagesResult.Add(entry);
|
||||
}
|
||||
if(!added && discussionMessages) discussionMessagesResult.Add(page.FullName);
|
||||
|
||||
string newPageChangesData = string.Join(":", pageChangesResult.ToArray());
|
||||
string newDiscussionMessagesData = string.Join(":", discussionMessagesResult.ToArray());
|
||||
|
||||
bool done = user.Provider.StoreUserData(user, PageChangesKey, newPageChangesData) &
|
||||
user.Provider.StoreUserData(user, DiscussionMessagesKey, newDiscussionMessagesData);
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the email notification status for a namespace.
|
||||
/// </summary>
|
||||
/// <param name="user">The user for which to set the notification status.</param>
|
||||
/// <param name="nspace">The namespace subject of the notification.</param>
|
||||
/// <param name="pageChanges">A value indicating whether page changes should be notified.</param>
|
||||
/// <param name="discussionMessages">A value indicating whether discussion messages should be notified.</param>
|
||||
/// <returns><c>true</c> if the notification is set, <c>false</c> otherwise.</returns>
|
||||
public static bool SetEmailNotification(UserInfo user, NamespaceInfo nspace, bool pageChanges, bool discussionMessages) {
|
||||
if(user == null) return false;
|
||||
|
||||
// Get user's data
|
||||
// Depending on the status of pageChanges and discussionMessages,
|
||||
// either remove existing entries (if any) or add new entries
|
||||
// In the process, remove entries that refer to inexistent pages
|
||||
|
||||
// Format
|
||||
// Namespace1:Namespace2:Namespace3
|
||||
|
||||
string pageChangeData = user.Provider.RetrieveUserData(user, NamespacePageChangesKey);
|
||||
string discussionMessagesData = user.Provider.RetrieveUserData(user, NamespaceDiscussionMessagesKey);
|
||||
|
||||
if(pageChangeData == null) pageChangeData = "";
|
||||
if(discussionMessagesData == null) discussionMessagesData = "";
|
||||
|
||||
string[] pageChangesEntries = pageChangeData.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
string[] discussionMessagesEntries = discussionMessagesData.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
List<string> pageChangesResult = new List<string>(pageChangesEntries.Length + 1);
|
||||
List<string> discussionMessagesResult = new List<string>(discussionMessagesEntries.Length + 1);
|
||||
|
||||
string namespaceName = nspace != null ? nspace.Name : "<root>";
|
||||
string lowercaseNamespace = nspace != null ? nspace.Name.ToLowerInvariant() : "<root>";
|
||||
|
||||
bool added = false;
|
||||
foreach(string entry in pageChangesEntries) {
|
||||
if(entry.ToLowerInvariant() == lowercaseNamespace) {
|
||||
if(pageChanges) {
|
||||
pageChangesResult.Add(entry);
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(entry == "<root>") pageChangesResult.Add("<root>");
|
||||
else if(Pages.FindNamespace(entry) != null) pageChangesResult.Add(entry);
|
||||
}
|
||||
}
|
||||
if(!added && pageChanges) pageChangesResult.Add(namespaceName);
|
||||
|
||||
added = false;
|
||||
foreach(string entry in discussionMessagesEntries) {
|
||||
if(entry.ToLowerInvariant() == lowercaseNamespace) {
|
||||
if(discussionMessages) {
|
||||
discussionMessagesResult.Add(entry);
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(entry == "<root>") discussionMessagesResult.Add("<root>");
|
||||
else if(Pages.FindNamespace(entry) != null) discussionMessagesResult.Add(entry);
|
||||
}
|
||||
}
|
||||
if(!added && discussionMessages) discussionMessagesResult.Add(namespaceName);
|
||||
|
||||
string newPageChangesData = string.Join(":", pageChangesResult.ToArray());
|
||||
string newDiscussionMessagesData = string.Join(":", discussionMessagesResult.ToArray());
|
||||
|
||||
bool done = user.Provider.StoreUserData(user, NamespacePageChangesKey, newPageChangesData) &
|
||||
user.Provider.StoreUserData(user, NamespaceDiscussionMessagesKey, newDiscussionMessagesData);
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the email notification status for a page.
|
||||
/// </summary>
|
||||
/// <param name="user">The user for which to get the notification status.</param>
|
||||
/// <param name="page">The page subject of the notification.</param>
|
||||
/// <param name="pageChanges">A value indicating whether page changes should be notified.</param>
|
||||
/// <param name="discussionMessages">A value indicating whether discussion messages should be notified.</param>
|
||||
public static void GetEmailNotification(UserInfo user, PageInfo page, out bool pageChanges, out bool discussionMessages) {
|
||||
pageChanges = false;
|
||||
discussionMessages = false;
|
||||
|
||||
if(user == null || page == null) return;
|
||||
|
||||
string pageChangeData = user.Provider.RetrieveUserData(user, PageChangesKey);
|
||||
string discussionMessagesData = user.Provider.RetrieveUserData(user, DiscussionMessagesKey);
|
||||
|
||||
if(pageChangeData == null) pageChangeData = "";
|
||||
if(discussionMessagesData == null) discussionMessagesData = "";
|
||||
|
||||
pageChangeData = pageChangeData.ToLowerInvariant();
|
||||
discussionMessagesData = discussionMessagesData.ToLowerInvariant();
|
||||
|
||||
string[] pageChangeEntries = pageChangeData.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
string[] discussionMessagesEntries = discussionMessagesData.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
string lowercasePage = page.FullName.ToLowerInvariant();
|
||||
|
||||
// Elements in the array are already lowercase
|
||||
pageChanges = Array.Find(pageChangeEntries, delegate(string elem) { return elem == lowercasePage; }) != null;
|
||||
discussionMessages = Array.Find(discussionMessagesEntries, delegate(string elem) { return elem == lowercasePage; }) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the email notification status for a namespace.
|
||||
/// </summary>
|
||||
/// <param name="user">The user for which to get the notification status.</param>
|
||||
/// <param name="nspace">The namespace subject of the notification (<c>null</c> for the root).</param>
|
||||
/// <param name="pageChanges">A value indicating whether page changes should be notified.</param>
|
||||
/// <param name="discussionMessages">A value indicating whether discussion messages should be notified.</param>
|
||||
public static void GetEmailNotification(UserInfo user, NamespaceInfo nspace, out bool pageChanges, out bool discussionMessages) {
|
||||
pageChanges = false;
|
||||
discussionMessages = false;
|
||||
|
||||
if(user == null) return;
|
||||
|
||||
string lowercaseNamespaces = nspace != null ? nspace.Name.ToLowerInvariant() : "<root>";
|
||||
|
||||
string pageChangesData = user.Provider.RetrieveUserData(user, NamespacePageChangesKey);
|
||||
string discussionMessagesData = user.Provider.RetrieveUserData(user, NamespaceDiscussionMessagesKey);
|
||||
|
||||
if(pageChangesData == null) pageChangesData = "";
|
||||
if(discussionMessagesData == null) discussionMessagesData = "";
|
||||
|
||||
pageChangesData = pageChangesData.ToLowerInvariant();
|
||||
discussionMessagesData = discussionMessagesData.ToLowerInvariant();
|
||||
|
||||
string[] pageChangeEntries = pageChangesData.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
string[] discussionMessagesEntries = discussionMessagesData.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
// Elements in the array are already lowercase
|
||||
pageChanges = Array.Find(pageChangeEntries, delegate(string elem) { return elem == lowercaseNamespaces; }) != null;
|
||||
discussionMessages = Array.Find(discussionMessagesEntries, delegate(string elem) { return elem == lowercaseNamespaces; }) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the users that must be notified of a page change.
|
||||
/// </summary>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <returns>The users to be notified.</returns>
|
||||
public static UserInfo[] GetUsersToNotifyForPageChange(PageInfo page) {
|
||||
if(page == null) return new UserInfo[0];
|
||||
|
||||
UserInfo[] specific = GetUsersToNotify(page, PageChangesKey);
|
||||
UserInfo[] nspace = GetUsersToNotify(Pages.FindNamespace(NameTools.GetNamespace(page.FullName)),
|
||||
NamespacePageChangesKey);
|
||||
|
||||
UserInfo[] temp = MergeArrays(specific, nspace);
|
||||
List<UserInfo> result = new List<UserInfo>(temp.Length);
|
||||
|
||||
// Verify read permissions
|
||||
foreach(UserInfo user in temp) {
|
||||
if(AuthChecker.CheckActionForPage(page, Actions.ForPages.ReadPage, user.Username, user.Groups)) {
|
||||
result.Add(user);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the users that must be notified of a discussion message.
|
||||
/// </summary>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <returns>The users to be notified.</returns>
|
||||
public static UserInfo[] GetUsersToNotifyForDiscussionMessages(PageInfo page) {
|
||||
if(page == null) return new UserInfo[0];
|
||||
|
||||
UserInfo[] specific = GetUsersToNotify(page, DiscussionMessagesKey);
|
||||
UserInfo[] nspace = GetUsersToNotify(Pages.FindNamespace(NameTools.GetNamespace(page.FullName)),
|
||||
NamespaceDiscussionMessagesKey);
|
||||
|
||||
UserInfo[] temp = MergeArrays(specific, nspace);
|
||||
List<UserInfo> result = new List<UserInfo>(temp.Length);
|
||||
|
||||
// Verify read permissions
|
||||
foreach(UserInfo user in temp) {
|
||||
if(AuthChecker.CheckActionForPage(page, Actions.ForPages.ReadDiscussion, user.Username, user.Groups)) {
|
||||
result.Add(user);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges two arrays of users, removing duplicates.
|
||||
/// </summary>
|
||||
/// <param name="array1">The first array.</param>
|
||||
/// <param name="array2">The second array.</param>
|
||||
/// <returns>The merged users.</returns>
|
||||
private static UserInfo[] MergeArrays(UserInfo[] array1, UserInfo[] array2) {
|
||||
List<UserInfo> result = new List<UserInfo>(array1.Length + array2.Length);
|
||||
result.AddRange(array1);
|
||||
|
||||
UsernameComparer comp = new UsernameComparer();
|
||||
foreach(UserInfo user in array2) {
|
||||
bool found = false;
|
||||
foreach(UserInfo present in result) {
|
||||
if(comp.Compare(present, user) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!found) {
|
||||
result.Add(user);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the users to notify for either a page change or a discussion message.
|
||||
/// </summary>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <param name="key">The key to look for in the user's data.</param>
|
||||
/// <returns>The users to be notified.</returns>
|
||||
private static UserInfo[] GetUsersToNotify(PageInfo page, string key) {
|
||||
List<UserInfo> result = new List<UserInfo>(200);
|
||||
|
||||
string lowercasePage = page.FullName.ToLowerInvariant();
|
||||
|
||||
foreach(IUsersStorageProviderV30 prov in Collectors.UsersProviderCollector.AllProviders) {
|
||||
IDictionary<UserInfo, string> users = prov.GetUsersWithData(key);
|
||||
|
||||
string[] fields;
|
||||
foreach(KeyValuePair<UserInfo, string> pair in users) {
|
||||
fields = pair.Value.ToLowerInvariant().Split(':');
|
||||
|
||||
if(Array.Find(fields, delegate(string elem) { return elem == lowercasePage; }) != null) {
|
||||
result.Add(pair.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the users to notify for either a page change or a discussion message in a namespace.
|
||||
/// </summary>
|
||||
/// <param name="nspace">The namespace (<c>null</c> for the root).</param>
|
||||
/// <param name="key">The key to look for in the user's data.</param>
|
||||
/// <returns>The users to be notified.</returns>
|
||||
private static UserInfo[] GetUsersToNotify(NamespaceInfo nspace, string key) {
|
||||
List<UserInfo> result = new List<UserInfo>(200);
|
||||
|
||||
string lowercaseNamespace = nspace != null ? nspace.Name.ToLowerInvariant() : "<root>";
|
||||
|
||||
foreach(IUsersStorageProviderV30 prov in Collectors.UsersProviderCollector.AllProviders) {
|
||||
IDictionary<UserInfo, string> users = prov.GetUsersWithData(key);
|
||||
|
||||
string[] fields;
|
||||
foreach(KeyValuePair<UserInfo, string> pair in users) {
|
||||
fields = pair.Value.ToLowerInvariant().Split(':');
|
||||
|
||||
if(Array.Find(fields, delegate(string elem) { return elem == lowercaseNamespace; }) != null) {
|
||||
result.Add(pair.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
970
Core/UsersStorageProvider.cs
Normal file
970
Core/UsersStorageProvider.cs
Normal file
|
@ -0,0 +1,970 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki {
|
||||
|
||||
/// <summary>
|
||||
/// Implements a Users Storage Provider.
|
||||
/// </summary>
|
||||
public class UsersStorageProvider : IUsersStorageProviderV30 {
|
||||
|
||||
private const string UsersFile = "Users.cs";
|
||||
private const string UsersDataFile = "UsersData.cs";
|
||||
private const string GroupsFile = "Groups.cs";
|
||||
|
||||
private readonly ComponentInformation info = new ComponentInformation("Local Users Provider",
|
||||
"ScrewTurn Software", Settings.WikiVersion, "http://www.screwturn.eu", null);
|
||||
|
||||
private IHostV30 host;
|
||||
|
||||
private UserGroup[] groupsCache = null;
|
||||
private UserInfo[] usersCache = null;
|
||||
|
||||
private string GetFullPath(string filename) {
|
||||
return Path.Combine(host.GetSettingValue(SettingName.PublicDirectory), filename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Provider.
|
||||
/// </summary>
|
||||
/// <param name="host">The Host of the Provider.</param>
|
||||
/// <param name="config">The Configuration data, if any.</param>
|
||||
/// <exception cref="ArgumentNullException">If <b>host</b> or <b>config</b> are <c>null</c>.</exception>
|
||||
/// <exception cref="InvalidConfigurationException">If <b>config</b> is not valid or is incorrect.</exception>
|
||||
public void Init(IHostV30 host, string config) {
|
||||
if(host == null) throw new ArgumentNullException("host");
|
||||
if(config == null) throw new ArgumentNullException("config");
|
||||
|
||||
this.host = host;
|
||||
|
||||
if(!LocalProvidersTools.CheckWritePermissions(host.GetSettingValue(SettingName.PublicDirectory))) {
|
||||
throw new InvalidConfigurationException("Cannot write into the public directory - check permissions");
|
||||
}
|
||||
|
||||
if(!File.Exists(GetFullPath(UsersFile))) {
|
||||
File.Create(GetFullPath(UsersFile)).Close();
|
||||
}
|
||||
if(!File.Exists(GetFullPath(UsersDataFile))) {
|
||||
File.Create(GetFullPath(UsersDataFile)).Close();
|
||||
}
|
||||
if(!File.Exists(GetFullPath(GroupsFile))) {
|
||||
File.Create(GetFullPath(GroupsFile)).Close();
|
||||
}
|
||||
|
||||
VerifyAndPerformUpgrade();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the need for a data upgrade, and performs it when needed.
|
||||
/// </summary>
|
||||
private void VerifyAndPerformUpgrade() {
|
||||
// Load file lines
|
||||
// Parse first line (if any) with old (v2) algorithm
|
||||
// If parsing is successful, then the file must be converted
|
||||
// Conversion consists in removing the 'ADMIN|USER' field, creating the proper default groups and setting user membership
|
||||
|
||||
// Structure v2:
|
||||
// Username|PasswordHash|Email|Active-Inactive|DateTime|Admin-User
|
||||
|
||||
//string[] lines = File.ReadAllLines(GetFullPath(UsersFile));
|
||||
// Use this method because version 2.0 file might have started with a blank line
|
||||
string[] lines = File.ReadAllText(GetFullPath(UsersFile)).Replace("\r", "").Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if(lines.Length > 0) {
|
||||
bool upgradeIsNeeded = false;
|
||||
LocalUserInfo[] users = new LocalUserInfo[lines.Length];
|
||||
bool[] oldStyleAdmin = new bool[lines.Length]; // Values are valid only if upgradeIsNeeded=true
|
||||
|
||||
char[] splitter = new char[] { '|' };
|
||||
|
||||
for(int i = 0; i < lines.Length; i++) {
|
||||
string line = lines[i];
|
||||
|
||||
string[] fields = line.Split(splitter, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
string displayName = null;
|
||||
|
||||
if(fields.Length == 6) {
|
||||
if(fields[5] == "ADMIN" || fields[5] == "USER") {
|
||||
// Version 2.0
|
||||
upgradeIsNeeded = true;
|
||||
oldStyleAdmin[i] = fields[5] == "ADMIN";
|
||||
}
|
||||
else {
|
||||
// Version 3.0 with DisplayName specified
|
||||
oldStyleAdmin[i] = false;
|
||||
displayName = fields[5];
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Can be a version 3.0 file, with empty DisplayName
|
||||
oldStyleAdmin[i] = false;
|
||||
}
|
||||
|
||||
users[i] = new LocalUserInfo(fields[0], displayName, fields[2],
|
||||
fields[3].ToLowerInvariant() == "active", DateTime.Parse(fields[4]), this, fields[1]);
|
||||
}
|
||||
|
||||
if(upgradeIsNeeded) {
|
||||
// Dump users
|
||||
// Create default groups
|
||||
// Set membership for old users
|
||||
// Tell the host to set the permissions for the default groups
|
||||
|
||||
string backupFile = GetFullPath(Path.GetFileNameWithoutExtension(UsersFile) + "_v2" + Path.GetExtension(UsersFile));
|
||||
File.Copy(GetFullPath(UsersFile), backupFile);
|
||||
|
||||
host.LogEntry("Upgrading users format from 2.0 to 3.0", LogEntryType.General, null, this);
|
||||
|
||||
DumpUsers(users);
|
||||
UserGroup adminsGroup = AddUserGroup(host.GetSettingValue(SettingName.AdministratorsGroup), "Built-in Administrators");
|
||||
UserGroup usersGroup = AddUserGroup(host.GetSettingValue(SettingName.UsersGroup), "Built-in Users");
|
||||
|
||||
for(int i = 0; i < users.Length; i++) {
|
||||
if(oldStyleAdmin[i]) {
|
||||
SetUserMembership(users[i], new string[] { adminsGroup.Name });
|
||||
}
|
||||
else {
|
||||
SetUserMembership(users[i], new string[] { usersGroup.Name });
|
||||
}
|
||||
}
|
||||
|
||||
host.UpgradeSecurityFlagsToGroupsAcl(adminsGroup, usersGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method invoked on shutdown.
|
||||
/// </summary>
|
||||
/// <remarks>This method might not be invoked in some cases.</remarks>
|
||||
public void Shutdown() { }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Information about the Provider.
|
||||
/// </summary>
|
||||
public ComponentInformation Information {
|
||||
get { return info; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a brief summary of the configuration string format, in HTML. Returns <c>null</c> if no configuration is needed.
|
||||
/// </summary>
|
||||
public string ConfigHelpHtml {
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether user accounts are read-only.
|
||||
/// </summary>
|
||||
public bool UserAccountsReadOnly {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether user groups are read-only. If so, the provider
|
||||
/// should support default user groups as defined in the wiki configuration.
|
||||
/// </summary>
|
||||
public bool UserGroupsReadOnly {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether group membership is read-only (if <see cref="UserAccountsReadOnly" />
|
||||
/// is <c>false</c>, then this property must be <c>false</c>). If this property is <c>true</c>, the provider
|
||||
/// should return membership data compatible with default user groups.
|
||||
/// </summary>
|
||||
public bool GroupMembershipReadOnly {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether users' data is read-only.
|
||||
/// </summary>
|
||||
public bool UsersDataReadOnly {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests a Password for a User account.
|
||||
/// </summary>
|
||||
/// <param name="user">The User account.</param>
|
||||
/// <param name="password">The Password to test.</param>
|
||||
/// <returns>True if the Password is correct.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <b>user</b> or <b>password</b> are <c>null</c>.</exception>
|
||||
public bool TestAccount(UserInfo user, string password) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
if(password == null) throw new ArgumentNullException("password");
|
||||
|
||||
return TryManualLogin(user.Username, password) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the complete list of Users.
|
||||
/// </summary>
|
||||
/// <returns>All the Users, sorted by username.</returns>
|
||||
public UserInfo[] GetUsers() {
|
||||
lock(this) {
|
||||
if(usersCache == null) {
|
||||
|
||||
UserGroup[] groups = GetUserGroups();
|
||||
|
||||
string[] lines = File.ReadAllLines(GetFullPath(UsersFile));
|
||||
|
||||
UserInfo[] result = new UserInfo[lines.Length];
|
||||
|
||||
char[] splitter = new char[] { '|' };
|
||||
|
||||
string[] fields;
|
||||
for(int i = 0; i < lines.Length; i++) {
|
||||
fields = lines[i].Split(splitter, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
// Structure (version 3.0 - file previously converted):
|
||||
// Username|PasswordHash|Email|Active-Inactive|DateTime[|DisplayName]
|
||||
|
||||
string displayName = fields.Length == 6 ? fields[5] : null;
|
||||
|
||||
result[i] = new LocalUserInfo(fields[0], displayName, fields[2], fields[3].ToLowerInvariant().Equals("active"),
|
||||
DateTime.Parse(fields[4]), this, fields[1]);
|
||||
|
||||
result[i].Groups = GetGroupsForUser(result[i].Username, groups);
|
||||
}
|
||||
|
||||
Array.Sort(result, new UsernameComparer());
|
||||
|
||||
usersCache = result;
|
||||
}
|
||||
|
||||
return usersCache;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of all the groups a user is member of.
|
||||
/// </summary>
|
||||
/// <param name="user">The username.</param>
|
||||
/// <param name="groups">The groups.</param>
|
||||
/// <returns>The names of the groups the user is member of.</returns>
|
||||
private string[] GetGroupsForUser(string user, UserGroup[] groups) {
|
||||
List<string> result = new List<string>(3);
|
||||
|
||||
foreach(UserGroup group in groups) {
|
||||
if(Array.Find(group.Users, delegate(string u) { return u == user; }) != null) {
|
||||
result.Add(group.Name);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a proper local instance of a user account.
|
||||
/// </summary>
|
||||
/// <param name="user">The user account.</param>
|
||||
/// <returns>The local instance, or <c>null</c>.</returns>
|
||||
private LocalUserInfo LoadLocalInstance(UserInfo user) {
|
||||
UserInfo[] users = GetUsers();
|
||||
UsernameComparer comp = new UsernameComparer();
|
||||
for(int i = 0; i < users.Length; i++) {
|
||||
if(comp.Compare(users[i], user) == 0) return users[i] as LocalUserInfo;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for a User.
|
||||
/// </summary>
|
||||
/// <param name="user">The User to search for.</param>
|
||||
/// <returns>True if the User already exists.</returns>
|
||||
private bool UserExists(UserInfo user) {
|
||||
UserInfo[] users = GetUsers();
|
||||
UsernameComparer comp = new UsernameComparer();
|
||||
for(int i = 0; i < users.Length; i++) {
|
||||
if(comp.Compare(users[i], user) == 0) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new User.
|
||||
/// </summary>
|
||||
/// <param name="username">The Username.</param>
|
||||
/// <param name="displayName">The display name (can be <c>null</c>).</param>
|
||||
/// <param name="password">The Password.</param>
|
||||
/// <param name="email">The Email address.</param>
|
||||
/// <param name="active">A value specifying whether or not the account is active.</param>
|
||||
/// <param name="dateTime">The Account creation Date/Time.</param>
|
||||
/// <returns>The correct <see cref="T:UserInfo"/> object or <c>null</c>.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <b>username</b>, <b>password</b> or <b>email</b> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <b>username</b>, <b>password</b> or <b>email</b> are empty.</exception>
|
||||
public UserInfo AddUser(string username, string displayName, string password, string email, bool active, DateTime dateTime) {
|
||||
if(username == null) throw new ArgumentNullException("username");
|
||||
if(username.Length == 0) throw new ArgumentException("Username cannot be empty", "username");
|
||||
if(password == null) throw new ArgumentNullException("password");
|
||||
if(password.Length == 0) throw new ArgumentException("Password cannot be empty", "password");
|
||||
if(email == null) throw new ArgumentNullException("email");
|
||||
if(email.Length == 0) throw new ArgumentException("Email cannot be empty", "email");
|
||||
|
||||
lock(this) {
|
||||
if(UserExists(new UserInfo(username, displayName, "", true, DateTime.Now, this))) return null;
|
||||
|
||||
BackupUsersFile();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append(username);
|
||||
sb.Append("|");
|
||||
sb.Append(Hash.Compute(password));
|
||||
sb.Append("|");
|
||||
sb.Append(email);
|
||||
sb.Append("|");
|
||||
sb.Append(active ? "ACTIVE" : "INACTIVE");
|
||||
sb.Append("|");
|
||||
sb.Append(dateTime.ToString("yyyy'/'MM'/'dd' 'HH':'mm':'ss"));
|
||||
// ADMIN|USER no more used in version 3.0
|
||||
//sb.Append("|");
|
||||
//sb.Append(admin ? "ADMIN" : "USER");
|
||||
if(!string.IsNullOrEmpty(displayName)) {
|
||||
sb.Append("|");
|
||||
sb.Append(displayName);
|
||||
}
|
||||
sb.Append("\r\n");
|
||||
File.AppendAllText(GetFullPath(UsersFile), sb.ToString());
|
||||
|
||||
usersCache = null;
|
||||
|
||||
return new LocalUserInfo(username, displayName, email, active, dateTime, this, Hash.Compute(password));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies a User.
|
||||
/// </summary>
|
||||
/// <param name="user">The Username of the user to modify.</param>
|
||||
/// <param name="newDisplayName">The new display name (can be <c>null</c>).</param>
|
||||
/// <param name="newPassword">The new Password (<c>null</c> or blank to keep the current password).</param>
|
||||
/// <param name="newEmail">The new Email address.</param>
|
||||
/// <param name="newActive">A value indicating whether the account is active.</param>
|
||||
/// <returns>The correct <see cref="T:UserInfo"/> object or <c>null</c>.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <b>user</b> or <b>newEmail</b> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <b>newEmail</b> is empty.</exception>
|
||||
public UserInfo ModifyUser(UserInfo user, string newDisplayName, string newPassword, string newEmail, bool newActive) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
if(newEmail == null) throw new ArgumentNullException("newEmail");
|
||||
if(newEmail.Length == 0) throw new ArgumentException("New Email cannot be empty", "newEmail");
|
||||
|
||||
lock(this) {
|
||||
LocalUserInfo local = LoadLocalInstance(user);
|
||||
if(local == null) return null;
|
||||
|
||||
UserInfo[] allUsers = GetUsers();
|
||||
UsernameComparer comp = new UsernameComparer();
|
||||
|
||||
usersCache = null;
|
||||
|
||||
for(int i = 0; i < allUsers.Length; i++) {
|
||||
if(comp.Compare(allUsers[i], user) == 0) {
|
||||
LocalUserInfo result = new LocalUserInfo(user.Username, newDisplayName, newEmail,
|
||||
newActive, user.DateTime, this,
|
||||
string.IsNullOrEmpty(newPassword) ? local.PasswordHash : Hash.Compute(newPassword));
|
||||
result.Groups = allUsers[i].Groups;
|
||||
allUsers[i] = result;
|
||||
DumpUsers(allUsers);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a User.
|
||||
/// </summary>
|
||||
/// <param name="user">The User to remove.</param>
|
||||
/// <returns>True if the User has been removed successfully.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <b>user</b> is <c>null</c>.</exception>
|
||||
public bool RemoveUser(UserInfo user) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
|
||||
lock(this) {
|
||||
UserInfo[] users = GetUsers();
|
||||
UsernameComparer comp = new UsernameComparer();
|
||||
int idx = -1;
|
||||
for(int i = 0; i < users.Length; i++) {
|
||||
if(comp.Compare(users[i], user) == 0) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(idx < 0) return false;
|
||||
|
||||
// Remove user's data
|
||||
string lowercaseUsername = user.Username.ToLowerInvariant();
|
||||
string[] lines = File.ReadAllLines(GetFullPath(UsersDataFile));
|
||||
List<string> newLines = new List<string>(lines.Length);
|
||||
string[] fields;
|
||||
for(int i = 0; i < lines.Length; i++) {
|
||||
fields = lines[i].Split('|');
|
||||
if(fields[0].ToLowerInvariant() != lowercaseUsername) {
|
||||
newLines.Add(lines[i]);
|
||||
}
|
||||
}
|
||||
File.WriteAllLines(GetFullPath(UsersDataFile), newLines.ToArray());
|
||||
|
||||
// Remove user
|
||||
List<UserInfo> tmp = new List<UserInfo>(users);
|
||||
tmp.Remove(tmp[idx]);
|
||||
DumpUsers(tmp.ToArray());
|
||||
|
||||
usersCache = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void BackupUsersFile() {
|
||||
lock(this) {
|
||||
File.Copy(GetFullPath(UsersFile),
|
||||
GetFullPath(Path.GetFileNameWithoutExtension(UsersFile) +
|
||||
".bak" + Path.GetExtension(UsersFile)), true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes on disk all the Users.
|
||||
/// </summary>
|
||||
/// <param name="users">The User list.</param>
|
||||
/// <remarks>This method does not lock resources, therefore a lock is need in the caller.</remarks>
|
||||
private void DumpUsers(UserInfo[] users) {
|
||||
lock(this) {
|
||||
BackupUsersFile();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for(int i = 0; i < users.Length; i++) {
|
||||
LocalUserInfo u = (LocalUserInfo)users[i];
|
||||
sb.Append(u.Username);
|
||||
sb.Append("|");
|
||||
sb.Append(u.PasswordHash);
|
||||
sb.Append("|");
|
||||
sb.Append(u.Email);
|
||||
sb.Append("|");
|
||||
sb.Append(u.Active ? "ACTIVE" : "INACTIVE");
|
||||
sb.Append("|");
|
||||
sb.Append(u.DateTime.ToString("yyyy'/'MM'/'dd' 'HH':'mm':'ss"));
|
||||
// ADMIN|USER no more used in version 3.0
|
||||
//sb.Append("|");
|
||||
//sb.Append(u.Admin ? "ADMIN" : "USER");
|
||||
if(!string.IsNullOrEmpty(u.DisplayName)) {
|
||||
sb.Append("|");
|
||||
sb.Append(u.DisplayName);
|
||||
}
|
||||
sb.Append("\r\n");
|
||||
}
|
||||
File.WriteAllText(GetFullPath(UsersFile), sb.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a user group.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the group to find.</param>
|
||||
/// <returns>The <see cref="T:UserGroup" /> or <c>null</c> if no data is found.</returns>
|
||||
private UserGroup FindGroup(string name) {
|
||||
lock(this) {
|
||||
UserGroup[] allUsers = GetUserGroups();
|
||||
UserGroupComparer comp = new UserGroupComparer();
|
||||
UserGroup target = new UserGroup(name, "", this);
|
||||
|
||||
foreach(UserGroup g in allUsers) {
|
||||
if(comp.Compare(g, target) == 0) return g;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the user groups.
|
||||
/// </summary>
|
||||
/// <returns>All the groups, sorted by name.</returns>
|
||||
public UserGroup[] GetUserGroups() {
|
||||
lock(this) {
|
||||
if(groupsCache == null) {
|
||||
|
||||
string[] lines = File.ReadAllLines(GetFullPath(GroupsFile));
|
||||
|
||||
UserGroup[] result = new UserGroup[lines.Length];
|
||||
|
||||
string[] fields;
|
||||
string[] users;
|
||||
for(int count = 0; count < lines.Length; count++) {
|
||||
// Structure - description can be empty
|
||||
// Name|Description|User1|User2|...
|
||||
|
||||
fields = lines[count].Split('|');
|
||||
users = new string[fields.Length - 2];
|
||||
|
||||
for(int i = 0; i < fields.Length - 2; i++) {
|
||||
users[i] = fields[i + 2];
|
||||
}
|
||||
|
||||
result[count] = new UserGroup(fields[0], fields[1], this);
|
||||
result[count].Users = users;
|
||||
}
|
||||
|
||||
Array.Sort(result, new UserGroupComparer());
|
||||
|
||||
groupsCache = result;
|
||||
}
|
||||
|
||||
return groupsCache;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new user group.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the group.</param>
|
||||
/// <param name="description">The description of the group.</param>
|
||||
/// <returns>The correct <see cref="T:UserGroup"/> object or <c>null</c>.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <b>name</b> or <b>description</b> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <b>name</b> is empty.</exception>
|
||||
public UserGroup AddUserGroup(string name, string description) {
|
||||
if(name == null) throw new ArgumentNullException("name");
|
||||
if(name.Length == 0) throw new ArgumentException("Name cannot be empty", "name");
|
||||
if(description == null) throw new ArgumentNullException("description");
|
||||
|
||||
lock(this) {
|
||||
if(FindGroup(name) != null) return null;
|
||||
|
||||
BackupGroupsFile();
|
||||
|
||||
groupsCache = null;
|
||||
|
||||
// Structure - description can be empty
|
||||
// Name|Description|User1|User2|...
|
||||
|
||||
File.AppendAllText(GetFullPath(GroupsFile),
|
||||
name + "|" + description + "\r\n");
|
||||
|
||||
return new UserGroup(name, description, this);
|
||||
}
|
||||
}
|
||||
|
||||
private void BackupGroupsFile() {
|
||||
lock(this) {
|
||||
File.Copy(GetFullPath(GroupsFile),
|
||||
GetFullPath(Path.GetFileNameWithoutExtension(GroupsFile) +
|
||||
".bak" + Path.GetExtension(GroupsFile)), true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps user groups on disk.
|
||||
/// </summary>
|
||||
/// <param name="groups">The user groups to dump.</param>
|
||||
private void DumpUserGroups(UserGroup[] groups) {
|
||||
lock(this) {
|
||||
StringBuilder sb = new StringBuilder(1000);
|
||||
foreach(UserGroup group in groups) {
|
||||
// Structure - description can be empty
|
||||
// Name|Description|User1|User2|...
|
||||
|
||||
sb.Append(group.Name);
|
||||
sb.Append("|");
|
||||
sb.Append(group.Description);
|
||||
if(group.Users.Length > 0) {
|
||||
foreach(string user in group.Users) {
|
||||
sb.Append("|");
|
||||
sb.Append(user);
|
||||
}
|
||||
}
|
||||
sb.Append("\r\n");
|
||||
}
|
||||
BackupGroupsFile();
|
||||
File.WriteAllText(GetFullPath(GroupsFile), sb.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies a user group.
|
||||
/// </summary>
|
||||
/// <param name="group">The group to modify.</param>
|
||||
/// <param name="description">The new description of the group.</param>
|
||||
/// <returns>The correct <see cref="T:UserGroup"/> object or <c>null</c>.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <b>group</b> or <b>description</b> are <c>null</c>.</exception>
|
||||
public UserGroup ModifyUserGroup(UserGroup group, string description) {
|
||||
if(group == null) throw new ArgumentNullException("group");
|
||||
if(description == null) throw new ArgumentNullException("description");
|
||||
|
||||
lock(this) {
|
||||
UserGroup[] allGroups = GetUserGroups();
|
||||
|
||||
groupsCache = null;
|
||||
|
||||
UserGroupComparer comp = new UserGroupComparer();
|
||||
for(int i = 0; i < allGroups.Length; i++) {
|
||||
if(comp.Compare(allGroups[i], group) == 0) {
|
||||
allGroups[i].Description = description;
|
||||
DumpUserGroups(allGroups);
|
||||
return allGroups[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a user group.
|
||||
/// </summary>
|
||||
/// <param name="group">The group to remove.</param>
|
||||
/// <returns><c>true</c> if the group is removed, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <b>group</b> is <c>null</c>.</exception>
|
||||
public bool RemoveUserGroup(UserGroup group) {
|
||||
if(group == null) throw new ArgumentNullException("group");
|
||||
|
||||
lock(this) {
|
||||
UserGroup[] allGroups = GetUserGroups();
|
||||
|
||||
List<UserGroup> result = new List<UserGroup>(allGroups.Length);
|
||||
|
||||
UserGroupComparer comp = new UserGroupComparer();
|
||||
|
||||
foreach(UserGroup g in allGroups) {
|
||||
if(comp.Compare(g, group) != 0) {
|
||||
result.Add(g);
|
||||
}
|
||||
}
|
||||
|
||||
DumpUserGroups(result.ToArray());
|
||||
|
||||
groupsCache = null;
|
||||
|
||||
return result.Count == allGroups.Length - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the group memberships of a user account.
|
||||
/// </summary>
|
||||
/// <param name="user">The user account.</param>
|
||||
/// <param name="groups">The groups the user account is member of.</param>
|
||||
/// <returns>The correct <see cref="T:UserGroup"/> object or <c>null</c>.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <b>user</b> or <b>groups</b> are <c>null</c>.</exception>
|
||||
public UserInfo SetUserMembership(UserInfo user, string[] groups) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
if(groups == null) throw new ArgumentNullException("groups");
|
||||
|
||||
lock(this) {
|
||||
foreach(string g in groups) {
|
||||
if(FindGroup(g) == null) return null;
|
||||
}
|
||||
|
||||
LocalUserInfo local = LoadLocalInstance(user);
|
||||
if(local == null) return null;
|
||||
|
||||
UserGroup[] allGroups = GetUserGroups();
|
||||
|
||||
List<string> users;
|
||||
for(int i = 0; i < allGroups.Length; i++) {
|
||||
|
||||
users = new List<string>(allGroups[i].Users);
|
||||
|
||||
if(IsSelected(allGroups[i], groups)) {
|
||||
// Current group is one of the selected, add user to it
|
||||
if(!users.Contains(user.Username)) users.Add(user.Username);
|
||||
}
|
||||
else {
|
||||
// Current group is not bound with the user, remove user
|
||||
users.Remove(user.Username);
|
||||
}
|
||||
|
||||
allGroups[i].Users = users.ToArray();
|
||||
}
|
||||
|
||||
groupsCache = null;
|
||||
usersCache = null;
|
||||
|
||||
DumpUserGroups(allGroups);
|
||||
|
||||
LocalUserInfo result = new LocalUserInfo(local.Username, local.DisplayName, local.Email, local.Active,
|
||||
local.DateTime, this, local.PasswordHash);
|
||||
result.Groups = groups;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a user group is contained in an array of user group names.
|
||||
/// </summary>
|
||||
/// <param name="group">The user group to check.</param>
|
||||
/// <param name="groups">The user group names array.</param>
|
||||
/// <returns><c>true</c> if <b>users</b> contains <b>user.Name</b>, <c>false</c> otherwise.</returns>
|
||||
private static bool IsSelected(UserGroup group, string[] groups) {
|
||||
StringComparer comp = StringComparer.OrdinalIgnoreCase;
|
||||
return Array.Find(groups, delegate(string g) { return comp.Compare(g, group.Name) == 0; }) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to login a user directly through the provider.
|
||||
/// </summary>
|
||||
/// <param name="username">The username.</param>
|
||||
/// <param name="password">The password.</param>
|
||||
/// <returns>The correct UserInfo object, or <c>null</c>.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <b>username</b> or <b>password</b> are <c>null</c>.</exception>
|
||||
public UserInfo TryManualLogin(string username, string password) {
|
||||
if(username == null) throw new ArgumentNullException("username");
|
||||
if(password == null) throw new ArgumentNullException("password");
|
||||
|
||||
// Shortcut
|
||||
if(username.Length == 0) return null;
|
||||
if(password.Length == 0) return null;
|
||||
|
||||
lock(this) {
|
||||
string hash = Hash.Compute(password);
|
||||
UserInfo[] all = GetUsers();
|
||||
foreach(UserInfo u in all) {
|
||||
if(u.Active &&
|
||||
//string.Compare(u.Username, username, false, System.Globalization.CultureInfo.InvariantCulture) == 0 &&
|
||||
//string.Compare(((LocalUserInfo)u).PasswordHash, hash, false, System.Globalization.CultureInfo.InvariantCulture) == 0) {
|
||||
string.CompareOrdinal(u.Username, username) == 0 &&
|
||||
string.CompareOrdinal(((LocalUserInfo)u).PasswordHash, hash) == 0) {
|
||||
return u;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to login a user directly through the provider using
|
||||
/// the current HttpContext and without username/password.
|
||||
/// </summary>
|
||||
/// <param name="context">The current HttpContext.</param>
|
||||
/// <returns>The correct UserInfo object, or <c>null</c>.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <b>context</b> is <c>null</c>.</exception>
|
||||
public UserInfo TryAutoLogin(System.Web.HttpContext context) {
|
||||
if(context == null) throw new ArgumentNullException("context");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to retrieve the information about a user account.
|
||||
/// </summary>
|
||||
/// <param name="username">The username.</param>
|
||||
/// <returns>The correct UserInfo object, or <c>null</c>.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <b>username</b> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <b>username</b> is empty.</exception>
|
||||
public UserInfo GetUser(string username) {
|
||||
if(username == null) throw new ArgumentNullException("username");
|
||||
if(username.Length == 0) throw new ArgumentException("Username cannot be empty", "username");
|
||||
|
||||
lock(this) {
|
||||
UserInfo[] all = GetUsers();
|
||||
foreach(UserInfo u in all) {
|
||||
if(string.Compare(u.Username, username, false, System.Globalization.CultureInfo.InvariantCulture) == 0) {
|
||||
return u;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to retrieve the information about a user account.
|
||||
/// </summary>
|
||||
/// <param name="email">The email address.</param>
|
||||
/// <returns>The first user found with the specified email address, or <c>null</c>.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <b>email</b> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <b>email</b> is empty.</exception>
|
||||
public UserInfo GetUserByEmail(string email) {
|
||||
if(email == null) throw new ArgumentNullException("email");
|
||||
if(email.Length == 0) throw new ArgumentException("Email cannot be empty", "email");
|
||||
|
||||
lock(this) {
|
||||
foreach(UserInfo user in GetUsers()) {
|
||||
if(user.Email == email) return user;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the provider that a user has logged in through the authentication cookie.
|
||||
/// </summary>
|
||||
/// <param name="user">The user who has logged in.</param>
|
||||
/// <exception cref="ArgumentNullException">If <b>user</b> is <c>null</c>.</exception>
|
||||
public void NotifyCookieLogin(UserInfo user) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the provider that a user has logged out.
|
||||
/// </summary>
|
||||
/// <param name="user">The user who has logged out.</param>
|
||||
/// <exception cref="ArgumentNullException">If <b>user</b> is <c>null</c>.</exception>
|
||||
public void NotifyLogout(UserInfo user) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores a user data element, overwriting the previous one if present.
|
||||
/// </summary>
|
||||
/// <param name="user">The user the data belongs to.</param>
|
||||
/// <param name="key">The key of the data element (case insensitive).</param>
|
||||
/// <param name="value">The value of the data element, <c>null</c> for deleting the data.</param>
|
||||
/// <returns><c>true</c> if the data element is stored, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <b>user</b> or <b>key</b> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <b>key</b> is empty.</exception>
|
||||
public bool StoreUserData(UserInfo user, string key, string value) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
if(key == null) throw new ArgumentNullException("key");
|
||||
if(key.Length == 0) throw new ArgumentException("Key cannot be empty", "key");
|
||||
|
||||
// Format
|
||||
// User|Key|Value
|
||||
|
||||
lock(this) {
|
||||
if(GetUser(user.Username) == null) return false;
|
||||
|
||||
// Find a previously existing key and replace it if found
|
||||
// If not found, add a new line
|
||||
|
||||
string lowercaseUsername = user.Username.ToLowerInvariant();
|
||||
string lowercaseKey = key.ToLowerInvariant();
|
||||
|
||||
string[] lines = File.ReadAllLines(GetFullPath(UsersDataFile));
|
||||
|
||||
string[] fields;
|
||||
for(int i = 0; i < lines.Length; i++) {
|
||||
fields = lines[i].Split('|');
|
||||
if(fields[0].ToLowerInvariant() == lowercaseUsername && fields[1].ToLowerInvariant() == lowercaseKey) {
|
||||
if(value != null) {
|
||||
// Replace the value, then save file
|
||||
lines[i] = fields[0] + "|" + fields[1] + "|" + value;
|
||||
}
|
||||
else {
|
||||
// Remove the element
|
||||
string[] newLines = new string[lines.Length - 1];
|
||||
Array.Copy(lines, newLines, i);
|
||||
Array.Copy(lines, i + 1, newLines, i, lines.Length - i - 1);
|
||||
lines = newLines;
|
||||
}
|
||||
File.WriteAllLines(GetFullPath(UsersDataFile), lines);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If the program gets here, the element was not present, append it
|
||||
File.AppendAllText(GetFullPath(UsersDataFile), user.Username + "|" + key + "|" + value + "\r\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a user data element, if any.
|
||||
/// </summary>
|
||||
/// <param name="user">The user the data belongs to.</param>
|
||||
/// <param name="key">The key of the data element.</param>
|
||||
/// <returns>The value of the data element, or <c>null</c> if the element is not found.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <b>user</b> or <b>key</b> are <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <b>key</b> is empty.</exception>
|
||||
public string RetrieveUserData(UserInfo user, string key) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
if(key == null) throw new ArgumentNullException("key");
|
||||
if(key.Length == 0) throw new ArgumentException("Key cannot be empty", "key");
|
||||
|
||||
lock(this) {
|
||||
string lowercaseUsername = user.Username.ToLowerInvariant();
|
||||
string lowercaseKey = key.ToLowerInvariant();
|
||||
|
||||
string[] lines = File.ReadAllLines(GetFullPath(UsersDataFile));
|
||||
|
||||
string[] fields;
|
||||
foreach(string line in lines) {
|
||||
fields = line.Split('|');
|
||||
|
||||
if(fields[0].ToLowerInvariant() == lowercaseUsername && fields[1].ToLowerInvariant() == lowercaseKey) {
|
||||
return fields[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all the user data elements for a user.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>The user data elements (key->value).</returns>
|
||||
/// <exception cref="ArgumentNullException">If <b>user</b> is <c>null</c>.</exception>
|
||||
public IDictionary<string, string> RetrieveAllUserData(UserInfo user) {
|
||||
if(user == null) throw new ArgumentNullException("user");
|
||||
|
||||
lock(this) {
|
||||
string lowercaseUsername = user.Username.ToLowerInvariant();
|
||||
|
||||
string[] lines = File.ReadAllLines(GetFullPath(UsersDataFile));
|
||||
|
||||
Dictionary<string, string> result = new Dictionary<string, string>(10);
|
||||
|
||||
string[] fields;
|
||||
foreach(string line in lines) {
|
||||
fields = line.Split('|');
|
||||
|
||||
if(fields[0].ToLowerInvariant() == lowercaseUsername) {
|
||||
result.Add(fields[1], fields[2]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the users that have the specified element in their data.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the data.</param>
|
||||
/// <returns>The users and the data.</returns>
|
||||
/// <exception cref="ArgumentNullException">If <b>key</b> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">If <b>key</b> is empty.</exception>
|
||||
public IDictionary<UserInfo, string> GetUsersWithData(string key) {
|
||||
if(key == null) throw new ArgumentNullException("key");
|
||||
if(key.Length == 0) throw new ArgumentException("Key cannot be empty", "key");
|
||||
|
||||
lock(this) {
|
||||
UserInfo[] allUsers = GetUsers();
|
||||
|
||||
string[] lines = File.ReadAllLines(GetFullPath(UsersDataFile));
|
||||
|
||||
Dictionary<UserInfo, string> result = new Dictionary<UserInfo, string>(lines.Length / 4);
|
||||
|
||||
string[] fields;
|
||||
foreach(string line in lines) {
|
||||
fields = line.Split('|');
|
||||
|
||||
if(fields[1] == key) {
|
||||
UserInfo currentUser = Array.Find(allUsers, delegate(UserInfo user) {
|
||||
return user.Username == fields[0];
|
||||
});
|
||||
if(currentUser != null) result.Add(currentUser, fields[2]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
64
Documentation.shfb
Normal file
64
Documentation.shfb
Normal file
|
@ -0,0 +1,64 @@
|
|||
<project schemaVersion="1.6.0.7">
|
||||
<assemblies>
|
||||
<assembly assemblyPath=".\PluginPack\bin\Release\PluginPack.dll" xmlCommentsPath=".\PluginPack\bin\Release\PluginPack.xml" commentsOnly="False" />
|
||||
<assembly assemblyPath=".\AclEngine\bin\Release\ScrewTurn.Wiki.AclEngine.dll" xmlCommentsPath=".\AclEngine\bin\Release\ScrewTurn.Wiki.AclEngine.xml" commentsOnly="False" />
|
||||
<assembly assemblyPath=".\Core\bin\Release\ScrewTurn.Wiki.Core.dll" xmlCommentsPath=".\Core\bin\Release\ScrewTurn.Wiki.Core.xml" commentsOnly="False" />
|
||||
<assembly assemblyPath=".\Core\bin\Release\ScrewTurn.Wiki.PluginFramework.dll" xmlCommentsPath=".\Core\bin\Release\ScrewTurn.Wiki.PluginFramework.xml" commentsOnly="False" />
|
||||
<assembly assemblyPath=".\SqlProvidersCommon\bin\Release\ScrewTurn.Wiki.Plugins.SqlCommon.dll" xmlCommentsPath=".\SqlProvidersCommon\bin\Release\ScrewTurn.Wiki.Plugins.SqlCommon.xml" commentsOnly="False" />
|
||||
<assembly assemblyPath=".\SearchEngine\bin\Release\ScrewTurn.Wiki.SearchEngine.dll" xmlCommentsPath=".\SearchEngine\bin\Release\ScrewTurn.Wiki.SearchEngine.xml" commentsOnly="False" />
|
||||
<assembly assemblyPath=".\SqlServerProviders\bin\Release\SqlServerProviders.dll" xmlCommentsPath=".\SqlServerProviders\bin\Release\SqlServerProviders.xml" commentsOnly="False" />
|
||||
</assemblies>
|
||||
<namespaceSummaries>
|
||||
<namespaceSummaryItem name="" isDocumented="False" />
|
||||
<namespaceSummaryItem name="ScrewTurn.Wiki" isDocumented="True">Namespace containing the ScrewTurn Wiki engine.</namespaceSummaryItem>
|
||||
<namespaceSummaryItem name="ScrewTurn.Wiki.AclEngine" isDocumented="True">Namespace containing objects that implement the ACL Engine.</namespaceSummaryItem>
|
||||
<namespaceSummaryItem name="ScrewTurn.Wiki.ImportWiki" isDocumented="True">Namespace containing objects that are used to import data from other wiki engines.</namespaceSummaryItem>
|
||||
<namespaceSummaryItem name="ScrewTurn.Wiki.PluginFramework" isDocumented="True">Namespace containing base types used to build the ScrewTurn Wiki engine core and plugins.</namespaceSummaryItem>
|
||||
<namespaceSummaryItem name="ScrewTurn.Wiki.PluginPack" isDocumented="True" />
|
||||
<namespaceSummaryItem name="ScrewTurn.Wiki.Plugins.PluginPack" isDocumented="True">Namespace containing generic helper providers.</namespaceSummaryItem>
|
||||
<namespaceSummaryItem name="ScrewTurn.Wiki.Plugins.SqlCommon" isDocumented="True">Namespace containing base types used to build the SQL-based storage providers.</namespaceSummaryItem>
|
||||
<namespaceSummaryItem name="ScrewTurn.Wiki.Plugins.SqlServer" isDocumented="True">Namespace containing SQL Server storage providers.</namespaceSummaryItem>
|
||||
<namespaceSummaryItem name="ScrewTurn.Wiki.SearchEngine" isDocumented="True">Namespace containing base types used for building an extensible index-based search engine.</namespaceSummaryItem>
|
||||
</namespaceSummaries>
|
||||
<ProjectSummary>ScrewTurn Wiki documentation.</ProjectSummary>
|
||||
<MissingTags>Summary, Parameter, Returns, AutoDocumentCtors, Namespace, TypeParameter</MissingTags>
|
||||
<VisibleItems>Attributes, InheritedMembers, InheritedFrameworkMembers, Protected, SealedProtected</VisibleItems>
|
||||
<HtmlHelp1xCompilerPath path="%ProgramFiles%\HTML Help Workshop\" />
|
||||
<HtmlHelp2xCompilerPath path="" />
|
||||
<OutputPath>.\Help\</OutputPath>
|
||||
<SandcastlePath path="%ProgramFiles%\Sandcastle\" />
|
||||
<WorkingPath path="" />
|
||||
<CleanIntermediates>True</CleanIntermediates>
|
||||
<KeepLogFile>True</KeepLogFile>
|
||||
<BuildLogFile path="" />
|
||||
<HelpFileFormat>HtmlHelp1x</HelpFileFormat>
|
||||
<CppCommentsFixup>False</CppCommentsFixup>
|
||||
<FrameworkVersion>2.0.50727</FrameworkVersion>
|
||||
<IndentHtml>False</IndentHtml>
|
||||
<Preliminary>True</Preliminary>
|
||||
<RootNamespaceContainer>False</RootNamespaceContainer>
|
||||
<RootNamespaceTitle />
|
||||
<HelpTitle>ScrewTurn Wiki Developer's Documentation</HelpTitle>
|
||||
<HtmlHelpName>ScrewTurnWiki</HtmlHelpName>
|
||||
<Language>en-US</Language>
|
||||
<CopyrightHref>http://www.screwturn.eu</CopyrightHref>
|
||||
<CopyrightText>Copyright 2006-2009 Dario Solera</CopyrightText>
|
||||
<FeedbackEMailAddress />
|
||||
<FeedbackEMailLinkText />
|
||||
<HeaderText>ScrewTurn Wiki Developer's Documentation</HeaderText>
|
||||
<FooterText />
|
||||
<ProjectLinkType>Local</ProjectLinkType>
|
||||
<SdkLinkType>Msdn</SdkLinkType>
|
||||
<SdkLinkTarget>Blank</SdkLinkTarget>
|
||||
<PresentationStyle>Prototype</PresentationStyle>
|
||||
<NamingMethod>Guid</NamingMethod>
|
||||
<SyntaxFilters>Standard</SyntaxFilters>
|
||||
<ShowFeedbackControl>False</ShowFeedbackControl>
|
||||
<BinaryTOC>True</BinaryTOC>
|
||||
<IncludeFavorites>False</IncludeFavorites>
|
||||
<CollectionTocStyle>Hierarchical</CollectionTocStyle>
|
||||
<IncludeStopWordList>True</IncludeStopWordList>
|
||||
<PlugInNamespaces>ms.vsipcc+, ms.vsexpresscc+</PlugInNamespaces>
|
||||
<HelpFileVersion>3.0.0.149</HelpFileVersion>
|
||||
<ContentPlacement>BelowNamespaces</ContentPlacement>
|
||||
</project>
|
BIN
Help/ScrewTurnWiki.chm
Normal file
BIN
Help/ScrewTurnWiki.chm
Normal file
Binary file not shown.
7
Install - Readme.txt
Normal file
7
Install - Readme.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
|
||||
In order to install and run ScrewTurn Wiki, you must first compile the application as described
|
||||
in the Build directory, then follow the instructions that are generated along with the binary packages.
|
||||
|
||||
Alternatively, you can download the appropriate compiled package from the application website [1].
|
||||
|
||||
[1] http://www.screwturn.eu
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue