diff --git a/AssemblyInfoCommon.cs b/AssemblyInfoCommon.cs new file mode 100644 index 0000000..1f933db --- /dev/null +++ b/AssemblyInfoCommon.cs @@ -0,0 +1,3 @@ +using System.Reflection; + +[assembly: AssemblyCopyright("Copyright © Andrey Shchekin 2010–2011")] \ No newline at end of file diff --git a/Core.Tests/Core.Tests.csproj b/Core.Tests/Core.Tests.csproj index 80ab9d9..a7ccbd5 100644 --- a/Core.Tests/Core.Tests.csproj +++ b/Core.Tests/Core.Tests.csproj @@ -1,86 +1,89 @@ - - - - Debug - AnyCPU - 9.0.21022 - 2.0 - {93813FFE-7AD9-40A3-96DF-A00A8C5137F8} - {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Library - Properties - AshMind.Gallery.Core.Tests - AshMind.Gallery.Core.Tests - v4.0 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - ..\.Libraries\Moq.dll - - - - - - - - 4.0 - - - 4.0 - - - 4.0 - - - - - - - - - - - - - - {BF5A70D6-912D-472E-A1F7-607B88F062BB} - Core - - - - - - - - - - - + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {93813FFE-7AD9-40A3-96DF-A00A8C5137F8} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Library + Properties + AshMind.Gallery.Core.Tests + AshMind.Gallery.Core.Tests + v4.0 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + ..\.Libraries\Moq.dll + + + + + + + + 4.0 + + + 4.0 + + + 4.0 + + + + + Properties\AssemblyInfoCommon.cs + + + + + + + + + + + + {BF5A70D6-912D-472E-A1F7-607B88F062BB} + Core + + + + + + + + + + + \ No newline at end of file diff --git a/Core.Tests/IO/MemoryFile.cs b/Core.Tests/IO/MemoryFile.cs index 9b5b341..7c53942 100644 --- a/Core.Tests/IO/MemoryFile.cs +++ b/Core.Tests/IO/MemoryFile.cs @@ -1,86 +1,84 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; - -using AshMind.Gallery.Core.IO; - -namespace AshMind.Gallery.Core.Tests.IO { - public class MemoryFile : IFile { - private MemoryStream stream = new MemoryStream(); - - public MemoryFile() { - this.Exists = true; - } - - public MemoryFile(MemoryLocation location, string name, bool exists) { - this.Location = location; - this.Name = name; - this.Exists = exists; - } - - public DateTimeOffset GetLastWriteTime() { - throw new NotImplementedException(); - } - - private void ReopenStream() { - var bytes = this.stream.ToArray(); - this.stream = new MemoryStream(); - this.stream.Write(bytes, 0, bytes.Length); - } - - public Stream Read(FileLockMode lockMode) { - if (!this.stream.CanRead) // disposed - this.ReopenStream(); - - this.stream.Seek(0, SeekOrigin.Begin); - return this.stream; - } - - public Stream Open(FileLockMode lockMode, FileOpenMode openMode) { - if (!this.stream.CanWrite) // disposed - this.ReopenStream(); - - if (openMode == FileOpenMode.Append) { - this.stream.Seek(0, SeekOrigin.End); - return this.stream; - } - - if (openMode == FileOpenMode.ReadOrWrite) { - this.stream.Seek(0, SeekOrigin.Begin); - return this.stream; - } - - if (openMode == FileOpenMode.Recreate) { - this.stream = new MemoryStream(); - return this.stream; - } - - throw new ArgumentOutOfRangeException("openMode"); - } - - public string Name { get; set; } - - public string Path { - get { throw new NotImplementedException(); } - } - - public bool Exists { get; set; } - - public bool IsHidden() { - throw new NotImplementedException(); - } - - public void SetHidden(bool value) { - throw new NotImplementedException(); - } - - public byte[] GetBytes() { - return this.stream.ToArray(); - } - - public ILocation Location { get; set; } - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using AshMind.Gallery.Core.IO; + +namespace AshMind.Gallery.Core.Tests.IO { + public class MemoryFile : IFile { + private MemoryStream stream = new MemoryStream(); + + public MemoryFile() { + this.Exists = true; + } + + public MemoryFile(MemoryLocation location, string name, bool exists) { + this.Location = location; + this.Name = name; + this.Exists = exists; + } + + public DateTimeOffset GetLastWriteTime() { + throw new NotImplementedException(); + } + + private void ReopenStream() { + var bytes = this.stream.ToArray(); + this.stream = new MemoryStream(); + this.stream.Write(bytes, 0, bytes.Length); + } + + public Stream Read(FileLockMode lockMode) { + if (!this.stream.CanRead) // disposed + this.ReopenStream(); + + this.stream.Seek(0, SeekOrigin.Begin); + return this.stream; + } + + public Stream Open(FileLockMode lockMode, FileOpenMode openMode) { + if (!this.stream.CanWrite) // disposed + this.ReopenStream(); + + if (openMode == FileOpenMode.Append) { + this.stream.Seek(0, SeekOrigin.End); + return this.stream; + } + + if (openMode == FileOpenMode.ReadOrWrite) { + this.stream.Seek(0, SeekOrigin.Begin); + return this.stream; + } + + if (openMode == FileOpenMode.Recreate) { + this.stream = new MemoryStream(); + return this.stream; + } + + throw new ArgumentOutOfRangeException("openMode"); + } + + public string Name { get; set; } + + public string Path { + get { throw new NotImplementedException(); } + } + + public bool Exists { get; set; } + + public bool IsHidden() { + throw new NotImplementedException(); + } + + public void SetHidden(bool value) { + throw new NotImplementedException(); + } + + public byte[] GetBytes() { + return this.stream.ToArray(); + } + + public ILocation Location { get; set; } + } +} diff --git a/Core.Tests/IO/MemoryFileSystem.cs b/Core.Tests/IO/MemoryFileSystem.cs index 26f3451..0c7046a 100644 --- a/Core.Tests/IO/MemoryFileSystem.cs +++ b/Core.Tests/IO/MemoryFileSystem.cs @@ -1,34 +1,33 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Gallery.Core.IO; - -namespace AshMind.Gallery.Core.Tests.IO { - public class MemoryFileSystem : IFileSystem { - public IEnumerable GetLocations(string root) { - throw new NotImplementedException(); - } - - public bool IsFileName(string pathOrFileName) { - throw new NotImplementedException(); - } - - public string BuildPath(params string[] parts) { - throw new NotImplementedException(); - } - - public IFile GetFile(string path, bool nullUnlessExists = true) { - throw new NotImplementedException(); - } - - public bool IsLocation(string path) { - throw new NotImplementedException(); - } - - public ILocation GetLocation(string path, ActionIfMissing actionIfMissing) { - return new MemoryLocation { Path = path }; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Gallery.Core.IO; + +namespace AshMind.Gallery.Core.Tests.IO { + public class MemoryFileSystem : IFileSystem { + public IEnumerable GetLocations(string root) { + throw new NotImplementedException(); + } + + public bool IsFileName(string pathOrFileName) { + throw new NotImplementedException(); + } + + public string BuildPath(params string[] parts) { + throw new NotImplementedException(); + } + + public IFile GetFile(string path, bool nullUnlessExists = true) { + throw new NotImplementedException(); + } + + public bool IsLocation(string path) { + throw new NotImplementedException(); + } + + public ILocation GetLocation(string path, ActionIfMissing actionIfMissing) { + return new MemoryLocation { Path = path }; + } + } +} diff --git a/Core.Tests/IO/MemoryLocation.cs b/Core.Tests/IO/MemoryLocation.cs index a667b5f..2001719 100644 --- a/Core.Tests/IO/MemoryLocation.cs +++ b/Core.Tests/IO/MemoryLocation.cs @@ -1,63 +1,62 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Extensions; - -using AshMind.Gallery.Core.IO; - -namespace AshMind.Gallery.Core.Tests.IO { - public class MemoryLocation : ILocation, IEnumerable { - private readonly IDictionary files = new Dictionary(); - - public MemoryLocation() { - this.Name = string.Empty; - this.Path = string.Empty; - } - - public void Add(string fileName, IFile file) { - this.files.Add(fileName, file); - } - - public void Add(IFile file) { - this.files.Add(file.Name, file); - } - - public IFile GetFile(string name, bool nullUnlessExists = true) { - var file = this.files.GetValueOrDefault(name); - if (!nullUnlessExists) - file = file ?? new MemoryFile(this, name, false); - - return file; - } - - public ILocation GetLocation(string path, ActionIfMissing actionIfMissing = ActionIfMissing.ReturnNull) { - return new MemoryLocation { Path = this.Path + "/" + path }; - } - - public IEnumerable GetFiles() { - return this.files.Values; - } - - public string Name { get; set; } - public string Path { get; set; } - - public bool IsHidden() { - throw new NotImplementedException(); - } - - public void SetHidden(bool value) { - throw new NotImplementedException(); - } - - IEnumerator IEnumerable.GetEnumerator() { - return this.files.GetEnumerator(); - } - - public IEnumerable GetLocations(bool recursive) { - throw new NotImplementedException(); - } - } -} +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Extensions; + +using AshMind.Gallery.Core.IO; + +namespace AshMind.Gallery.Core.Tests.IO { + public class MemoryLocation : ILocation, IEnumerable { + private readonly IDictionary files = new Dictionary(); + + public MemoryLocation() { + this.Name = string.Empty; + this.Path = string.Empty; + } + + public void Add(string fileName, IFile file) { + this.files.Add(fileName, file); + } + + public void Add(IFile file) { + this.files.Add(file.Name, file); + } + + public IFile GetFile(string name, bool nullUnlessExists = true) { + var file = this.files.GetValueOrDefault(name); + if (!nullUnlessExists) + file = file ?? new MemoryFile(this, name, false); + + return file; + } + + public ILocation GetLocation(string path, ActionIfMissing actionIfMissing = ActionIfMissing.ReturnNull) { + return new MemoryLocation { Path = this.Path + "/" + path }; + } + + public IEnumerable GetFiles() { + return this.files.Values; + } + + public string Name { get; set; } + public string Path { get; set; } + + public bool IsHidden() { + throw new NotImplementedException(); + } + + public void SetHidden(bool value) { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() { + return this.files.GetEnumerator(); + } + + public IEnumerable GetLocations(bool recursive) { + throw new NotImplementedException(); + } + } +} diff --git a/Core.Tests/IO/Resource.cs b/Core.Tests/IO/Resource.cs index ecb1db7..76eeefd 100644 --- a/Core.Tests/IO/Resource.cs +++ b/Core.Tests/IO/Resource.cs @@ -1,56 +1,55 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; - -using AshMind.Gallery.Core.IO; - -namespace AshMind.Gallery.Core.Tests.IO { - public class Resource : IFile { - private string resourcePath; - private Assembly assembly; - - public Resource(string resourcePath) { - this.resourcePath = resourcePath; - this.assembly = Assembly.GetExecutingAssembly(); - } - - public DateTimeOffset GetLastWriteTime() { - throw new NotImplementedException(); - } - - public Stream Read(FileLockMode lockMode) { - return this.assembly.GetManifestResourceStream(this.resourcePath); - } - - public Stream Open(FileLockMode lockMode, FileOpenMode openMode) { - throw new NotImplementedException(); - } - - public string Name { - get { throw new NotImplementedException(); } - } - - public string Path { - get { return this.resourcePath; } - } - - public bool IsHidden() { - throw new NotImplementedException(); - } - - public void SetHidden(bool value) { - throw new NotImplementedException(); - } - - public bool Exists { - get { return true; } - } - - public ILocation Location { - get { throw new NotImplementedException(); } - } - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; + +using AshMind.Gallery.Core.IO; + +namespace AshMind.Gallery.Core.Tests.IO { + public class Resource : IFile { + private readonly string resourcePath; + private readonly Assembly assembly; + + public Resource(string resourcePath) { + this.resourcePath = resourcePath; + this.assembly = Assembly.GetExecutingAssembly(); + } + + public DateTimeOffset GetLastWriteTime() { + throw new NotImplementedException(); + } + + public Stream Read(FileLockMode lockMode) { + return this.assembly.GetManifestResourceStream(this.resourcePath); + } + + public Stream Open(FileLockMode lockMode, FileOpenMode openMode) { + throw new NotImplementedException(); + } + + public string Name { + get { throw new NotImplementedException(); } + } + + public string Path { + get { return this.resourcePath; } + } + + public bool IsHidden() { + throw new NotImplementedException(); + } + + public void SetHidden(bool value) { + throw new NotImplementedException(); + } + + public bool Exists { + get { return true; } + } + + public ILocation Location { + get { throw new NotImplementedException(); } + } + } +} diff --git a/Core.Tests/Of.AlbumSupport/AlbumIDProviderTest.cs b/Core.Tests/Of.AlbumSupport/AlbumIDProviderTest.cs index 4864aab..3e3fe3b 100644 --- a/Core.Tests/Of.AlbumSupport/AlbumIDProviderTest.cs +++ b/Core.Tests/Of.AlbumSupport/AlbumIDProviderTest.cs @@ -1,39 +1,35 @@ -using System; -using System.Collections.Generic; -using System.Text; - -using Gallio.Framework; -using MbUnit.Framework; -using MbUnit.Framework.ContractVerifiers; - -using AshMind.Gallery.Core.AlbumSupport; -using AshMind.Gallery.Core.IO; -using AshMind.Gallery.Core.Tests.IO; - -namespace AshMind.Gallery.Core.Tests.Of.AlbumSupport { - [TestFixture] - public class AlbumIDProviderTest { - [Test] - public void TestGetAlbumIDIsConsistent() { - var idmap = new MemoryFile(); - - var testDescriptor = new AlbumDescriptor("testKey", "testPath"); - - var firstProvider = CreateProvider(idmap); - var id = firstProvider.GetAlbumID("test", testDescriptor); - var secondProvider = CreateProvider(idmap); - var resultDescriptor = secondProvider.GetAlbumDescriptor(id); - - Assert.AreEqual(testDescriptor, resultDescriptor); - } - - private AlbumIDProvider CreateProvider(IFile idmap) { - return new AlbumIDProvider( - new MemoryLocation { - { "idmap", idmap } - }, - new MemoryFileSystem() - ); - } - } -} +using System; +using System.Collections.Generic; + +using MbUnit.Framework; + +using AshMind.Gallery.Core.AlbumSupport; +using AshMind.Gallery.Core.IO; +using AshMind.Gallery.Core.Tests.IO; + +namespace AshMind.Gallery.Core.Tests.Of.AlbumSupport { + [TestFixture] + public class AlbumIDProviderTest { + [Test] + public void TestGetAlbumIDIsConsistent() { + var idmap = new MemoryFile(); + + var testDescriptor = new AlbumDescriptor("testKey", "testPath"); + + var firstProvider = CreateProvider(idmap); + var id = firstProvider.GetAlbumID("test", testDescriptor); + var secondProvider = CreateProvider(idmap); + var resultDescriptor = secondProvider.GetAlbumDescriptor(id); + + Assert.AreEqual(testDescriptor, resultDescriptor); + } + + private AlbumIDProvider CreateProvider(IFile idmap) { + return new AlbumIDProvider( + new MemoryLocation { + { "idmap", idmap } + } + ); + } + } +} diff --git a/Core.Tests/Of.Integration/Picasa/PicasaIniParserTest.cs b/Core.Tests/Of.Integration/Picasa/PicasaIniParserTest.cs index 3cf7c56..f0f067e 100644 --- a/Core.Tests/Of.Integration/Picasa/PicasaIniParserTest.cs +++ b/Core.Tests/Of.Integration/Picasa/PicasaIniParserTest.cs @@ -1,46 +1,40 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using Gallio.Framework; -using MbUnit.Framework; -using MbUnit.Framework.ContractVerifiers; - -using Moq; - -using AshMind.Gallery.Core.IO; -using AshMind.Gallery.Core.Integration.Picasa; -using AshMind.Gallery.Core.Tests.IO; - -namespace AshMind.Gallery.Core.Tests.Of.Integration.Picasa { - [TestFixture] - public class PicasaIniParserTest { - [Test] - [Row("picasa.simple.ini", 1, "8f7eee7d92388080", "ashmind_lh", "108")] - [Row("picasa.complex.ini", 1, "ba3dd2b88e998b4e", "ashmind_lh", "4bfdd6100df50504")] - [Row("picasa.complex.ini", 2, "8f7eee7d92388080", "ashmind_lh", "108")] - public void TestLoadFromLoadsContactsCorrectly(string resourceName, int index, string hash, string userCode, string id) { - var picasaIni = new PicasaIniParser().Parse(Resource(resourceName)); - var contact = picasaIni.Contacts.Select(x => new { x.Hash, x.UserCode, x.ID }).ElementAtOrDefault(index - 1); - - Assert.AreEqual( - new { Hash = hash, UserCode = userCode, ID = id }, - contact - ); - } - - [Test] - public void TestLoadFromLoadsFacesCorrectly() { - var picasaIni = new PicasaIniParser().Parse(Resource("picasa.simple.ini")); - Assert.AreElementsEqual( - picasaIni.Items.Select(x => new { FileName = x.Key, Faces = string.Join(";", x.Value.Faces.Select(f => f.ContactHash)) }), - new[] { new { FileName = "DSC00369.JPG", Faces = "8f7eee7d92388080" } } - ); - } - - private Resource Resource(string name) { - return new Resource(this.GetType().Namespace + ".Resources." + name); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using MbUnit.Framework; + +using AshMind.Gallery.Core.Integration.Picasa; +using AshMind.Gallery.Core.Tests.IO; + +namespace AshMind.Gallery.Core.Tests.Of.Integration.Picasa { + [TestFixture] + public class PicasaIniParserTest { + [Test] + [Row("picasa.simple.ini", 1, "8f7eee7d92388080", "ashmind_lh", "108")] + [Row("picasa.complex.ini", 1, "ba3dd2b88e998b4e", "ashmind_lh", "4bfdd6100df50504")] + [Row("picasa.complex.ini", 2, "8f7eee7d92388080", "ashmind_lh", "108")] + public void TestLoadFromLoadsContactsCorrectly(string resourceName, int index, string hash, string userCode, string id) { + var picasaIni = new PicasaIniParser().Parse(Resource(resourceName)); + var contact = picasaIni.Contacts.Select(x => new { x.Hash, x.UserCode, x.ID }).ElementAtOrDefault(index - 1); + + Assert.AreEqual( + new { Hash = hash, UserCode = userCode, ID = id }, + contact + ); + } + + [Test] + public void TestLoadFromLoadsFacesCorrectly() { + var picasaIni = new PicasaIniParser().Parse(Resource("picasa.simple.ini")); + Assert.AreElementsEqual( + picasaIni.Items.Select(x => new { FileName = x.Key, Faces = string.Join(";", x.Value.Faces.Select(f => f.ContactHash)) }), + new[] { new { FileName = "DSC00369.JPG", Faces = "8f7eee7d92388080" } } + ); + } + + private Resource Resource(string name) { + return new Resource(this.GetType().Namespace + ".Resources." + name); + } + } +} diff --git a/Core.Tests/Properties/AssemblyInfo.cs b/Core.Tests/Properties/AssemblyInfo.cs index 0b28a58..26fa5a0 100644 --- a/Core.Tests/Properties/AssemblyInfo.cs +++ b/Core.Tests/Properties/AssemblyInfo.cs @@ -1,35 +1,28 @@ -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("Core.Tests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("Core.Tests")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2010")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// 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("eec44b3a-ec56-45de-b71b-ebc5d040fda1")] - -// 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("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +using System.Reflection; +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("AshMind.Gallery.Core.Tests")] +[assembly: AssemblyProduct("AshMind.Gallery.Core.Tests")] + +// 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("eec44b3a-ec56-45de-b71b-ebc5d040fda1")] + +// 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("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Core/Album.cs b/Core/Album.cs index d8147ef..2579c6c 100644 --- a/Core/Album.cs +++ b/Core/Album.cs @@ -1,90 +1,88 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Collections.ObjectModel; - -using AshMind.Extensions; - -using AshMind.Gallery.Core.AlbumSupport; -using AshMind.Gallery.Core.IO; - -namespace AshMind.Gallery.Core { - public class Album : IReadOnlySupport { - public static Album Empty { get; private set; } - - private bool readOnly = false; - private Lazy> lazyItems; - private readonly Lazy lazyDate; - - static Album() { - Empty = new Album(new AlbumDescriptor("", ""), "", () => new AlbumItem[0], null); - } - - public Album(AlbumDescriptor descriptor, string name, IList items, object securableToken) - : this(descriptor, name, () => items, securableToken) - { - } - - public Album(AlbumDescriptor descriptor, string name, Func> getItems, object securableToken) - : this(descriptor, name, new Lazy>(() => getItems().AsReadOnly(), true), securableToken) - { - } - - private Album(AlbumDescriptor descriptor, string name, Lazy> items, object securableToken) { - this.Descriptor = descriptor; - this.Name = name; - this.lazyItems = items; - this.SecurableToken = securableToken; - this.lazyDate = new Lazy( - () => Items.Min(i => (DateTimeOffset?)i.Date) ?? DateTimeOffset.Now, - true - ); - } - - public AlbumDescriptor Descriptor { get; private set; } - public object SecurableToken { get; private set; } - public string Name { get; private set; } - - public DateTimeOffset Date { - get { return this.lazyDate.Value; } - } - - public ReadOnlyCollection Items { - get { return this.lazyItems.Value; } - } - - #region IReadOnlySupport Members - - public void MakeReadOnly() { - if (this.readOnly) - return; - - this.readOnly = true; - this.lazyItems = this.lazyItems.Apply(items => MakeReadOnly(items)); - } - - private void MakeReadOnly(IEnumerable items) { - foreach (var item in items) { - item.MakeReadOnly(); - } - } - - public bool IsReadOnly { - get { return this.readOnly; } - } - - public Album AsWritable() { - if (!this.readOnly) - return this; - - return new Album( - this.Descriptor, this.Name, - () => this.lazyItems.Value.Select(item => item.AsWritable()).ToArray(), - this.SecurableToken - ); - } - - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Collections.ObjectModel; + +using AshMind.Extensions; + +using AshMind.Gallery.Core.AlbumSupport; + +namespace AshMind.Gallery.Core { + public class Album : IReadOnlySupport { + public static Album Empty { get; private set; } + + private bool readOnly; + private Lazy> lazyItems; + private readonly Lazy lazyDate; + + static Album() { + Empty = new Album(new AlbumDescriptor("", ""), "", () => new AlbumItem[0], null); + } + + public Album(AlbumDescriptor descriptor, string name, IList items, object securableToken) + : this(descriptor, name, () => items, securableToken) + { + } + + public Album(AlbumDescriptor descriptor, string name, Func> getItems, object securableToken) + : this(descriptor, name, new Lazy>(() => getItems().AsReadOnly(), true), securableToken) + { + } + + private Album(AlbumDescriptor descriptor, string name, Lazy> items, object securableToken) { + this.Descriptor = descriptor; + this.Name = name; + this.lazyItems = items; + this.SecurableToken = securableToken; + this.lazyDate = new Lazy( + () => Items.Min(i => (DateTimeOffset?)i.Date) ?? DateTimeOffset.Now, + true + ); + } + + public AlbumDescriptor Descriptor { get; private set; } + public object SecurableToken { get; private set; } + public string Name { get; private set; } + + public DateTimeOffset Date { + get { return this.lazyDate.Value; } + } + + public ReadOnlyCollection Items { + get { return this.lazyItems.Value; } + } + + #region IReadOnlySupport Members + + public void MakeReadOnly() { + if (this.readOnly) + return; + + this.readOnly = true; + this.lazyItems = this.lazyItems.Apply(items => MakeReadOnly(items)); + } + + private void MakeReadOnly(IEnumerable items) { + foreach (var item in items) { + item.MakeReadOnly(); + } + } + + public bool IsReadOnly { + get { return this.readOnly; } + } + + public Album AsWritable() { + if (!this.readOnly) + return this; + + return new Album( + this.Descriptor, this.Name, + () => this.lazyItems.Value.Select(item => item.AsWritable()).ToArray(), + this.SecurableToken + ); + } + + #endregion + } +} diff --git a/Core/AlbumItem.cs b/Core/AlbumItem.cs index 125098a..c651215 100644 --- a/Core/AlbumItem.cs +++ b/Core/AlbumItem.cs @@ -1,91 +1,89 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Extensions; - -using AshMind.Gallery.Core.ImageProcessing; -using AshMind.Gallery.Core.IO; -using AshMind.Gallery.Core.Security; - -namespace AshMind.Gallery.Core { - public class AlbumItem : IReadOnlySupport { - private bool readOnly; - private Lazy> lazyComments; - private Lazy lazyPrimaryAlbum; - - public AlbumItem( - IFile file, - string name, - AlbumItemType type, - DateTimeOffset date, - Func> getComments - ) { - this.File = file; - this.Name = name; - this.Type = type; - this.Date = date; - this.lazyComments = new Lazy>(getComments); - this.DeleteProposals = new HashSet(); - } - - public IFile File { get; private set; } - public string Name { get; private set; } - public AlbumItemType Type { get; private set; } - public DateTimeOffset Date { get; private set; } - - public Album PrimaryAlbum { - get { return this.LazyPrimaryAlbum != null ? this.LazyPrimaryAlbum.Value : null; } - } - - public ICollection DeleteProposals { get; private set; } - internal Lazy LazyPrimaryAlbum { - get { return this.lazyPrimaryAlbum; } - set { - if (this.readOnly) - throw new InvalidOperationException("The AlbumItem is read-only."); - - this.lazyPrimaryAlbum = value; - } - } - - public IList Comments { - get { return this.lazyComments.Value; } - } - - public bool IsProposedToBeDeleted { - get { return this.DeleteProposals.Count > 0; } - } - - #region IReadOnlySupport Members - - public void MakeReadOnly() { - if (this.readOnly) - return; - - this.readOnly = true; - this.lazyPrimaryAlbum = this.lazyPrimaryAlbum.Apply(album => album.MakeReadOnly()); - this.lazyComments = this.lazyComments.Apply(comments => comments.AsReadOnly()); - this.DeleteProposals = this.DeleteProposals.ToArray().AsReadOnly(); - } - - public bool IsReadOnly { - get { return this.readOnly; } - } - - public AlbumItem AsWritable() { - if (!this.IsReadOnly) - return this; - - var item = new AlbumItem( - this.File, this.Name, this.Type, this.Date, - () => this.Comments - ); - item.DeleteProposals.AddRange(this.DeleteProposals); - return item; - } - - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Extensions; + +using AshMind.Gallery.Core.IO; +using AshMind.Gallery.Core.Security; + +namespace AshMind.Gallery.Core { + public class AlbumItem : IReadOnlySupport { + private bool readOnly; + private Lazy> lazyComments; + private Lazy lazyPrimaryAlbum; + + public AlbumItem( + IFile file, + string name, + AlbumItemType type, + DateTimeOffset date, + Func> getComments + ) { + this.File = file; + this.Name = name; + this.Type = type; + this.Date = date; + this.lazyComments = new Lazy>(getComments); + this.DeleteProposals = new HashSet(); + } + + public IFile File { get; private set; } + public string Name { get; private set; } + public AlbumItemType Type { get; private set; } + public DateTimeOffset Date { get; private set; } + + public Album PrimaryAlbum { + get { return this.LazyPrimaryAlbum != null ? this.LazyPrimaryAlbum.Value : null; } + } + + public ICollection DeleteProposals { get; private set; } + internal Lazy LazyPrimaryAlbum { + get { return this.lazyPrimaryAlbum; } + set { + if (this.readOnly) + throw new InvalidOperationException("The AlbumItem is read-only."); + + this.lazyPrimaryAlbum = value; + } + } + + public IList Comments { + get { return this.lazyComments.Value; } + } + + public bool IsProposedToBeDeleted { + get { return this.DeleteProposals.Count > 0; } + } + + #region IReadOnlySupport Members + + public void MakeReadOnly() { + if (this.readOnly) + return; + + this.readOnly = true; + this.lazyPrimaryAlbum = this.lazyPrimaryAlbum.Apply(album => album.MakeReadOnly()); + this.lazyComments = this.lazyComments.Apply(comments => comments.AsReadOnly()); + this.DeleteProposals = this.DeleteProposals.ToArray().AsReadOnly(); + } + + public bool IsReadOnly { + get { return this.readOnly; } + } + + public AlbumItem AsWritable() { + if (!this.IsReadOnly) + return this; + + var item = new AlbumItem( + this.File, this.Name, this.Type, this.Date, + () => this.Comments + ); + item.DeleteProposals.AddRange(this.DeleteProposals); + return item; + } + + #endregion + } +} diff --git a/Core/AlbumItemType.cs b/Core/AlbumItemType.cs index a9c13ac..0df96ed 100644 --- a/Core/AlbumItemType.cs +++ b/Core/AlbumItemType.cs @@ -1,14 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core -{ - public enum AlbumItemType - { - Unknown, - Image, - Video - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core +{ + public enum AlbumItemType + { + Unknown, + Image, + Video + } +} diff --git a/Core/AlbumProviderKeys.cs b/Core/AlbumProviderKeys.cs index 9b404f8..2f0fcd5 100644 --- a/Core/AlbumProviderKeys.cs +++ b/Core/AlbumProviderKeys.cs @@ -1,11 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core { - public static class AlbumProviderKeys { - public const string People = "person"; - public const string Default = "path"; - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core { + public static class AlbumProviderKeys { + public const string People = "person"; + public const string Default = "path"; + } +} diff --git a/Core/AlbumSupport/AlbumDescriptor.cs b/Core/AlbumSupport/AlbumDescriptor.cs index b3d8e74..70d9d6e 100644 --- a/Core/AlbumSupport/AlbumDescriptor.cs +++ b/Core/AlbumSupport/AlbumDescriptor.cs @@ -1,33 +1,32 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.AlbumSupport { - public class AlbumDescriptor { - public AlbumDescriptor(string providerKey, string providerSpecificPath) { - this.ProviderKey = providerKey; - this.ProviderSpecificPath = providerSpecificPath; - } - - public string ProviderKey { get; private set; } - public string ProviderSpecificPath { get; private set; } - - public override bool Equals(object obj) { - if (obj.GetType() != typeof(AlbumDescriptor)) - return false; - - var descriptor = obj as AlbumDescriptor; - return this.ProviderKey == descriptor.ProviderKey - && this.ProviderSpecificPath == descriptor.ProviderSpecificPath; - } - - public override int GetHashCode() { - return this.ProviderKey.GetHashCode() ^ this.ProviderSpecificPath.GetHashCode(); - } - - public override string ToString() { - return new { this.ProviderKey, this.ProviderSpecificPath }.ToString(); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.AlbumSupport { + public class AlbumDescriptor { + public AlbumDescriptor(string providerKey, string providerSpecificPath) { + this.ProviderKey = providerKey; + this.ProviderSpecificPath = providerSpecificPath; + } + + public string ProviderKey { get; private set; } + public string ProviderSpecificPath { get; private set; } + + public override bool Equals(object obj) { + if (obj.GetType() != typeof(AlbumDescriptor)) + return false; + + var descriptor = obj as AlbumDescriptor; + return this.ProviderKey == descriptor.ProviderKey + && this.ProviderSpecificPath == descriptor.ProviderSpecificPath; + } + + public override int GetHashCode() { + return this.ProviderKey.GetHashCode() ^ this.ProviderSpecificPath.GetHashCode(); + } + + public override string ToString() { + return new { this.ProviderKey, this.ProviderSpecificPath }.ToString(); + } + } +} diff --git a/Core/AlbumSupport/AlbumFacade.cs b/Core/AlbumSupport/AlbumFacade.cs index cd330c6..f7bbc54 100644 --- a/Core/AlbumSupport/AlbumFacade.cs +++ b/Core/AlbumSupport/AlbumFacade.cs @@ -1,83 +1,76 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -using AshMind.Extensions; - -using AshMind.Gallery.Core.Commenting; -using AshMind.Gallery.Core.ImageProcessing; -using AshMind.Gallery.Core.Internal; -using AshMind.Gallery.Core.IO; -using AshMind.Gallery.Core.Metadata; -using AshMind.Gallery.Core.Security; -using AshMind.Gallery.Core.Integration; - -namespace AshMind.Gallery.Core.AlbumSupport { - public class AlbumFacade : IAlbumFacade { - private readonly IAlbumIDProvider idProvider; - private readonly IDictionary albumProviders; - private readonly IAlbumFilter[] albumFilters; - private readonly IAlbumItemMetadataProvider[] metadataProviders; - - internal ILocation Root { private set; get; } - - public AlbumFacade( - ILocation root, - IAlbumIDProvider idProvider, - IAlbumProvider[] albumProviders, - IAlbumFilter[] albumFilters, - IAlbumItemMetadataProvider[] metadataProviders - ) { - this.Root = root; - - this.idProvider = idProvider; - this.albumProviders = albumProviders.ToDictionary(p => p.ProviderKey); - this.albumFilters = albumFilters; - this.metadataProviders = metadataProviders; - } - - public IEnumerable GetAlbums(string providerKey, IUser user) { - Argument.VerifyNotNullOrEmpty("providerKey", providerKey); - Argument.VerifyNotNull("user", user); - - var locations = GetAlbumLocations(); - return this.albumProviders[providerKey].GetAllAlbums(locations, user); - } - - private IEnumerable GetAlbumLocations() { - return this.Root.GetLocations(true) - .Where(location => !this.albumFilters.Any(a => a.ShouldSkip(location))); - } - - public string GetAlbumID(Album album) { - Argument.VerifyNotNull("album", album); - - return this.idProvider.GetAlbumID(album.Name, album.Descriptor); - } - - public AlbumItem GetItem(string albumID, string itemName, IUser user) { - Argument.VerifyNotNullOrEmpty("itemName", itemName); - - var album = GetAlbum(albumID, user); - return album.Items.FirstOrDefault(item => item.Name == itemName); - } - - public Album GetAlbum(string albumID, IUser user) { - Argument.VerifyNotNullOrEmpty("albumID", albumID); - Argument.VerifyNotNull("user", user); - - var descriptor = this.idProvider.GetAlbumDescriptor(albumID); - var provider = this.albumProviders[descriptor.ProviderKey]; - - return provider.GetAlbum(GetAlbumLocations(), descriptor.ProviderSpecificPath, user); - } - - public void SaveItem(AlbumItem item) { - Argument.VerifyNotNull("item", item); - - this.metadataProviders.ForEach(p => p.SaveMetadata(item)); - } - } +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Extensions; + +using AshMind.Gallery.Core.IO; +using AshMind.Gallery.Core.Security; + +namespace AshMind.Gallery.Core.AlbumSupport { + public class AlbumFacade : IAlbumFacade { + private readonly IAlbumIDProvider idProvider; + private readonly IDictionary albumProviders; + private readonly IAlbumFilter[] albumFilters; + private readonly IAlbumItemMetadataProvider[] metadataProviders; + + internal ILocation Root { private set; get; } + + public AlbumFacade( + ILocation root, + IAlbumIDProvider idProvider, + IEnumerable albumProviders, + IAlbumFilter[] albumFilters, + IAlbumItemMetadataProvider[] metadataProviders + ) { + this.Root = root; + + this.idProvider = idProvider; + this.albumProviders = albumProviders.ToDictionary(p => p.ProviderKey); + this.albumFilters = albumFilters; + this.metadataProviders = metadataProviders; + } + + public IEnumerable GetAlbums(string providerKey, IUser user) { + Argument.VerifyNotNullOrEmpty("providerKey", providerKey); + Argument.VerifyNotNull("user", user); + + var locations = GetAlbumLocations(); + return this.albumProviders[providerKey].GetAllAlbums(locations, user); + } + + private IEnumerable GetAlbumLocations() { + return this.Root.GetLocations(true) + .Where(location => !this.albumFilters.Any(a => a.ShouldSkip(location))); + } + + public string GetAlbumID(Album album) { + Argument.VerifyNotNull("album", album); + + return this.idProvider.GetAlbumID(album.Name, album.Descriptor); + } + + public AlbumItem GetItem(string albumID, string itemName, IUser user) { + Argument.VerifyNotNullOrEmpty("itemName", itemName); + + var album = GetAlbum(albumID, user); + return album.Items.FirstOrDefault(item => item.Name == itemName); + } + + public Album GetAlbum(string albumID, IUser user) { + Argument.VerifyNotNullOrEmpty("albumID", albumID); + Argument.VerifyNotNull("user", user); + + var descriptor = this.idProvider.GetAlbumDescriptor(albumID); + var provider = this.albumProviders[descriptor.ProviderKey]; + + return provider.GetAlbum(GetAlbumLocations(), descriptor.ProviderSpecificPath, user); + } + + public void SaveItem(AlbumItem item) { + Argument.VerifyNotNull("item", item); + + this.metadataProviders.ForEach(p => p.SaveMetadata(item)); + } + } } \ No newline at end of file diff --git a/Core/AlbumSupport/AlbumFactory.cs b/Core/AlbumSupport/AlbumFactory.cs index 7fb5022..7420bc3 100644 --- a/Core/AlbumSupport/AlbumFactory.cs +++ b/Core/AlbumSupport/AlbumFactory.cs @@ -1,31 +1,27 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Gallery.Core.IO; -using AshMind.Gallery.Core.Commenting; - -namespace AshMind.Gallery.Core.AlbumSupport { - public class AlbumFactory { - private readonly IAlbumNameTransform[] nameTransforms; - - public AlbumFactory(IAlbumNameTransform[] nameTransforms) { - this.nameTransforms = nameTransforms; - } - - public Album Create( - AlbumDescriptor descriptor, - string proposedName, Func> itemsFactory, - object securableToken - ) { - var name = nameTransforms.Aggregate(proposedName, (n, t) => t.Transform(n, descriptor)); - - return new Album( - descriptor, - name, itemsFactory, - securableToken - ); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.AlbumSupport { + public class AlbumFactory { + private readonly IAlbumNameTransform[] nameTransforms; + + public AlbumFactory(IAlbumNameTransform[] nameTransforms) { + this.nameTransforms = nameTransforms; + } + + public Album Create( + AlbumDescriptor descriptor, + string proposedName, Func> itemsFactory, + object securableToken + ) { + var name = nameTransforms.Aggregate(proposedName, (n, t) => t.Transform(n, descriptor)); + + return new Album( + descriptor, + name, itemsFactory, + securableToken + ); + } + } +} diff --git a/Core/AlbumSupport/AlbumIDProvider.cs b/Core/AlbumSupport/AlbumIDProvider.cs index 5798da8..148175a 100644 --- a/Core/AlbumSupport/AlbumIDProvider.cs +++ b/Core/AlbumSupport/AlbumIDProvider.cs @@ -1,59 +1,52 @@ -using System; -using System.Collections.Generic; -using System.Collections.Concurrent; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; - -using AshMind.Gallery.Core.IO; - -using File = System.IO.File; - -namespace AshMind.Gallery.Core.AlbumSupport { - internal class AlbumIDProvider : IAlbumIDProvider { - private readonly IFile idMapFile; - private readonly ConcurrentDictionary idMap; - private readonly IFileSystem albumFileSystem; - - public AlbumIDProvider(ILocation dataRoot, IFileSystem albumFileSystem) { - this.idMapFile = dataRoot.GetFile("idmap", false); - this.idMap = new ConcurrentDictionary(this.LoadMap()); - - this.albumFileSystem = albumFileSystem; - } - - private IEnumerable> LoadMap() { - if (!this.idMapFile.Exists) - return Enumerable.Empty>(); - - var lines = this.idMapFile.ReadAllLines(); - return lines.Select(line => line.Split('\t')) - .Select(parts => new KeyValuePair( - parts[0], - new AlbumDescriptor( - parts[1], - parts[2] - ) - )); - } - - public string GetAlbumID(string name, AlbumDescriptor descriptor) { - var pair = this.idMap.Select(p => (KeyValuePair?)p) - .SingleOrDefault(p => p.Value.Equals(descriptor)); - - if (pair != null) - return pair.Value.Key; - - var id = Regex.Replace(name, @"(:?[\s'""]|:)", "_"); - if (this.idMap.TryAdd(id, descriptor)) - this.idMapFile.AppendAllText(id + "\t" + descriptor.ProviderKey + "\t" + descriptor.ProviderSpecificPath + Environment.NewLine); - - return id; - } - - public AlbumDescriptor GetAlbumDescriptor(string albumID) { - return idMap[albumID]; - } - } -} +using System; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Linq; +using System.Text.RegularExpressions; + +using AshMind.Gallery.Core.IO; + +namespace AshMind.Gallery.Core.AlbumSupport { + internal class AlbumIDProvider : IAlbumIDProvider { + private readonly IFile idMapFile; + private readonly ConcurrentDictionary idMap; + + public AlbumIDProvider(ILocation dataRoot) { + this.idMapFile = dataRoot.GetFile("idmap", false); + this.idMap = new ConcurrentDictionary(this.LoadMap()); + } + + private IEnumerable> LoadMap() { + if (!this.idMapFile.Exists) + return Enumerable.Empty>(); + + var lines = this.idMapFile.ReadAllLines(); + return lines.Select(line => line.Split('\t')) + .Select(parts => new KeyValuePair( + parts[0], + new AlbumDescriptor( + parts[1], + parts[2] + ) + )); + } + + public string GetAlbumID(string name, AlbumDescriptor descriptor) { + var pair = this.idMap.Select(p => (KeyValuePair?)p) + .SingleOrDefault(p => p.Value.Equals(descriptor)); + + if (pair != null) + return pair.Value.Key; + + var id = Regex.Replace(name, @"(:?[\s'""]|:)", "_"); + if (this.idMap.TryAdd(id, descriptor)) + this.idMapFile.AppendAllText(id + "\t" + descriptor.ProviderKey + "\t" + descriptor.ProviderSpecificPath + Environment.NewLine); + + return id; + } + + public AlbumDescriptor GetAlbumDescriptor(string albumID) { + return idMap[albumID]; + } + } +} diff --git a/Core/AlbumSupport/AlbumItemFactory.cs b/Core/AlbumSupport/AlbumItemFactory.cs index 39993c8..3d2e6a9 100644 --- a/Core/AlbumSupport/AlbumItemFactory.cs +++ b/Core/AlbumSupport/AlbumItemFactory.cs @@ -1,37 +1,36 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Gallery.Core.IO; -using AshMind.Gallery.Core.Commenting; - -namespace AshMind.Gallery.Core.AlbumSupport { - public class AlbumItemFactory { - private readonly ICommentRepository commentRepository; - private readonly IAlbumItemMetadataProvider[] metadataProviders; - - public AlbumItemFactory( - ICommentRepository commentRepository, - IAlbumItemMetadataProvider[] metadataProviders - ) { - this.commentRepository = commentRepository; - this.metadataProviders = metadataProviders; - } - - public AlbumItem CreateFrom(IFile file, AlbumItemType itemType) { - var item = new AlbumItem( - file, - file.Name, - itemType, - file.GetLastWriteTime(), - () => this.commentRepository.LoadCommentsOf(file.Path) - ); - foreach (var provider in this.metadataProviders) { - provider.LoadMetadataTo(item); - } - - return item; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Gallery.Core.IO; +using AshMind.Gallery.Core.Commenting; + +namespace AshMind.Gallery.Core.AlbumSupport { + public class AlbumItemFactory { + private readonly ICommentRepository commentRepository; + private readonly IAlbumItemMetadataProvider[] metadataProviders; + + public AlbumItemFactory( + ICommentRepository commentRepository, + IAlbumItemMetadataProvider[] metadataProviders + ) { + this.commentRepository = commentRepository; + this.metadataProviders = metadataProviders; + } + + public AlbumItem CreateFrom(IFile file, AlbumItemType itemType) { + var item = new AlbumItem( + file, + file.Name, + itemType, + file.GetLastWriteTime(), + () => this.commentRepository.LoadCommentsOf(file.Path) + ); + foreach (var provider in this.metadataProviders) { + provider.LoadMetadataTo(item); + } + + return item; + } + } +} diff --git a/Core/AlbumSupport/AlbumItemLocationBasedMetadataProvider.cs b/Core/AlbumSupport/AlbumItemLocationBasedMetadataProvider.cs index 5518102..84cf1f0 100644 --- a/Core/AlbumSupport/AlbumItemLocationBasedMetadataProvider.cs +++ b/Core/AlbumSupport/AlbumItemLocationBasedMetadataProvider.cs @@ -1,72 +1,69 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Extensions; - -using AshMind.Gallery.Core.Metadata; -using AshMind.Gallery.Core.Security; - -namespace AshMind.Gallery.Core.AlbumSupport { - public class AlbumItemLocationBasedMetadataProvider : IAlbumItemMetadataProvider { - #region AlbumItemRawMetadata Class - - private class AlbumItemRawMetadata { - public AlbumItemRawMetadata() { - this.DeleteProposals = new List(); - } - - public IList DeleteProposals { get; private set; } - } - - #endregion - - private readonly ILocationMetadataProvider locationMetadataProvider; - private readonly IRepository userGroupRepository; - private readonly IUserGroupSecureReferenceStrategy userGroupReferenceSupport; - - public AlbumItemLocationBasedMetadataProvider( - ILocationMetadataProvider locationMetadataProvider, - IRepository userGroupRepository, - IUserGroupSecureReferenceStrategy userGroupReferenceSupport - ) { - this.locationMetadataProvider = locationMetadataProvider; - this.userGroupRepository = userGroupRepository; - this.userGroupReferenceSupport = userGroupReferenceSupport; - } - - public void LoadMetadataTo(AlbumItem item) { - var metadata = locationMetadataProvider.GetMetadata( - item.File.Location, item.File.Name - ); - if (metadata == null) - return; - - var userGroups = userGroupRepository.Query().ToList(); - - item.DeleteProposals.AddRange( - metadata.DeleteProposals.Select( - p => userGroupReferenceSupport.ResolveReference(p, userGroups) - ).OfType() - ); - } - - public void SaveMetadata(AlbumItem item) { - var metadata = locationMetadataProvider.GetMetadata( - item.File.Location, item.File.Name - ); - if (metadata == null) - metadata = new AlbumItemRawMetadata(); - - metadata.DeleteProposals.Clear(); - metadata.DeleteProposals.AddRange( - item.DeleteProposals.Select(this.userGroupReferenceSupport.GetReference) - ); - - locationMetadataProvider.ApplyMetadata( - item.File.Location, item.File.Name, metadata - ); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Extensions; + +using AshMind.Gallery.Core.Metadata; +using AshMind.Gallery.Core.Security; + +namespace AshMind.Gallery.Core.AlbumSupport { + public class AlbumItemLocationBasedMetadataProvider : IAlbumItemMetadataProvider { + #region AlbumItemRawMetadata Class + + private class AlbumItemRawMetadata { + public AlbumItemRawMetadata() { + this.DeleteProposals = new List(); + } + + public IList DeleteProposals { get; private set; } + } + + #endregion + + private readonly ILocationMetadataProvider locationMetadataProvider; + private readonly IRepository userGroupRepository; + private readonly IUserGroupSecureReferenceStrategy userGroupReferenceSupport; + + public AlbumItemLocationBasedMetadataProvider( + ILocationMetadataProvider locationMetadataProvider, + IRepository userGroupRepository, + IUserGroupSecureReferenceStrategy userGroupReferenceSupport + ) { + this.locationMetadataProvider = locationMetadataProvider; + this.userGroupRepository = userGroupRepository; + this.userGroupReferenceSupport = userGroupReferenceSupport; + } + + public void LoadMetadataTo(AlbumItem item) { + var metadata = locationMetadataProvider.GetMetadata( + item.File.Location, item.File.Name + ); + if (metadata == null) + return; + + var userGroups = userGroupRepository.Query().ToList(); + + item.DeleteProposals.AddRange( + metadata.DeleteProposals.Select( + p => userGroupReferenceSupport.ResolveReference(p, userGroups) + ).OfType() + ); + } + + public void SaveMetadata(AlbumItem item) { + var metadata = locationMetadataProvider.GetMetadata( + item.File.Location, item.File.Name + ); + if (metadata == null) + metadata = new AlbumItemRawMetadata(); + + metadata.DeleteProposals.Clear(); + metadata.DeleteProposals.AddRange( + item.DeleteProposals.Select(this.userGroupReferenceSupport.GetReference) + ); + + locationMetadataProvider.ApplyMetadata(item.File.Location, item.File.Name, metadata); + } + } +} diff --git a/Core/AlbumSupport/GuessItemType.cs b/Core/AlbumSupport/GuessItemType.cs index 0591e61..837638f 100644 --- a/Core/AlbumSupport/GuessItemType.cs +++ b/Core/AlbumSupport/GuessItemType.cs @@ -1,26 +1,25 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.AlbumSupport { - internal static class GuessItemType { - private static class FileExtensions { - public static readonly HashSet OfVideo = new HashSet(StringComparer.InvariantCultureIgnoreCase) { ".mov", ".avi", ".mpg" }; - public static readonly HashSet OfImage = new HashSet(StringComparer.InvariantCultureIgnoreCase) { ".jpg", ".jpeg", ".png", ".gif" }; - } - - public static AlbumItemType Of(string path) { - var extension = Path.GetExtension(path); - - if (FileExtensions.OfVideo.Contains(extension)) - return AlbumItemType.Video; - - if (FileExtensions.OfImage.Contains(extension)) - return AlbumItemType.Image; - - return AlbumItemType.Unknown; - } - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace AshMind.Gallery.Core.AlbumSupport { + internal static class GuessItemType { + private static class FileExtensions { + public static readonly HashSet OfVideo = new HashSet(StringComparer.InvariantCultureIgnoreCase) { ".mov", ".avi", ".mpg" }; + public static readonly HashSet OfImage = new HashSet(StringComparer.InvariantCultureIgnoreCase) { ".jpg", ".jpeg", ".png", ".gif" }; + } + + public static AlbumItemType Of(string path) { + var extension = Path.GetExtension(path); + + if (FileExtensions.OfVideo.Contains(extension)) + return AlbumItemType.Video; + + if (FileExtensions.OfImage.Contains(extension)) + return AlbumItemType.Image; + + return AlbumItemType.Unknown; + } + } +} diff --git a/Core/AlbumSupport/HiddenAlbumFilter.cs b/Core/AlbumSupport/HiddenAlbumFilter.cs index ff4dd35..abb02b1 100644 --- a/Core/AlbumSupport/HiddenAlbumFilter.cs +++ b/Core/AlbumSupport/HiddenAlbumFilter.cs @@ -1,14 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Gallery.Core.IO; - -namespace AshMind.Gallery.Core.AlbumSupport { - public class HiddenAlbumFilter : IAlbumFilter { - public bool ShouldSkip(ILocation location) { - return location.IsHidden(); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Gallery.Core.IO; + +namespace AshMind.Gallery.Core.AlbumSupport { + public class HiddenAlbumFilter : IAlbumFilter { + public bool ShouldSkip(ILocation location) { + return location.IsHidden(); + } + } +} diff --git a/Core/AlbumSupport/IAlbumFilter.cs b/Core/AlbumSupport/IAlbumFilter.cs index 8925e48..abc9c70 100644 --- a/Core/AlbumSupport/IAlbumFilter.cs +++ b/Core/AlbumSupport/IAlbumFilter.cs @@ -1,12 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Gallery.Core.IO; - -namespace AshMind.Gallery.Core.AlbumSupport { - public interface IAlbumFilter { - bool ShouldSkip(ILocation location); - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Gallery.Core.IO; + +namespace AshMind.Gallery.Core.AlbumSupport { + public interface IAlbumFilter { + bool ShouldSkip(ILocation location); + } +} diff --git a/Core/AlbumSupport/IAlbumIDProvider.cs b/Core/AlbumSupport/IAlbumIDProvider.cs index 4a83a0f..96d6573 100644 --- a/Core/AlbumSupport/IAlbumIDProvider.cs +++ b/Core/AlbumSupport/IAlbumIDProvider.cs @@ -1,14 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Gallery.Core.IO; - -namespace AshMind.Gallery.Core.AlbumSupport -{ - public interface IAlbumIDProvider { - string GetAlbumID(string name, AlbumDescriptor descriptor); - AlbumDescriptor GetAlbumDescriptor(string albumID); - } +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.AlbumSupport { + public interface IAlbumIDProvider { + string GetAlbumID(string name, AlbumDescriptor descriptor); + AlbumDescriptor GetAlbumDescriptor(string albumID); + } } \ No newline at end of file diff --git a/Core/AlbumSupport/IAlbumItemMetadataProvider.cs b/Core/AlbumSupport/IAlbumItemMetadataProvider.cs index 871c931..419ae47 100644 --- a/Core/AlbumSupport/IAlbumItemMetadataProvider.cs +++ b/Core/AlbumSupport/IAlbumItemMetadataProvider.cs @@ -1,11 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.AlbumSupport { - public interface IAlbumItemMetadataProvider { - void LoadMetadataTo(AlbumItem item); - void SaveMetadata(AlbumItem item); - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.AlbumSupport { + public interface IAlbumItemMetadataProvider { + void LoadMetadataTo(AlbumItem item); + void SaveMetadata(AlbumItem item); + } +} diff --git a/Core/AlbumSupport/IAlbumNameTransform.cs b/Core/AlbumSupport/IAlbumNameTransform.cs index 7d524bf..67026fd 100644 --- a/Core/AlbumSupport/IAlbumNameTransform.cs +++ b/Core/AlbumSupport/IAlbumNameTransform.cs @@ -1,10 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.AlbumSupport { - public interface IAlbumNameTransform { - string Transform(string albumName, AlbumDescriptor descriptor); - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.AlbumSupport { + public interface IAlbumNameTransform { + string Transform(string albumName, AlbumDescriptor descriptor); + } +} diff --git a/Core/AlbumSupport/IAlbumProvider.cs b/Core/AlbumSupport/IAlbumProvider.cs index d68c914..c237b28 100644 --- a/Core/AlbumSupport/IAlbumProvider.cs +++ b/Core/AlbumSupport/IAlbumProvider.cs @@ -1,16 +1,15 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Gallery.Core.IO; -using AshMind.Gallery.Core.Security; - -namespace AshMind.Gallery.Core { - public interface IAlbumProvider { - IEnumerable GetAllAlbums(IEnumerable locations, IUser user); - Album GetAlbum(IEnumerable locations, string providerSpecificPath, IUser user); - - string ProviderKey { get; } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Gallery.Core.IO; +using AshMind.Gallery.Core.Security; + +namespace AshMind.Gallery.Core.AlbumSupport { + public interface IAlbumProvider { + IEnumerable GetAllAlbums(IEnumerable locations, IUser user); + Album GetAlbum(IEnumerable locations, string providerSpecificPath, IUser user); + + string ProviderKey { get; } + } +} diff --git a/Core/AlbumSupport/Providers/FileSystemAlbumProvider.cs b/Core/AlbumSupport/Providers/FileSystemAlbumProvider.cs index b1fa0c4..643ce56 100644 --- a/Core/AlbumSupport/Providers/FileSystemAlbumProvider.cs +++ b/Core/AlbumSupport/Providers/FileSystemAlbumProvider.cs @@ -1,89 +1,87 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Caching; -using System.Text; - -using AshMind.Gallery.Core.AlbumSupport; -using AshMind.Gallery.Core.IO; -using AshMind.Gallery.Core.Security; -using AshMind.Gallery.Core.Fixes; - -namespace AshMind.Gallery.Core.AlbumSupport.Providers { - public class FileSystemAlbumProvider : IAlbumProvider { - private readonly IFileSystem fileSystem; - private readonly AlbumFactory albumFactory; - private readonly AlbumItemFactory itemFactory; - private readonly IAuthorizationService authorization; - private readonly ObjectCache cache; - - public FileSystemAlbumProvider( - IFileSystem fileSystem, - AlbumFactory albumFactory, - AlbumItemFactory itemFactory, - IAuthorizationService authorization, - ObjectCache cache - ) { - this.fileSystem = fileSystem; - this.albumFactory = albumFactory; - this.itemFactory = itemFactory; - this.authorization = authorization; - this.cache = cache; - } - - public IEnumerable GetAllAlbums(IEnumerable locations, IUser user) { - return from location in locations - let album = GetAlbum(location, user) - where album != null - orderby album.Date descending - select album; - } - - public Album GetAlbum(IEnumerable locations, string providerSpecificPath, IUser user) { - return GetAlbum(this.fileSystem.GetLocation(providerSpecificPath), user); - } - - public Album GetAlbum(ILocation location, IUser user, bool ensureNonEmpty = true) { - if (!authorization.IsAuthorized(user, SecurableAction.View, location)) - return null; - - var cacheKey = "album:" + location.Path; - var album = (Album)this.cache.Get(cacheKey); - if (album != null) - return album.AsWritable(); - - Func itemFactory = () => this.GetItemsAtLocation(location, user).ToArray(); - if (ensureNonEmpty) { - var items = itemFactory(); - if (items.Length == 0) - return null; - - itemFactory = () => items; - } - - album = this.albumFactory.Create( - new AlbumDescriptor(this.ProviderKey, location.Path), - location.Name, itemFactory, - location - ); - album.MakeReadOnly(); - this.cache.Set(cacheKey, album, new CacheItemPolicy { - ChangeMonitors = { new FixedFileChangeMonitor(new[] { location.Path }) } - }); - - return album.AsWritable(); - } - - private IEnumerable GetItemsAtLocation(ILocation location, IUser user) { - return from file in location.GetFiles() - let itemType = GuessItemType.Of(file.Name) - where itemType == AlbumItemType.Image - let item = this.itemFactory.CreateFrom(file, itemType) - select item; - } - - public string ProviderKey { - get { return AlbumProviderKeys.Default; } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Caching; + +using AshMind.Gallery.Core.IO; +using AshMind.Gallery.Core.Security; +using AshMind.Gallery.Core.Fixes; + +namespace AshMind.Gallery.Core.AlbumSupport.Providers { + public class FileSystemAlbumProvider : IAlbumProvider { + private readonly IFileSystem fileSystem; + private readonly AlbumFactory albumFactory; + private readonly AlbumItemFactory itemFactory; + private readonly IAuthorizationService authorization; + private readonly ObjectCache cache; + + public FileSystemAlbumProvider( + IFileSystem fileSystem, + AlbumFactory albumFactory, + AlbumItemFactory itemFactory, + IAuthorizationService authorization, + ObjectCache cache + ) { + this.fileSystem = fileSystem; + this.albumFactory = albumFactory; + this.itemFactory = itemFactory; + this.authorization = authorization; + this.cache = cache; + } + + public IEnumerable GetAllAlbums(IEnumerable locations, IUser user) { + return from location in locations + let album = GetAlbum(location, user) + where album != null + orderby album.Date descending + select album; + } + + public Album GetAlbum(IEnumerable locations, string providerSpecificPath, IUser user) { + return GetAlbum(this.fileSystem.GetLocation(providerSpecificPath), user); + } + + public Album GetAlbum(ILocation location, IUser user, bool ensureNonEmpty = true) { + if (!authorization.IsAuthorized(user, SecurableAction.View, location)) + return null; + + var cacheKey = "album:" + location.Path; + var album = (Album)this.cache.Get(cacheKey); + if (album != null) + return album.AsWritable(); + + Func itemsFactory = () => this.GetItemsAtLocation(location).ToArray(); + if (ensureNonEmpty) { + var items = itemsFactory(); + if (items.Length == 0) + return null; + + itemsFactory = () => items; + } + + album = this.albumFactory.Create( + new AlbumDescriptor(this.ProviderKey, location.Path), + location.Name, itemsFactory, + location + ); + album.MakeReadOnly(); + this.cache.Set(cacheKey, album, new CacheItemPolicy { + ChangeMonitors = { new FixedFileChangeMonitor(new[] { location.Path }) } + }); + + return album.AsWritable(); + } + + private IEnumerable GetItemsAtLocation(ILocation location) { + return from file in location.GetFiles() + let itemType = GuessItemType.Of(file.Name) + where itemType == AlbumItemType.Image + let item = this.itemFactory.CreateFrom(file, itemType) + select item; + } + + public string ProviderKey { + get { return AlbumProviderKeys.Default; } + } + } +} diff --git a/Core/AlbumSupport/Providers/PersonAlbumProvider.cs b/Core/AlbumSupport/Providers/PersonAlbumProvider.cs index 8381124..f8cfefa 100644 --- a/Core/AlbumSupport/Providers/PersonAlbumProvider.cs +++ b/Core/AlbumSupport/Providers/PersonAlbumProvider.cs @@ -1,130 +1,129 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Caching; -using System.Text; - -using AshMind.Extensions; - -using AshMind.Gallery.Core.Integration; -using AshMind.Gallery.Core.IO; -using AshMind.Gallery.Core.Security; - -namespace AshMind.Gallery.Core.AlbumSupport.Providers { - public class PersonAlbumProvider : IAlbumProvider { - private readonly AlbumFactory albumFactory; - private readonly AlbumItemFactory itemFactory; - private readonly IFaceProvider[] faceProviders; - private readonly FileSystemAlbumProvider primaryAlbumProvider; - private readonly IAuthorizationService authorization; - private readonly ObjectCache faceCache; - - public PersonAlbumProvider( - AlbumFactory albumFactory, - AlbumItemFactory itemFactory, - FileSystemAlbumProvider primaryAlbumProvider, - IFaceProvider[] faceProviders, - IAuthorizationService authorization, - ObjectCache faceCache - ) { - this.albumFactory = albumFactory; - this.itemFactory = itemFactory; - this.primaryAlbumProvider = primaryAlbumProvider; - this.faceProviders = faceProviders; - this.authorization = authorization; - this.faceCache = faceCache; - } - - public IEnumerable GetAllAlbums(IEnumerable locations, IUser user) { - var albumsCacheKey = "faces:all_albums"; - var cached = this.faceCache.Get(albumsCacheKey); - if (cached != null) - return CorrectAlbums((Album[])cached, user); - - var albums = ( - from location in locations - from face in GetFaces(location) - group face by face.Person into personFaces - let uniqueKey = personFaces.Key.Emails.ElementAtOrDefault(0) ?? personFaces.Key.Name - select this.albumFactory.Create( - new AlbumDescriptor(this.ProviderKey, uniqueKey), - personFaces.Key.Name, - () => ( - from face in personFaces - let itemType = GuessItemType.Of(face.File.Name) - where itemType == AlbumItemType.Image - select CreateAlbumItem(face, itemType) - ).ToList(), - new SecurableUniqueKey(uniqueKey) - ) - ).ToArray(); - - albums.ForEach(a => a.MakeReadOnly()); - this.faceCache.Set(albumsCacheKey, albums, new CacheItemPolicy { - AbsoluteExpiration = DateTimeOffset.Now.AddDays(3) - }); - - return CorrectAlbums(albums, user); - } - - private AlbumItem CreateAlbumItem(Face face, AlbumItemType itemType) { - var item = this.itemFactory.CreateFrom(face.File, itemType); - item.LazyPrimaryAlbum = new Lazy( - () => this.primaryAlbumProvider.GetAlbum(face.File.Location, User.System, ensureNonEmpty: false) - ); - - return item; - } - - public Album GetAlbum(IEnumerable locations, string providerSpecificPath, IUser user) { - return this.GetAllAlbums(locations, user).Single(a => a.Descriptor.ProviderSpecificPath == providerSpecificPath); - } - - private IEnumerable CorrectAlbums(IEnumerable albums, IUser user) { - return FilterByAuthorization(albums, user).Select(a => a.AsWritable()); - } - - private IEnumerable FilterByAuthorization(IEnumerable albums, IUser user) { - var realUser = user as User; - return albums.Where( - album => (realUser != null && album.Descriptor.ProviderSpecificPath == realUser.Email) - || IsAuthorizedTo(album, user) - ); - } - - private bool IsAuthorizedTo(Album album, IUser user) { - return this.authorization.IsAuthorized( - user, SecurableAction.View, - new SecurableUniqueKey(album.Descriptor.ProviderSpecificPath) - ); - } - - private IEnumerable GetFaces(ILocation location) { - var cacheKey = "faces:" + location.Path; - var cached = this.faceCache.Get(cacheKey); - if (cached != null) - return (Face[])cached; - - var faces = ( - from provider in this.faceProviders - from face in provider.GetFaces(location) - select face - ).ToArray(); - - // https://connect.microsoft.com/VisualStudio/feedback/details/565313/breaking-problem-with-datetimeoffset-that-affects-multiple-framework-classes?wa=wsignin1.0 - // this.faceCache.Set(cacheKey, faces, new CacheItemPolicy { - // ChangeMonitors = { new HostFileChangeMonitor(new[] { location.Path }) } - // }); - - this.faceCache.Set(cacheKey, faces, new CacheItemPolicy { - AbsoluteExpiration = DateTimeOffset.Now.AddDays(3) - }); - - return faces; - } - - public string ProviderKey { - get { return AlbumProviderKeys.People; } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Caching; + +using AshMind.Extensions; + +using AshMind.Gallery.Core.Integration; +using AshMind.Gallery.Core.IO; +using AshMind.Gallery.Core.Security; + +namespace AshMind.Gallery.Core.AlbumSupport.Providers { + public class PersonAlbumProvider : IAlbumProvider { + private readonly AlbumFactory albumFactory; + private readonly AlbumItemFactory itemFactory; + private readonly IFaceProvider[] faceProviders; + private readonly FileSystemAlbumProvider primaryAlbumProvider; + private readonly IAuthorizationService authorization; + private readonly ObjectCache faceCache; + + public PersonAlbumProvider( + AlbumFactory albumFactory, + AlbumItemFactory itemFactory, + FileSystemAlbumProvider primaryAlbumProvider, + IFaceProvider[] faceProviders, + IAuthorizationService authorization, + ObjectCache faceCache + ) { + this.albumFactory = albumFactory; + this.itemFactory = itemFactory; + this.primaryAlbumProvider = primaryAlbumProvider; + this.faceProviders = faceProviders; + this.authorization = authorization; + this.faceCache = faceCache; + } + + public IEnumerable GetAllAlbums(IEnumerable locations, IUser user) { + const string albumsCacheKey = "faces:all_albums"; + var cached = this.faceCache.Get(albumsCacheKey); + if (cached != null) + return CorrectAlbums((Album[])cached, user); + + var albums = ( + from location in locations + from face in GetFaces(location) + group face by face.Person into personFaces + let uniqueKey = personFaces.Key.Emails.ElementAtOrDefault(0) ?? personFaces.Key.Name + select this.albumFactory.Create( + new AlbumDescriptor(this.ProviderKey, uniqueKey), + personFaces.Key.Name, + () => ( + from face in personFaces + let itemType = GuessItemType.Of(face.File.Name) + where itemType == AlbumItemType.Image + select CreateAlbumItem(face, itemType) + ).ToList(), + new SecurableUniqueKey(uniqueKey) + ) + ).ToArray(); + + albums.ForEach(a => a.MakeReadOnly()); + this.faceCache.Set(albumsCacheKey, albums, new CacheItemPolicy { + AbsoluteExpiration = DateTimeOffset.Now.AddDays(3) + }); + + return CorrectAlbums(albums, user); + } + + private AlbumItem CreateAlbumItem(Face face, AlbumItemType itemType) { + var item = this.itemFactory.CreateFrom(face.File, itemType); + item.LazyPrimaryAlbum = new Lazy( + () => this.primaryAlbumProvider.GetAlbum(face.File.Location, User.System, ensureNonEmpty: false) + ); + + return item; + } + + public Album GetAlbum(IEnumerable locations, string providerSpecificPath, IUser user) { + return this.GetAllAlbums(locations, user).Single(a => a.Descriptor.ProviderSpecificPath == providerSpecificPath); + } + + private IEnumerable CorrectAlbums(IEnumerable albums, IUser user) { + return FilterByAuthorization(albums, user).Select(a => a.AsWritable()); + } + + private IEnumerable FilterByAuthorization(IEnumerable albums, IUser user) { + var realUser = user as User; + return albums.Where( + album => (realUser != null && album.Descriptor.ProviderSpecificPath == realUser.Email) + || IsAuthorizedTo(album, user) + ); + } + + private bool IsAuthorizedTo(Album album, IUser user) { + return this.authorization.IsAuthorized( + user, SecurableAction.View, + new SecurableUniqueKey(album.Descriptor.ProviderSpecificPath) + ); + } + + private IEnumerable GetFaces(ILocation location) { + var cacheKey = "faces:" + location.Path; + var cached = this.faceCache.Get(cacheKey); + if (cached != null) + return (Face[])cached; + + var faces = ( + from provider in this.faceProviders + from face in provider.GetFaces(location) + select face + ).ToArray(); + + // https://connect.microsoft.com/VisualStudio/feedback/details/565313/breaking-problem-with-datetimeoffset-that-affects-multiple-framework-classes?wa=wsignin1.0 + // this.faceCache.Set(cacheKey, faces, new CacheItemPolicy { + // ChangeMonitors = { new HostFileChangeMonitor(new[] { location.Path }) } + // }); + + this.faceCache.Set(cacheKey, faces, new CacheItemPolicy { + AbsoluteExpiration = DateTimeOffset.Now.AddDays(3) + }); + + return faces; + } + + public string ProviderKey { + get { return AlbumProviderKeys.People; } + } + } +} diff --git a/Core/Argument.cs b/Core/Argument.cs index 9f57d59..40d22be 100644 --- a/Core/Argument.cs +++ b/Core/Argument.cs @@ -1,119 +1,119 @@ -using System; -using System.Collections; -using System.Diagnostics; - -// Ïðîñòðàíñòâî èì¸í îïóùåíî äëÿ óäîáñòâà ïîâòîðíîãî èñïîëüçîâàíèÿ - -public static class Argument -{ - [DebuggerHidden] - public static void VerifyNotNull(string name, object value) - { - if (value == null) - throw new ArgumentNullException(name); - } - - [DebuggerHidden] - public static void VerifyNotEmpty(string name, string value) - { - if (value.Length == 0) - throw new ArgumentException(Argument.CannotBeEmpty, name); - } - - [DebuggerHidden] - public static void VerifyNotEmpty(string name, Array array) - { - if (array.Length == 0) - throw new ArgumentException(Argument.CannotBeEmpty, name); - } - - [DebuggerHidden] - public static void VerifyNotEmpty(string name, ICollection collection) - { - if (collection.Count == 0) - throw new ArgumentException(Argument.CannotBeEmpty, name); - } - - [DebuggerHidden] - public static void VerifyNotNullOrEmpty(string name, string value) - { - Argument.VerifyNotNull(name, value); - Argument.VerifyNotEmpty(name, value); - } - - [DebuggerHidden] - public static void VerifyNotNullOrEmpty(string name, Array array) - { - Argument.VerifyNotNull(name, array); - Argument.VerifyNotEmpty(name, array); - } - - [DebuggerHidden] - public static void VerifyNotNullOrEmpty(string name, ICollection collection) - { - Argument.VerifyNotNull(name, collection); - Argument.VerifyNotEmpty(name, collection); - } - - [DebuggerHidden] - public static void VerifyType(string name, object value, Type type) - { - if (!type.IsInstanceOfType(value)) - { - throw new ArgumentException( - Argument.FormatWrongType(value, type), - name - ); - } - } - - [DebuggerHidden] - public static void CheckBuffer(string name, Array buffer, int offset, int count) - { - Argument.VerifyNotNullOrEmpty(name, buffer); - - if(count <= 0) - throw new ArgumentOutOfRangeException(Argument.MustBePositive); - - if(offset < 0) - throw new ArgumentOutOfRangeException(Argument.MustBePositive); - - if(buffer.Length - offset < count) - throw new ArgumentException(Argument.InsufficientBuffer); - } - - private static string CannotBeEmpty - { - get { return Argument.GetString("Argument.CannotBeEmpty"); } - } - - private static string MustBePositive - { - get { return Argument.GetString("Argument.MustBePositive"); } - } - - private static string InsufficientBuffer - { - get { return Argument.GetString("Argument.InsufficientBuffer"); } - } - - public static string FormatWrongType(object value, Type type) - { - string template = Argument.GetString("Argument.WrongType"); - - return String.Format(template, value, type); - } - - public static string FormatWrongKeyType(object value, Type type) - { - string template = Argument.GetString("Argument.WrongKeyType"); - - return String.Format(template, value, type); - } - - private static string GetString(string key) - { - //return Resources.GetString(typeof(Argument), key); - return key; - } +using System; +using System.Collections; +using System.Diagnostics; + +// Ïðîñòðàíñòâî èì¸í îïóùåíî äëÿ óäîáñòâà ïîâòîðíîãî èñïîëüçîâàíèÿ + +public static class Argument +{ + [DebuggerHidden] + public static void VerifyNotNull(string name, object value) + { + if (value == null) + throw new ArgumentNullException(name); + } + + [DebuggerHidden] + public static void VerifyNotEmpty(string name, string value) + { + if (value.Length == 0) + throw new ArgumentException(Argument.CannotBeEmpty, name); + } + + [DebuggerHidden] + public static void VerifyNotEmpty(string name, Array array) + { + if (array.Length == 0) + throw new ArgumentException(Argument.CannotBeEmpty, name); + } + + [DebuggerHidden] + public static void VerifyNotEmpty(string name, ICollection collection) + { + if (collection.Count == 0) + throw new ArgumentException(Argument.CannotBeEmpty, name); + } + + [DebuggerHidden] + public static void VerifyNotNullOrEmpty(string name, string value) + { + Argument.VerifyNotNull(name, value); + Argument.VerifyNotEmpty(name, value); + } + + [DebuggerHidden] + public static void VerifyNotNullOrEmpty(string name, Array array) + { + Argument.VerifyNotNull(name, array); + Argument.VerifyNotEmpty(name, array); + } + + [DebuggerHidden] + public static void VerifyNotNullOrEmpty(string name, ICollection collection) + { + Argument.VerifyNotNull(name, collection); + Argument.VerifyNotEmpty(name, collection); + } + + [DebuggerHidden] + public static void VerifyType(string name, object value, Type type) + { + if (!type.IsInstanceOfType(value)) + { + throw new ArgumentException( + Argument.FormatWrongType(value, type), + name + ); + } + } + + [DebuggerHidden] + public static void CheckBuffer(string name, Array buffer, int offset, int count) + { + Argument.VerifyNotNullOrEmpty(name, buffer); + + if(count <= 0) + throw new ArgumentOutOfRangeException(Argument.MustBePositive); + + if(offset < 0) + throw new ArgumentOutOfRangeException(Argument.MustBePositive); + + if(buffer.Length - offset < count) + throw new ArgumentException(Argument.InsufficientBuffer); + } + + private static string CannotBeEmpty + { + get { return Argument.GetString("Argument.CannotBeEmpty"); } + } + + private static string MustBePositive + { + get { return Argument.GetString("Argument.MustBePositive"); } + } + + private static string InsufficientBuffer + { + get { return Argument.GetString("Argument.InsufficientBuffer"); } + } + + public static string FormatWrongType(object value, Type type) + { + var template = Argument.GetString("Argument.WrongType"); + + return String.Format(template, value, type); + } + + public static string FormatWrongKeyType(object value, Type type) + { + var template = Argument.GetString("Argument.WrongKeyType"); + + return String.Format(template, value, type); + } + + private static string GetString(string key) + { + //return Resources.GetString(typeof(Argument), key); + return key; + } } \ No newline at end of file diff --git a/Core/Comment.cs b/Core/Comment.cs index c408ac3..1ba67da 100644 --- a/Core/Comment.cs +++ b/Core/Comment.cs @@ -1,20 +1,19 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Gallery.Core.Security; - -namespace AshMind.Gallery.Core { - public class Comment { - public Comment(User author, DateTimeOffset date, string text) { - this.Author = author; - this.Date = date; - this.Text = text; - } - - public User Author { get; private set; } - public DateTimeOffset Date { get; private set; } - public string Text { get; private set; } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Gallery.Core.Security; + +namespace AshMind.Gallery.Core { + public class Comment { + public Comment(User author, DateTimeOffset date, string text) { + this.Author = author; + this.Date = date; + this.Text = text; + } + + public User Author { get; private set; } + public DateTimeOffset Date { get; private set; } + public string Text { get; private set; } + } +} diff --git a/Core/Commenting/ICommentRepository.cs b/Core/Commenting/ICommentRepository.cs index ee45a3e..73f5700 100644 --- a/Core/Commenting/ICommentRepository.cs +++ b/Core/Commenting/ICommentRepository.cs @@ -1,11 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.Commenting { - public interface ICommentRepository { - IList LoadCommentsOf(string itemPath); - void SaveComment(string itemPath, Comment comment); - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.Commenting { + public interface ICommentRepository { + IList LoadCommentsOf(string itemPath); + void SaveComment(string itemPath, Comment comment); + } +} diff --git a/Core/Core.csproj b/Core/Core.csproj index 922d545..d73ffb9 100644 --- a/Core/Core.csproj +++ b/Core/Core.csproj @@ -87,6 +87,9 @@ + + Properties\AssemblyInfoCommon.cs + diff --git a/Core/CoreModule.cs b/Core/CoreModule.cs index 7f8b56a..93fafc2 100644 --- a/Core/CoreModule.cs +++ b/Core/CoreModule.cs @@ -108,7 +108,7 @@ Func cacheFactory } private void RegisterAlbumSupport(ContainerBuilder builder, IList types) { - builder.Register(c => new AlbumIDProvider(this.storageLocation, this.fileSystem)) + builder.Register(c => new AlbumIDProvider(this.storageLocation)) .As(); builder.RegisterType(); diff --git a/Core/Fixes/FixedFileChangeMonitor.cs b/Core/Fixes/FixedFileChangeMonitor.cs index e0cfc32..3d753b7 100644 --- a/Core/Fixes/FixedFileChangeMonitor.cs +++ b/Core/Fixes/FixedFileChangeMonitor.cs @@ -1,37 +1,36 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Runtime.Caching; -using System.Collections.ObjectModel; - -using AshMind.Extensions; - -namespace AshMind.Gallery.Core.Fixes { - // This is a dummy class that only exists to fix the - // https://connect.microsoft.com/VisualStudio/feedback/details/565313/breaking-problem-with-datetimeoffset-that-affects-multiple-framework-classes - // It does not do anything by itself, but alternative cache can gather paths and set up its own monitoring. - internal class FixedFileChangeMonitor : FileChangeMonitor { - private readonly ReadOnlyCollection filePaths; - - public FixedFileChangeMonitor(string[] paths) { - this.filePaths = paths.AsReadOnly(); - } - - public override ReadOnlyCollection FilePaths { - get { return this.filePaths; } - } - - public override DateTimeOffset LastModified { - get { throw new NotSupportedException(); } - } - - protected override void Dispose(bool disposing) { - throw new NotSupportedException(); - } - - public override string UniqueId { - get { throw new NotSupportedException(); } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Caching; +using System.Collections.ObjectModel; + +using AshMind.Extensions; + +namespace AshMind.Gallery.Core.Fixes { + // This is a dummy class that only exists to fix the + // https://connect.microsoft.com/VisualStudio/feedback/details/565313/breaking-problem-with-datetimeoffset-that-affects-multiple-framework-classes + // It does not do anything by itself, but alternative cache can gather paths and set up its own monitoring. + internal class FixedFileChangeMonitor : FileChangeMonitor { + private readonly ReadOnlyCollection filePaths; + + public FixedFileChangeMonitor(string[] paths) { + this.filePaths = paths.AsReadOnly(); + } + + public override ReadOnlyCollection FilePaths { + get { return this.filePaths; } + } + + public override DateTimeOffset LastModified { + get { throw new NotSupportedException(); } + } + + protected override void Dispose(bool disposing) { + throw new NotSupportedException(); + } + + public override string UniqueId { + get { throw new NotSupportedException(); } + } + } +} diff --git a/Core/IAlbumFacade.cs b/Core/IAlbumFacade.cs index a58c054..c5f46a0 100644 --- a/Core/IAlbumFacade.cs +++ b/Core/IAlbumFacade.cs @@ -1,17 +1,16 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Gallery.Core.Security; - -namespace AshMind.Gallery.Core { - public interface IAlbumFacade { - IEnumerable GetAlbums(string providerKey, IUser user); - Album GetAlbum(string albumID, IUser user); - AlbumItem GetItem(string albumID, string itemName, IUser user); - string GetAlbumID(Album album); - - void SaveItem(AlbumItem item); - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Gallery.Core.Security; + +namespace AshMind.Gallery.Core { + public interface IAlbumFacade { + IEnumerable GetAlbums(string providerKey, IUser user); + Album GetAlbum(string albumID, IUser user); + AlbumItem GetItem(string albumID, string itemName, IUser user); + string GetAlbumID(Album album); + + void SaveItem(AlbumItem item); + } +} diff --git a/Core/IO/ActionIfMissing.cs b/Core/IO/ActionIfMissing.cs index d555997..aa0c752 100644 --- a/Core/IO/ActionIfMissing.cs +++ b/Core/IO/ActionIfMissing.cs @@ -1,13 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.IO { - public enum ActionIfMissing { - ReturnNull, - ReturnAsIs, - ThrowException, - CreateNew - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.IO { + public enum ActionIfMissing { + ReturnNull, + ReturnAsIs, + ThrowException, + CreateNew + } +} diff --git a/Core/IO/FileLockMode.cs b/Core/IO/FileLockMode.cs index 8c8820b..08cf9e5 100644 --- a/Core/IO/FileLockMode.cs +++ b/Core/IO/FileLockMode.cs @@ -1,13 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.IO { - public enum FileLockMode { - None, - Read, - Write, - ReadWrite - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.IO { + public enum FileLockMode { + None, + Read, + Write, + ReadWrite + } +} diff --git a/Core/IO/FileOpenMode.cs b/Core/IO/FileOpenMode.cs index 5b4300d..ec14ca7 100644 --- a/Core/IO/FileOpenMode.cs +++ b/Core/IO/FileOpenMode.cs @@ -1,12 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.IO { - public enum FileOpenMode { - ReadOrWrite, - Append, - Recreate - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.IO { + public enum FileOpenMode { + ReadOrWrite, + Append, + Recreate + } +} diff --git a/Core/IO/IFile.cs b/Core/IO/IFile.cs index fc1ba14..474ce5b 100644 --- a/Core/IO/IFile.cs +++ b/Core/IO/IFile.cs @@ -1,18 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.IO; - -namespace AshMind.Gallery.Core.IO { - public interface IFile : IFileSystemElement { - DateTimeOffset GetLastWriteTime(); - - Stream Read(FileLockMode lockMode); - Stream Open(FileLockMode lockMode, FileOpenMode openMode); - - bool Exists { get; } - - ILocation Location { get; } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; + +namespace AshMind.Gallery.Core.IO { + public interface IFile : IFileSystemElement { + DateTimeOffset GetLastWriteTime(); + + Stream Read(FileLockMode lockMode); + Stream Open(FileLockMode lockMode, FileOpenMode openMode); + + bool Exists { get; } + + ILocation Location { get; } + } +} diff --git a/Core/IO/IFileSystem.cs b/Core/IO/IFileSystem.cs index 997310f..c3ef11b 100644 --- a/Core/IO/IFileSystem.cs +++ b/Core/IO/IFileSystem.cs @@ -1,18 +1,16 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.IO { - public interface IFileSystem { - bool IsFileName(string pathOrFileName); - - string BuildPath(params string[] parts); - - IFile GetFile(string path, bool nullUnlessExists = true); - - bool IsLocation(string path); - ILocation GetLocation(string path, ActionIfMissing actionIfMissing = ActionIfMissing.ThrowException); - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.IO { + public interface IFileSystem { + bool IsFileName(string pathOrFileName); + + string BuildPath(params string[] parts); + + IFile GetFile(string path, bool nullUnlessExists = true); + + bool IsLocation(string path); + ILocation GetLocation(string path, ActionIfMissing actionIfMissing = ActionIfMissing.ThrowException); + } +} diff --git a/Core/IO/IFileSystemElement.cs b/Core/IO/IFileSystemElement.cs index 6d81d12..c8daf5e 100644 --- a/Core/IO/IFileSystemElement.cs +++ b/Core/IO/IFileSystemElement.cs @@ -1,14 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.IO { - public interface IFileSystemElement { - string Name { get; } - string Path { get; } - - bool IsHidden(); - void SetHidden(bool value); - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.IO { + public interface IFileSystemElement { + string Name { get; } + string Path { get; } + + bool IsHidden(); + void SetHidden(bool value); + } +} diff --git a/Core/IO/ILocation.cs b/Core/IO/ILocation.cs index 61f9459..68cca31 100644 --- a/Core/IO/ILocation.cs +++ b/Core/IO/ILocation.cs @@ -1,15 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.IO { - public interface ILocation : IFileSystemElement { - IFile GetFile(string name, bool nullUnlessExists = true); - - ILocation GetLocation(string path, ActionIfMissing actionIfMissing = ActionIfMissing.ThrowException); - IEnumerable GetLocations(bool recursive); - - IEnumerable GetFiles(); - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.IO { + public interface ILocation : IFileSystemElement { + IFile GetFile(string name, bool nullUnlessExists = true); + + ILocation GetLocation(string path, ActionIfMissing actionIfMissing = ActionIfMissing.ThrowException); + IEnumerable GetLocations(bool recursive); + + IEnumerable GetFiles(); + } +} diff --git a/Core/IO/Implementation/File.cs b/Core/IO/Implementation/File.cs index 966a6fb..749350f 100644 --- a/Core/IO/Implementation/File.cs +++ b/Core/IO/Implementation/File.cs @@ -1,82 +1,81 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.IO.Implementation { - internal class File : IFile { - private static IDictionary fileShare = new Dictionary { - { FileLockMode.None, FileShare.ReadWrite }, - { FileLockMode.Read, FileShare.Write }, - { FileLockMode.Write, FileShare.Read }, - { FileLockMode.ReadWrite, FileShare.None }, - }; - - private static IDictionary fileMode = new Dictionary { - { FileOpenMode.Append, FileMode.Append }, - { FileOpenMode.ReadOrWrite, FileMode.OpenOrCreate }, - { FileOpenMode.Recreate, FileMode.Create } - }; - - private static IDictionary fileAccess = new Dictionary { - { FileOpenMode.Append, FileAccess.Write }, - { FileOpenMode.ReadOrWrite, FileAccess.ReadWrite }, - { FileOpenMode.Recreate, FileAccess.Write } - }; - - private readonly string path; - - public File(string path, Location location) { - this.path = path; - this.Location = location; - } - - public bool IsHidden() { - return System.IO.File.GetAttributes(this.path).HasFlag(FileAttributes.Hidden); - } - - public void SetHidden(bool value) { - var attributes = System.IO.File.GetAttributes(this.path); - if (value) { - attributes |= FileAttributes.Hidden; - } - else { - attributes &= ~FileAttributes.Hidden; - } - - System.IO.File.SetAttributes(this.path, attributes); - } - - public Stream Read(FileLockMode lockMode) { - return System.IO.File.Open(this.path, FileMode.Open, FileAccess.Read, fileShare[lockMode]); - } - - public Stream Open(FileLockMode lockMode, FileOpenMode openMode) { - return System.IO.File.Open( - this.path, - fileMode[openMode], - fileAccess[openMode], - fileShare[lockMode] - ); - } - - public DateTimeOffset GetLastWriteTime() { - return System.IO.File.GetLastWriteTimeUtc(this.path); - } - - public string Name { - get { return System.IO.Path.GetFileName(this.path); } - } - - public string Path { - get { return this.path; } - } - - public bool Exists { - get { return System.IO.File.Exists(this.path); } - } - - public ILocation Location { get; private set; } - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace AshMind.Gallery.Core.IO.Implementation { + internal class File : IFile { + private static readonly IDictionary fileShare = new Dictionary { + { FileLockMode.None, FileShare.ReadWrite }, + { FileLockMode.Read, FileShare.Write }, + { FileLockMode.Write, FileShare.Read }, + { FileLockMode.ReadWrite, FileShare.None }, + }; + + private static readonly IDictionary fileMode = new Dictionary { + { FileOpenMode.Append, FileMode.Append }, + { FileOpenMode.ReadOrWrite, FileMode.OpenOrCreate }, + { FileOpenMode.Recreate, FileMode.Create } + }; + + private static readonly IDictionary fileAccess = new Dictionary { + { FileOpenMode.Append, FileAccess.Write }, + { FileOpenMode.ReadOrWrite, FileAccess.ReadWrite }, + { FileOpenMode.Recreate, FileAccess.Write } + }; + + private readonly string path; + + public File(string path, Location location) { + this.path = path; + this.Location = location; + } + + public bool IsHidden() { + return System.IO.File.GetAttributes(this.path).HasFlag(FileAttributes.Hidden); + } + + public void SetHidden(bool value) { + var attributes = System.IO.File.GetAttributes(this.path); + if (value) { + attributes |= FileAttributes.Hidden; + } + else { + attributes &= ~FileAttributes.Hidden; + } + + System.IO.File.SetAttributes(this.path, attributes); + } + + public Stream Read(FileLockMode lockMode) { + return System.IO.File.Open(this.path, FileMode.Open, FileAccess.Read, fileShare[lockMode]); + } + + public Stream Open(FileLockMode lockMode, FileOpenMode openMode) { + return System.IO.File.Open( + this.path, + fileMode[openMode], + fileAccess[openMode], + fileShare[lockMode] + ); + } + + public DateTimeOffset GetLastWriteTime() { + return System.IO.File.GetLastWriteTimeUtc(this.path); + } + + public string Name { + get { return System.IO.Path.GetFileName(this.path); } + } + + public string Path { + get { return this.path; } + } + + public bool Exists { + get { return System.IO.File.Exists(this.path); } + } + + public ILocation Location { get; private set; } + } +} diff --git a/Core/IO/Implementation/FileSystem.cs b/Core/IO/Implementation/FileSystem.cs index 3804d5d..f7b7490 100644 --- a/Core/IO/Implementation/FileSystem.cs +++ b/Core/IO/Implementation/FileSystem.cs @@ -1,42 +1,41 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.IO; - -namespace AshMind.Gallery.Core.IO.Implementation { - internal class FileSystem : IFileSystem { - public IFile GetFile(string path, bool nullUnlessExists = true) { - return this.GetLocation(Path.GetDirectoryName(path)) - .GetFile(Path.GetFileName(path), nullUnlessExists); - } - - public string BuildPath(params string[] parts) { - return Path.Combine(parts); - } - - public bool IsFileName(string pathOrFileName) { - return pathOrFileName.IndexOfAny(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }) < 0; - } - - public bool IsLocation(string path) { - return Directory.Exists(path); - } - - public ILocation GetLocation(string path, ActionIfMissing actionIfMissing = ActionIfMissing.ThrowException) { - if (!Directory.Exists(path) && actionIfMissing != ActionIfMissing.ReturnAsIs) { - if (actionIfMissing == ActionIfMissing.CreateNew) { - Directory.CreateDirectory(path); - } - else if (actionIfMissing == ActionIfMissing.ReturnNull) { - return null; - } - else if (actionIfMissing == ActionIfMissing.ThrowException) { - throw new FileNotFoundException("Location was not found.", path); - } - } - - return new Location(path); - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; + +namespace AshMind.Gallery.Core.IO.Implementation { + internal class FileSystem : IFileSystem { + public IFile GetFile(string path, bool nullUnlessExists = true) { + return this.GetLocation(Path.GetDirectoryName(path)) + .GetFile(Path.GetFileName(path), nullUnlessExists); + } + + public string BuildPath(params string[] parts) { + return Path.Combine(parts); + } + + public bool IsFileName(string pathOrFileName) { + return pathOrFileName.IndexOfAny(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }) < 0; + } + + public bool IsLocation(string path) { + return Directory.Exists(path); + } + + public ILocation GetLocation(string path, ActionIfMissing actionIfMissing = ActionIfMissing.ThrowException) { + if (!Directory.Exists(path) && actionIfMissing != ActionIfMissing.ReturnAsIs) { + if (actionIfMissing == ActionIfMissing.CreateNew) { + Directory.CreateDirectory(path); + } + else if (actionIfMissing == ActionIfMissing.ReturnNull) { + return null; + } + else if (actionIfMissing == ActionIfMissing.ThrowException) { + throw new FileNotFoundException("Location was not found.", path); + } + } + + return new Location(path); + } + } } \ No newline at end of file diff --git a/Core/IO/Implementation/Location.cs b/Core/IO/Implementation/Location.cs index 35f4ee8..2f1ba62 100644 --- a/Core/IO/Implementation/Location.cs +++ b/Core/IO/Implementation/Location.cs @@ -1,54 +1,53 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.IO; - -namespace AshMind.Gallery.Core.IO.Implementation { - internal class Location : ILocation { - public Location(string path) { - this.Path = path; - } - - public IEnumerable GetLocations(bool recursive) { - return Directory.EnumerateDirectories(this.Path, "*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly) - .Select(path => new Location(path)); - } - - public IEnumerable GetFiles() { - return Directory.EnumerateFiles(this.Path) - .Select(name => new File(name, this)); - } - - public IFile GetFile(string name, bool nullUnlessExists = true) { - var path = System.IO.Path.Combine(this.Path, name); - if (nullUnlessExists && !System.IO.File.Exists(path)) - return null; - - return new File(path, this); - } - - public ILocation GetLocation(string name, ActionIfMissing actionIfMissing = ActionIfMissing.ReturnNull) { - var path = System.IO.Path.Combine(this.Path, name); - return new FileSystem().GetLocation(path, actionIfMissing); - } - - public bool IsHidden() { - return new File(this.Path, this).IsHidden(); - } - - public void SetHidden(bool value) { - new File(this.Path, this).SetHidden(value); - } - - public string Name { - get { return System.IO.Path.GetFileName(this.Path); } - } - - public string Path { get; private set; } - - public override string ToString() { - return this.Path; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; + +namespace AshMind.Gallery.Core.IO.Implementation { + internal class Location : ILocation { + public Location(string path) { + this.Path = path; + } + + public IEnumerable GetLocations(bool recursive) { + return Directory.EnumerateDirectories(this.Path, "*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly) + .Select(path => new Location(path)); + } + + public IEnumerable GetFiles() { + return Directory.EnumerateFiles(this.Path) + .Select(name => new File(name, this)); + } + + public IFile GetFile(string name, bool nullUnlessExists = true) { + var path = System.IO.Path.Combine(this.Path, name); + if (nullUnlessExists && !System.IO.File.Exists(path)) + return null; + + return new File(path, this); + } + + public ILocation GetLocation(string name, ActionIfMissing actionIfMissing = ActionIfMissing.ReturnNull) { + var path = System.IO.Path.Combine(this.Path, name); + return new FileSystem().GetLocation(path, actionIfMissing); + } + + public bool IsHidden() { + return new File(this.Path, this).IsHidden(); + } + + public void SetHidden(bool value) { + new File(this.Path, this).SetHidden(value); + } + + public string Name { + get { return System.IO.Path.GetFileName(this.Path); } + } + + public string Path { get; private set; } + + public override string ToString() { + return this.Path; + } + } +} diff --git a/Core/IReadOnlySupport.cs b/Core/IReadOnlySupport.cs index 837517e..6ec9684 100644 --- a/Core/IReadOnlySupport.cs +++ b/Core/IReadOnlySupport.cs @@ -1,15 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core { - public interface IReadOnlySupport - where TReadOnlySupport : IReadOnlySupport - { - void MakeReadOnly(); - bool IsReadOnly { get; } - - TReadOnlySupport AsWritable(); - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core { + public interface IReadOnlySupport + where TReadOnlySupport : IReadOnlySupport + { + void MakeReadOnly(); + bool IsReadOnly { get; } + + TReadOnlySupport AsWritable(); + } +} diff --git a/Core/IRepository.cs b/Core/IRepository.cs index 09e7b0e..cef4cc6 100644 --- a/Core/IRepository.cs +++ b/Core/IRepository.cs @@ -1,13 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core { - public interface IRepository { - IQueryable Query(); - - object GetKey(T entity); - T Load(object key); - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core { + public interface IRepository { + IQueryable Query(); + + object GetKey(T entity); + T Load(object key); + } +} diff --git a/Core/ImageProcessing/ExifOrientationProvider.cs b/Core/ImageProcessing/ExifOrientationProvider.cs index 9286d28..642317c 100644 --- a/Core/ImageProcessing/ExifOrientationProvider.cs +++ b/Core/ImageProcessing/ExifOrientationProvider.cs @@ -1,40 +1,39 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Text; - -using AshMind.Gallery.Core.Metadata; -using AshMind.Gallery.Core.IO; - -namespace AshMind.Gallery.Core.ImageProcessing { - public class ExifOrientationProvider : IOrientationProvider { - private const int ExifOrientationId = 274; - - private static IDictionary orientations = new Dictionary { - { 1, new ImageOrientation(0) }, - { 2, new ImageOrientation(0, ImageMirroring.Horizontal) }, - { 3, new ImageOrientation(180) }, - { 4, new ImageOrientation(0, ImageMirroring.Vertical) }, - { 5, new ImageOrientation(90, ImageMirroring.Vertical) }, - { 6, new ImageOrientation(270) }, - { 7, new ImageOrientation(90, ImageMirroring.Horizontal) }, - { 8, new ImageOrientation(90) }, - }; - - public ImageOrientation GetOrientation(Image image, IFile imageFile) { - if (!image.PropertyIdList.Contains(ExifOrientationId)) - return null; - - var exifOrientation = image.GetPropertyItem(ExifOrientationId).Value[0]; - if (exifOrientation == 0) - return null; - - return orientations[exifOrientation]; - } - - public int Priority { - get { return 0; } - } - } -} +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; + +using AshMind.Gallery.Core.Metadata; +using AshMind.Gallery.Core.IO; + +namespace AshMind.Gallery.Core.ImageProcessing { + public class ExifOrientationProvider : IOrientationProvider { + private const int ExifOrientationId = 274; + + private static readonly IDictionary orientations = new Dictionary { + { 1, new ImageOrientation(0) }, + { 2, new ImageOrientation(0, ImageMirroring.Horizontal) }, + { 3, new ImageOrientation(180) }, + { 4, new ImageOrientation(0, ImageMirroring.Vertical) }, + { 5, new ImageOrientation(90, ImageMirroring.Vertical) }, + { 6, new ImageOrientation(270) }, + { 7, new ImageOrientation(90, ImageMirroring.Horizontal) }, + { 8, new ImageOrientation(90) }, + }; + + public ImageOrientation GetOrientation(Image image, IFile imageFile) { + if (!image.PropertyIdList.Contains(ExifOrientationId)) + return null; + + var exifOrientation = image.GetPropertyItem(ExifOrientationId).Value[0]; + if (exifOrientation == 0) + return null; + + return orientations[exifOrientation]; + } + + public int Priority { + get { return 0; } + } + } +} diff --git a/Core/ImageProcessing/ICacheDependencyProvider.cs b/Core/ImageProcessing/ICacheDependencyProvider.cs index ea7bd4b..007dbf8 100644 --- a/Core/ImageProcessing/ICacheDependencyProvider.cs +++ b/Core/ImageProcessing/ICacheDependencyProvider.cs @@ -1,12 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Gallery.Core.IO; - -namespace AshMind.Gallery.Core.ImageProcessing { - public interface ICacheDependencyProvider { - IEnumerable GetRelatedChanges(IFile primary); - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Gallery.Core.IO; + +namespace AshMind.Gallery.Core.ImageProcessing { + public interface ICacheDependencyProvider { + IEnumerable GetRelatedChanges(IFile primary); + } +} diff --git a/Core/ImageProcessing/ImageCache.cs b/Core/ImageProcessing/ImageCache.cs index 5f3aab1..8d6a73d 100644 --- a/Core/ImageProcessing/ImageCache.cs +++ b/Core/ImageProcessing/ImageCache.cs @@ -1,102 +1,99 @@ -using System; -using System.Drawing; -using System.Drawing.Imaging; -using System.Linq; -using System.IO; -using System.Security.Cryptography; -using System.Text; -using System.Web; - -using AshMind.Gallery.Core.Internal; -using AshMind.Gallery.Core.IO; - -using Encoder = System.Drawing.Imaging.Encoder; -using Newtonsoft.Json; - -namespace AshMind.Gallery.Core.ImageProcessing { - public class ImageCache { - private readonly object DiskAccessLock = new object(); - - public ILocation CacheRoot { get; private set; } - public ImageCacheFormat Format { get; private set; } - - private readonly ICacheDependencyProvider[] dependencyProviders; - - private readonly ImageCodecInfo imageEncoder; - private readonly EncoderParameters imageEncoderParameters; - - public ImageCache(ILocation cacheRoot, ImageCacheFormat format, ICacheDependencyProvider[] dependencyProviders) { - this.CacheRoot = cacheRoot; - this.Format = format; - - this.dependencyProviders = dependencyProviders; - - this.imageEncoder = ImageCodecInfo.GetImageEncoders().First(c => c.MimeType == format.MimeType); - this.imageEncoderParameters = new EncoderParameters { - Param = new[] { new EncoderParameter(Encoder.Quality, 100L) } - }; - } - - public IFile GetTransform(IFile imageFile, int size, Func transform) { - var cacheFile = GetCacheFile(imageFile, size); - if (!IsCachedAndUpToDate(imageFile, cacheFile)) - return this.CacheTransform(imageFile, cacheFile, image => transform(image, size)); - - return cacheFile; - } - - private bool IsCachedAndUpToDate(IFile imageFile, IFile cacheFile) { - if (!cacheFile.Exists) - return false; - - var cacheLastWriteTime = cacheFile.GetLastWriteTime(); - if (imageFile.GetLastWriteTime() > cacheLastWriteTime) - return false; - - var relatedChanges = this.dependencyProviders.SelectMany(p => p.GetRelatedChanges(imageFile)); - return relatedChanges.All(change => change < cacheLastWriteTime); - } - - private IFile CacheTransform(IFile imageFile, IFile cacheFile, Converter transform) { - using (var original = LoadOriginalImage(imageFile)) - using (var result = transform(original)) - using (var stream = cacheFile.Open(FileLockMode.ReadWrite, FileOpenMode.Recreate)) - { - result.Save(stream, this.imageEncoder, this.imageEncoderParameters); - } - - return cacheFile; - } - - private Image LoadOriginalImage(IFile imageFile) { - lock (DiskAccessLock) { - // using (var stream = imageFile.Read(FileLockMode.Write)) { - // return Image.FromStream(stream); - // } - // TEMPHACK: for some reason above code does not load exif - return Image.FromFile(imageFile.Path); - } - } - - private IFile GetCacheFile(IFile imageFile, int size) { - var cacheKey = this.GetCacheKey(imageFile.Path, size); - return this.CacheRoot.GetFile(cacheKey, false); - } - - private string GetCacheKey(string imagePath, int size) { - return string.Format( - "{0}-x{1}.{2}", - GetCacheKey(imagePath), size, - this.Format.FileExtension - ); - } - - private string GetCacheKey(string imagePath) { - using (var md5 = MD5.Create()) { - var pathBytes = Encoding.UTF8.GetBytes(imagePath); - var key = md5.ComputeHashAsString(pathBytes); - return key; - } - } - } -} +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Linq; +using System.Security.Cryptography; +using System.Text; + +using AshMind.Gallery.Core.Internal; +using AshMind.Gallery.Core.IO; + +using Encoder = System.Drawing.Imaging.Encoder; + +namespace AshMind.Gallery.Core.ImageProcessing { + public class ImageCache { + private readonly object DiskAccessLock = new object(); + + public ILocation CacheRoot { get; private set; } + public ImageCacheFormat Format { get; private set; } + + private readonly ICacheDependencyProvider[] dependencyProviders; + + private readonly ImageCodecInfo imageEncoder; + private readonly EncoderParameters imageEncoderParameters; + + public ImageCache(ILocation cacheRoot, ImageCacheFormat format, ICacheDependencyProvider[] dependencyProviders) { + this.CacheRoot = cacheRoot; + this.Format = format; + + this.dependencyProviders = dependencyProviders; + + this.imageEncoder = ImageCodecInfo.GetImageEncoders().First(c => c.MimeType == format.MimeType); + this.imageEncoderParameters = new EncoderParameters { + Param = new[] { new EncoderParameter(Encoder.Quality, 100L) } + }; + } + + public IFile GetTransform(IFile imageFile, int size, Func transform) { + var cacheFile = GetCacheFile(imageFile, size); + if (!IsCachedAndUpToDate(imageFile, cacheFile)) + return this.CacheTransform(imageFile, cacheFile, image => transform(image, size)); + + return cacheFile; + } + + private bool IsCachedAndUpToDate(IFile imageFile, IFile cacheFile) { + if (!cacheFile.Exists) + return false; + + var cacheLastWriteTime = cacheFile.GetLastWriteTime(); + if (imageFile.GetLastWriteTime() > cacheLastWriteTime) + return false; + + var relatedChanges = this.dependencyProviders.SelectMany(p => p.GetRelatedChanges(imageFile)); + return relatedChanges.All(change => change < cacheLastWriteTime); + } + + private IFile CacheTransform(IFile imageFile, IFile cacheFile, Converter transform) { + using (var original = LoadOriginalImage(imageFile)) + using (var result = transform(original)) + using (var stream = cacheFile.Open(FileLockMode.ReadWrite, FileOpenMode.Recreate)) + { + result.Save(stream, this.imageEncoder, this.imageEncoderParameters); + } + + return cacheFile; + } + + private Image LoadOriginalImage(IFile imageFile) { + lock (DiskAccessLock) { + // using (var stream = imageFile.Read(FileLockMode.Write)) { + // return Image.FromStream(stream); + // } + // TEMPHACK: for some reason above code does not load exif + return Image.FromFile(imageFile.Path); + } + } + + private IFile GetCacheFile(IFile imageFile, int size) { + var cacheKey = this.GetCacheKey(imageFile.Path, size); + return this.CacheRoot.GetFile(cacheKey, false); + } + + private string GetCacheKey(string imagePath, int size) { + return string.Format( + "{0}-x{1}.{2}", + GetCacheKey(imagePath), size, + this.Format.FileExtension + ); + } + + private string GetCacheKey(string imagePath) { + using (var md5 = MD5.Create()) { + var pathBytes = Encoding.UTF8.GetBytes(imagePath); + var key = md5.ComputeHashAsString(pathBytes); + return key; + } + } + } +} diff --git a/Core/ImageProcessing/ImageCacheFormat.cs b/Core/ImageProcessing/ImageCacheFormat.cs index b222650..38df40c 100644 --- a/Core/ImageProcessing/ImageCacheFormat.cs +++ b/Core/ImageProcessing/ImageCacheFormat.cs @@ -1,30 +1,29 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Net.Mime; - -namespace AshMind.Gallery.Core.ImageProcessing { - public class ImageCacheFormat { - public string MimeType { get; private set; } - public string FileExtension { get; private set; } - - public static ImageCacheFormat Jpeg { get; private set; } - public static ImageCacheFormat Png { get; private set; } - - private ImageCacheFormat() { - } - - static ImageCacheFormat() { - Jpeg = new ImageCacheFormat { - MimeType = MediaTypeNames.Image.Jpeg, - FileExtension = "jpg" - }; - - Png = new ImageCacheFormat { - MimeType = "image/png", - FileExtension = "png" - }; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Mime; + +namespace AshMind.Gallery.Core.ImageProcessing { + public class ImageCacheFormat { + public string MimeType { get; private set; } + public string FileExtension { get; private set; } + + public static ImageCacheFormat Jpeg { get; private set; } + public static ImageCacheFormat Png { get; private set; } + + private ImageCacheFormat() { + } + + static ImageCacheFormat() { + Jpeg = new ImageCacheFormat { + MimeType = MediaTypeNames.Image.Jpeg, + FileExtension = "jpg" + }; + + Png = new ImageCacheFormat { + MimeType = "image/png", + FileExtension = "png" + }; + } + } +} diff --git a/Core/ImageProcessing/ImageProcessor.cs b/Core/ImageProcessing/ImageProcessor.cs index c9c9142..65a9e88 100644 --- a/Core/ImageProcessing/ImageProcessor.cs +++ b/Core/ImageProcessing/ImageProcessor.cs @@ -1,62 +1,50 @@ -using System; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Linq; -using System.Drawing.Imaging; -using System.Collections.Generic; - -using AshMind.Gallery.Core.Metadata; - -namespace AshMind.Gallery.Core.ImageProcessing { - using OrientationTuple = Tuple; - - internal static class ImageProcessor { - private const int ExifOrientationId = 274; - - private static HashSet flipsWidthHeight = new HashSet { - RotateFlipType.Rotate90FlipNone, - RotateFlipType.Rotate90FlipX, - RotateFlipType.Rotate90FlipY, - RotateFlipType.Rotate270FlipNone, - RotateFlipType.Rotate270FlipX, - RotateFlipType.Rotate270FlipY - }; - - private static IDictionary rotateFlip = new Dictionary { - { new OrientationTuple(0, ImageMirroring.None), RotateFlipType.RotateNoneFlipNone }, - { new OrientationTuple(90, ImageMirroring.None), RotateFlipType.Rotate270FlipNone }, - { new OrientationTuple(180, ImageMirroring.None), RotateFlipType.Rotate180FlipNone }, - { new OrientationTuple(270, ImageMirroring.None), RotateFlipType.Rotate90FlipNone } - }; - - public static Image ReduceSize(Image image, int desiredSize) { - var targetSize = EstimateSize(image.Size, desiredSize); - - var resized = new Bitmap(targetSize.Width, targetSize.Height, image.PixelFormat); - resized.SetResolution(image.HorizontalResolution, image.VerticalResolution); - - using (var g = Graphics.FromImage(resized)) { - g.InterpolationMode = InterpolationMode.HighQualityBicubic; - g.DrawImage(image, 0, 0, targetSize.Width, targetSize.Height); - } - - return resized; - } - - public static Image CorrectOrientation(Image image, ImageOrientation orientation) { - if (orientation.Angle == 0 && orientation.Mirroring == ImageMirroring.None) - return image; - - image.RotateFlip(rotateFlip[new OrientationTuple(orientation.Angle, orientation.Mirroring)]); - return image; - } - - private static Size EstimateSize(Size size, int desiredSize) { - var ratio = ((double)desiredSize / size.Width); - return new Size( - desiredSize, - (int)(size.Height * ratio) - ); - } - } -} +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Linq; +using System.Collections.Generic; + +using AshMind.Gallery.Core.Metadata; + +namespace AshMind.Gallery.Core.ImageProcessing { + using OrientationTuple = Tuple; + + internal static class ImageProcessor { + private static readonly IDictionary rotateFlip = new Dictionary { + { new OrientationTuple(0, ImageMirroring.None), RotateFlipType.RotateNoneFlipNone }, + { new OrientationTuple(90, ImageMirroring.None), RotateFlipType.Rotate270FlipNone }, + { new OrientationTuple(180, ImageMirroring.None), RotateFlipType.Rotate180FlipNone }, + { new OrientationTuple(270, ImageMirroring.None), RotateFlipType.Rotate90FlipNone } + }; + + public static Image ReduceSize(Image image, int desiredSize) { + var targetSize = EstimateSize(image.Size, desiredSize); + + var resized = new Bitmap(targetSize.Width, targetSize.Height, image.PixelFormat); + resized.SetResolution(image.HorizontalResolution, image.VerticalResolution); + + using (var g = Graphics.FromImage(resized)) { + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + g.DrawImage(image, 0, 0, targetSize.Width, targetSize.Height); + } + + return resized; + } + + public static Image CorrectOrientation(Image image, ImageOrientation orientation) { + if (orientation.Angle == 0 && orientation.Mirroring == ImageMirroring.None) + return image; + + image.RotateFlip(rotateFlip[new OrientationTuple(orientation.Angle, orientation.Mirroring)]); + return image; + } + + private static Size EstimateSize(Size size, int desiredSize) { + var ratio = ((double)desiredSize / size.Width); + return new Size( + desiredSize, + (int)(size.Height * ratio) + ); + } + } +} diff --git a/Core/Integration/Face.cs b/Core/Integration/Face.cs index e98354d..54215df 100644 --- a/Core/Integration/Face.cs +++ b/Core/Integration/Face.cs @@ -1,21 +1,20 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Gallery.Core.IO; - -namespace AshMind.Gallery.Core.Integration { - public class Face { - public Face(Person person, IFile file) { - Argument.VerifyNotNull("person", person); - Argument.VerifyNotNull("file", file); - - this.Person = person; - this.File = file; - } - - public Person Person { get; private set; } - public IFile File { get; private set; } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Gallery.Core.IO; + +namespace AshMind.Gallery.Core.Integration { + public class Face { + public Face(Person person, IFile file) { + Argument.VerifyNotNull("person", person); + Argument.VerifyNotNull("file", file); + + this.Person = person; + this.File = file; + } + + public Person Person { get; private set; } + public IFile File { get; private set; } + } +} diff --git a/Core/Integration/IFaceProvider.cs b/Core/Integration/IFaceProvider.cs index 7c1069e..f17a3e8 100644 --- a/Core/Integration/IFaceProvider.cs +++ b/Core/Integration/IFaceProvider.cs @@ -1,12 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Gallery.Core.IO; - -namespace AshMind.Gallery.Core.Integration { - public interface IFaceProvider { - IEnumerable GetFaces(ILocation location); - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Gallery.Core.IO; + +namespace AshMind.Gallery.Core.Integration { + public interface IFaceProvider { + IEnumerable GetFaces(ILocation location); + } +} diff --git a/Core/Integration/Person.cs b/Core/Integration/Person.cs index 5425aa9..7ec13c6 100644 --- a/Core/Integration/Person.cs +++ b/Core/Integration/Person.cs @@ -1,17 +1,16 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.Integration { - public class Person { - public Person(string name, IEnumerable emails) { - this.Name = name; - this.Emails = emails.ToList().AsReadOnly(); - } - - public string Name { get; private set; } - public ReadOnlyCollection Emails { get; private set; } - } -} +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +namespace AshMind.Gallery.Core.Integration { + public class Person { + public Person(string name, IEnumerable emails) { + this.Name = name; + this.Emails = emails.ToList().AsReadOnly(); + } + + public string Name { get; private set; } + public ReadOnlyCollection Emails { get; private set; } + } +} diff --git a/Core/Integration/Picasa/IniParts/PicasaIniContact.cs b/Core/Integration/Picasa/IniParts/PicasaIniContact.cs index 256c559..a877fd7 100644 --- a/Core/Integration/Picasa/IniParts/PicasaIniContact.cs +++ b/Core/Integration/Picasa/IniParts/PicasaIniContact.cs @@ -1,18 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.Integration.Picasa.IniParts { - public class PicasaIniContact { - public PicasaIniContact(string hash, string userCode, string id) { - this.Hash = hash; - this.UserCode = userCode; - this.ID = id; - } - - public string Hash { get; private set; } - public string UserCode { get; private set; } - public string ID { get; private set; } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.Integration.Picasa.IniParts { + public class PicasaIniContact { + public PicasaIniContact(string hash, string userCode, string id) { + this.Hash = hash; + this.UserCode = userCode; + this.ID = id; + } + + public string Hash { get; private set; } + public string UserCode { get; private set; } + public string ID { get; private set; } + } +} diff --git a/Core/Integration/Picasa/IniParts/PicasaIniFace.cs b/Core/Integration/Picasa/IniParts/PicasaIniFace.cs index 3e9d158..408b5df 100644 --- a/Core/Integration/Picasa/IniParts/PicasaIniFace.cs +++ b/Core/Integration/Picasa/IniParts/PicasaIniFace.cs @@ -1,14 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.Integration.Picasa.IniParts { - public class PicasaIniFace { - public PicasaIniFace(string contactHash) { - this.ContactHash = contactHash; - } - - public string ContactHash { get; private set; } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.Integration.Picasa.IniParts { + public class PicasaIniFace { + public PicasaIniFace(string contactHash) { + this.ContactHash = contactHash; + } + + public string ContactHash { get; private set; } + } +} diff --git a/Core/Integration/Picasa/IniParts/PicasaIniMetadata.cs b/Core/Integration/Picasa/IniParts/PicasaIniMetadata.cs index f66a52f..2310fa6 100644 --- a/Core/Integration/Picasa/IniParts/PicasaIniMetadata.cs +++ b/Core/Integration/Picasa/IniParts/PicasaIniMetadata.cs @@ -1,16 +1,15 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.Integration.Picasa.IniParts { - public class PicasaIniMetadata { - public PicasaIniMetadata() { - this.Faces = new List(); - } - - public int? Rotate { get; set; } - public bool Starred { get; set; } - public IList Faces { get; private set; } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.Integration.Picasa.IniParts { + public class PicasaIniMetadata { + public PicasaIniMetadata() { + this.Faces = new List(); + } + + public int? Rotate { get; set; } + public bool Starred { get; set; } + public IList Faces { get; private set; } + } +} diff --git a/Core/Integration/Picasa/PicasaDatabase.cs b/Core/Integration/Picasa/PicasaDatabase.cs index 25d76b3..9f55952 100644 --- a/Core/Integration/Picasa/PicasaDatabase.cs +++ b/Core/Integration/Picasa/PicasaDatabase.cs @@ -1,16 +1,15 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Gallery.Core.IO; - -namespace AshMind.Gallery.Core.Integration.Picasa { - public class PicasaDatabase { - public PicasaDatabase(IFile contactsXml) { - this.ContactsXml = contactsXml; - } - - public IFile ContactsXml { get; private set; } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Gallery.Core.IO; + +namespace AshMind.Gallery.Core.Integration.Picasa { + public class PicasaDatabase { + public PicasaDatabase(IFile contactsXml) { + this.ContactsXml = contactsXml; + } + + public IFile ContactsXml { get; private set; } + } +} diff --git a/Core/Integration/Picasa/PicasaFaceProvider.cs b/Core/Integration/Picasa/PicasaFaceProvider.cs index 76460f9..3f0295a 100644 --- a/Core/Integration/Picasa/PicasaFaceProvider.cs +++ b/Core/Integration/Picasa/PicasaFaceProvider.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Xml.Linq; -using System.Text; using AshMind.Extensions; @@ -61,7 +60,7 @@ select attribute.Value var parsed = this.parser.Parse(ini); var rawFaces = from pair in parsed.Items from face in pair.Value.Faces - select new { FileName = pair.Key, ContactHash = face.ContactHash }; + select new { FileName = pair.Key, face.ContactHash }; var contactsIndex = parsed.Contacts.ToDictionary(c => c.Hash); diff --git a/Core/Integration/Picasa/PicasaIni.cs b/Core/Integration/Picasa/PicasaIni.cs index 3c0f5b1..08554f6 100644 --- a/Core/Integration/Picasa/PicasaIni.cs +++ b/Core/Integration/Picasa/PicasaIni.cs @@ -1,18 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Gallery.Core.Integration.Picasa.IniParts; - -namespace AshMind.Gallery.Core.Integration.Picasa { - public class PicasaIni { - public PicasaIni() { - this.Contacts = new List(); - this.Items = new Dictionary(); - } - - public IList Contacts { get; private set; } - public IDictionary Items { get; private set; } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Gallery.Core.Integration.Picasa.IniParts; + +namespace AshMind.Gallery.Core.Integration.Picasa { + public class PicasaIni { + public PicasaIni() { + this.Contacts = new List(); + this.Items = new Dictionary(); + } + + public IList Contacts { get; private set; } + public IDictionary Items { get; private set; } + } +} diff --git a/Core/Integration/Picasa/PicasaIniDependencyProvider.cs b/Core/Integration/Picasa/PicasaIniDependencyProvider.cs index a599db5..a0869fc 100644 --- a/Core/Integration/Picasa/PicasaIniDependencyProvider.cs +++ b/Core/Integration/Picasa/PicasaIniDependencyProvider.cs @@ -1,25 +1,24 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Gallery.Core.ImageProcessing; -using AshMind.Gallery.Core.IO; - -namespace AshMind.Gallery.Core.Integration.Picasa { - public class PicasaIniDependencyProvider : ICacheDependencyProvider { - private readonly PicasaIniFileFinder iniFileFinder; - - public PicasaIniDependencyProvider(PicasaIniFileFinder iniFileFinder) { - this.iniFileFinder = iniFileFinder; - } - - public IEnumerable GetRelatedChanges(IFile primary) { - var picasaIni = this.iniFileFinder.FindIn(primary.Location); - if (picasaIni == null) - yield break; - - yield return picasaIni.GetLastWriteTime(); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Gallery.Core.ImageProcessing; +using AshMind.Gallery.Core.IO; + +namespace AshMind.Gallery.Core.Integration.Picasa { + public class PicasaIniDependencyProvider : ICacheDependencyProvider { + private readonly PicasaIniFileFinder iniFileFinder; + + public PicasaIniDependencyProvider(PicasaIniFileFinder iniFileFinder) { + this.iniFileFinder = iniFileFinder; + } + + public IEnumerable GetRelatedChanges(IFile primary) { + var picasaIni = this.iniFileFinder.FindIn(primary.Location); + if (picasaIni == null) + yield break; + + yield return picasaIni.GetLastWriteTime(); + } + } +} diff --git a/Core/Integration/Picasa/PicasaIniFileFinder.cs b/Core/Integration/Picasa/PicasaIniFileFinder.cs index 5127a36..142a2c0 100644 --- a/Core/Integration/Picasa/PicasaIniFileFinder.cs +++ b/Core/Integration/Picasa/PicasaIniFileFinder.cs @@ -1,18 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Gallery.Core.IO; - -namespace AshMind.Gallery.Core.Integration.Picasa { - public class PicasaIniFileFinder { - private static readonly string[] fileNames = { ".picasa.ini", "picasa.ini" }; - - public IFile FindIn(ILocation location) { - return fileNames - .Select(name => location.GetFile(name)) - .FirstOrDefault(f => f != null); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Gallery.Core.IO; + +namespace AshMind.Gallery.Core.Integration.Picasa { + public class PicasaIniFileFinder { + private static readonly string[] fileNames = { ".picasa.ini", "picasa.ini" }; + + public IFile FindIn(ILocation location) { + return fileNames + .Select(name => location.GetFile(name)) + .FirstOrDefault(f => f != null); + } + } +} diff --git a/Core/Integration/Picasa/PicasaIniParser.cs b/Core/Integration/Picasa/PicasaIniParser.cs index 8297955..616c8c1 100644 --- a/Core/Integration/Picasa/PicasaIniParser.cs +++ b/Core/Integration/Picasa/PicasaIniParser.cs @@ -1,93 +1,93 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; - -using AshMind.Extensions; - -using AshMind.Gallery.Core.IO; -using AshMind.Gallery.Core.Integration.Picasa.IniParts; - -namespace AshMind.Gallery.Core.Integration.Picasa { - public class PicasaIniParser { - public PicasaIni Parse(IFile file) { - Argument.VerifyNotNull("file", file); - - var lines = file.ReadAllLines(); - return Parse(lines); - } - - private PicasaIni Parse(IEnumerable lines) { - var result = new PicasaIni(); - var linesIterator = lines.GetEnumerator(); - - var finished = !linesIterator.MoveNext(); - while (!finished) { - var line = linesIterator.Current; - if (line == "[Contacts]") { - MoveOverContacts(result, linesIterator, out finished); - continue; - } - else if (line.StartsWith("[") && line.Contains(".")) { - MoveOverItem(result, line, linesIterator, out finished); - continue; - } - - finished = !linesIterator.MoveNext(); - } - - return result; - } - - // example format: 8f7eee7d92388080=ashmind_lh,108 - private void MoveOverContacts(PicasaIni ini, IEnumerator linesIterator, out bool linesIteratorEnded) { - MoveOver(linesIterator, line => { - var parsed = Regex.Match(line, "^(?[^=]+)=(?[^,]+),(?[^,]+)$"); - if (!parsed.Success) - return; - - ini.Contacts.Add(new PicasaIniContact( - parsed.Groups["hash"].Value, - parsed.Groups["user"].Value, - parsed.Groups["id"].Value - )); - }, out linesIteratorEnded); - } - - - private void MoveOverItem(PicasaIni ini, string firstLine, IEnumerator linesIterator, out bool linesIteratorEnded) { - var fileName = firstLine.RemoveStart("[").RemoveEnd("]"); - var metadata = ini.Items.GetValueOrDefault(fileName); - if (metadata == null) { - metadata = new PicasaIniMetadata(); - ini.Items.Add(fileName, metadata); - } - - MoveOver(linesIterator, line => { - if (line.StartsWith("rotate=")) { - var rotate = int.Parse(Regex.Match(line, @"rotate\(([\d+])\)").Groups[1].Value); - metadata.Rotate = rotate; - } - else if (line.StartsWith("faces=")) { - var faces = line.SubstringAfter("faces=").Split(';'); - metadata.Faces.Clear(); - foreach (var face in faces) { - metadata.Faces.Add(new PicasaIniFace(face.SubstringAfter(","))); - } - } - }, out linesIteratorEnded); - } - - private void MoveOver(IEnumerator linesIterator, Action action, out bool linesIteratorEnded) { - linesIteratorEnded = !linesIterator.MoveNext(); - var line = linesIterator.Current; - while (!linesIteratorEnded && !line.StartsWith("[")) { - action(line); - - linesIteratorEnded = !linesIterator.MoveNext(); - line = linesIterator.Current; - } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +using AshMind.Extensions; + +using AshMind.Gallery.Core.IO; +using AshMind.Gallery.Core.Integration.Picasa.IniParts; + +namespace AshMind.Gallery.Core.Integration.Picasa { + public class PicasaIniParser { + public PicasaIni Parse(IFile file) { + Argument.VerifyNotNull("file", file); + + var lines = file.ReadAllLines(); + return Parse(lines); + } + + private PicasaIni Parse(IEnumerable lines) { + var result = new PicasaIni(); + var linesIterator = lines.GetEnumerator(); + + var finished = !linesIterator.MoveNext(); + while (!finished) { + var line = linesIterator.Current; + if (line == "[Contacts]") { + MoveOverContacts(result, linesIterator, out finished); + continue; + } + + if (line.StartsWith("[") && line.Contains(".")) { + MoveOverItem(result, line, linesIterator, out finished); + continue; + } + + finished = !linesIterator.MoveNext(); + } + + return result; + } + + // example format: 8f7eee7d92388080=ashmind_lh,108 + private void MoveOverContacts(PicasaIni ini, IEnumerator linesIterator, out bool linesIteratorEnded) { + MoveOver(linesIterator, line => { + var parsed = Regex.Match(line, "^(?[^=]+)=(?[^,]+),(?[^,]+)$"); + if (!parsed.Success) + return; + + ini.Contacts.Add(new PicasaIniContact( + parsed.Groups["hash"].Value, + parsed.Groups["user"].Value, + parsed.Groups["id"].Value + )); + }, out linesIteratorEnded); + } + + + private void MoveOverItem(PicasaIni ini, string firstLine, IEnumerator linesIterator, out bool linesIteratorEnded) { + var fileName = firstLine.RemoveStart("[").RemoveEnd("]"); + var metadata = ini.Items.GetValueOrDefault(fileName); + if (metadata == null) { + metadata = new PicasaIniMetadata(); + ini.Items.Add(fileName, metadata); + } + + MoveOver(linesIterator, line => { + if (line.StartsWith("rotate=")) { + var rotate = int.Parse(Regex.Match(line, @"rotate\(([\d+])\)").Groups[1].Value); + metadata.Rotate = rotate; + } + else if (line.StartsWith("faces=")) { + var faces = line.SubstringAfter("faces=").Split(';'); + metadata.Faces.Clear(); + foreach (var face in faces) { + metadata.Faces.Add(new PicasaIniFace(face.SubstringAfter(","))); + } + } + }, out linesIteratorEnded); + } + + private void MoveOver(IEnumerator linesIterator, Action action, out bool linesIteratorEnded) { + linesIteratorEnded = !linesIterator.MoveNext(); + var line = linesIterator.Current; + while (!linesIteratorEnded && !line.StartsWith("[")) { + action(line); + + linesIteratorEnded = !linesIterator.MoveNext(); + line = linesIterator.Current; + } + } + } +} diff --git a/Core/Integration/Picasa/PicasaOrientationProvider.cs b/Core/Integration/Picasa/PicasaOrientationProvider.cs index b14609b..748c043 100644 --- a/Core/Integration/Picasa/PicasaOrientationProvider.cs +++ b/Core/Integration/Picasa/PicasaOrientationProvider.cs @@ -1,42 +1,41 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Text; - -using AshMind.Extensions; - -using AshMind.Gallery.Core.Metadata; -using AshMind.Gallery.Core.IO; - -namespace AshMind.Gallery.Core.Integration.Picasa { - public class PicasaOrientationProvider : IOrientationProvider { - private readonly PicasaIniFileFinder picasaIniFileFinder; - private readonly PicasaIniParser picasaIniParser; - - public PicasaOrientationProvider(PicasaIniFileFinder picasaIniFileFinder, PicasaIniParser picasaIniParser) { - this.picasaIniFileFinder = picasaIniFileFinder; - this.picasaIniParser = picasaIniParser; - } - - public ImageOrientation GetOrientation(Image image, IFile imageFile) { - var picasaIniFile = this.picasaIniFileFinder.FindIn(imageFile.Location); - if (picasaIniFile == null) - return null; - - var picasaIni = this.picasaIniParser.Parse(picasaIniFile); - var item = picasaIni.Items.GetValueOrDefault(imageFile.Name); - if (item == null || item.Rotate == null) - return null; - - if (item.Rotate == 0) - return new ImageOrientation(0); - - return new ImageOrientation((4 - item.Rotate.Value) * 90); - } - - public int Priority { - get { return 1000; } - } - } -} +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; + +using AshMind.Extensions; + +using AshMind.Gallery.Core.Metadata; +using AshMind.Gallery.Core.IO; + +namespace AshMind.Gallery.Core.Integration.Picasa { + public class PicasaOrientationProvider : IOrientationProvider { + private readonly PicasaIniFileFinder picasaIniFileFinder; + private readonly PicasaIniParser picasaIniParser; + + public PicasaOrientationProvider(PicasaIniFileFinder picasaIniFileFinder, PicasaIniParser picasaIniParser) { + this.picasaIniFileFinder = picasaIniFileFinder; + this.picasaIniParser = picasaIniParser; + } + + public ImageOrientation GetOrientation(Image image, IFile imageFile) { + var picasaIniFile = this.picasaIniFileFinder.FindIn(imageFile.Location); + if (picasaIniFile == null) + return null; + + var picasaIni = this.picasaIniParser.Parse(picasaIniFile); + var item = picasaIni.Items.GetValueOrDefault(imageFile.Name); + if (item == null || item.Rotate == null) + return null; + + if (item.Rotate == 0) + return new ImageOrientation(0); + + return new ImageOrientation((4 - item.Rotate.Value) * 90); + } + + public int Priority { + get { return 1000; } + } + } +} diff --git a/Core/LazyExtensions.cs b/Core/LazyExtensions.cs index 3f57973..ad095db 100644 --- a/Core/LazyExtensions.cs +++ b/Core/LazyExtensions.cs @@ -1,19 +1,18 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core { - public static class LazyExtensions { - public static Lazy Apply(this Lazy lazy, Action action) { - return lazy.Apply(value => { - action(value); - return value; - }); - } - - public static Lazy Apply(this Lazy lazy, Func function) { - return new Lazy(() => function(lazy.Value)); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core { + public static class LazyExtensions { + public static Lazy Apply(this Lazy lazy, Action action) { + return lazy.Apply(value => { + action(value); + return value; + }); + } + + public static Lazy Apply(this Lazy lazy, Func function) { + return new Lazy(() => function(lazy.Value)); + } + } +} diff --git a/Core/Metadata/ILocationMetadataProvider.cs b/Core/Metadata/ILocationMetadataProvider.cs index 56972d9..859cc82 100644 --- a/Core/Metadata/ILocationMetadataProvider.cs +++ b/Core/Metadata/ILocationMetadataProvider.cs @@ -1,16 +1,15 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Gallery.Core.IO; - -namespace AshMind.Gallery.Core.Metadata { - public interface ILocationMetadataProvider { - T GetMetadata(ILocation location, string metadataKey) - where T : class; - - void ApplyMetadata(ILocation location, string metadataKey, T metadata) - where T : class; - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Gallery.Core.IO; + +namespace AshMind.Gallery.Core.Metadata { + public interface ILocationMetadataProvider { + T GetMetadata(ILocation location, string metadataKey) + where T : class; + + void ApplyMetadata(ILocation location, string metadataKey, T metadata) + where T : class; + } +} diff --git a/Core/Metadata/IOrientationProvider.cs b/Core/Metadata/IOrientationProvider.cs index ba6af5d..52f72c9 100644 --- a/Core/Metadata/IOrientationProvider.cs +++ b/Core/Metadata/IOrientationProvider.cs @@ -1,15 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Text; - -using AshMind.Gallery.Core.IO; - -namespace AshMind.Gallery.Core.Metadata { - public interface IOrientationProvider { - ImageOrientation GetOrientation(Image image, IFile imageFile); - - int Priority { get; } - } -} +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; + +using AshMind.Gallery.Core.IO; + +namespace AshMind.Gallery.Core.Metadata { + public interface IOrientationProvider { + ImageOrientation GetOrientation(Image image, IFile imageFile); + + int Priority { get; } + } +} diff --git a/Core/Metadata/ImageMirroring.cs b/Core/Metadata/ImageMirroring.cs index 57409fd..a23e588 100644 --- a/Core/Metadata/ImageMirroring.cs +++ b/Core/Metadata/ImageMirroring.cs @@ -1,12 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.Metadata { - public enum ImageMirroring { - None, - Horizontal, - Vertical - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.Metadata { + public enum ImageMirroring { + None, + Horizontal, + Vertical + } +} diff --git a/Core/Metadata/ImageOrientation.cs b/Core/Metadata/ImageOrientation.cs index 0286791..c44cf05 100644 --- a/Core/Metadata/ImageOrientation.cs +++ b/Core/Metadata/ImageOrientation.cs @@ -1,19 +1,18 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.Metadata { - public class ImageOrientation { - public ImageOrientation(int angle) : this(angle, ImageMirroring.None) { - } - - public ImageOrientation(int angle, ImageMirroring mirroring) { - this.Angle = angle; - this.Mirroring = mirroring; - } - - public int Angle { get; private set; } - public ImageMirroring Mirroring { get; private set; } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.Metadata { + public class ImageOrientation { + public ImageOrientation(int angle) : this(angle, ImageMirroring.None) { + } + + public ImageOrientation(int angle, ImageMirroring mirroring) { + this.Angle = angle; + this.Mirroring = mirroring; + } + + public int Angle { get; private set; } + public ImageMirroring Mirroring { get; private set; } + } +} diff --git a/Core/Metadata/Internal/JsonLocationMetadataProvider.cs b/Core/Metadata/Internal/JsonLocationMetadataProvider.cs index 25f9da5..7e38fda 100644 --- a/Core/Metadata/Internal/JsonLocationMetadataProvider.cs +++ b/Core/Metadata/Internal/JsonLocationMetadataProvider.cs @@ -1,86 +1,85 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Caching; -using System.Text; - -using Newtonsoft.Json; - -using AshMind.Extensions; - -using AshMind.Gallery.Core.IO; -using AshMind.Gallery.Core.Fixes; - -namespace AshMind.Gallery.Core.Metadata.Internal { - public class JsonLocationMetadataProvider : ILocationMetadataProvider { - private readonly ObjectCache cache; - - public JsonLocationMetadataProvider(ObjectCache cache) { - this.cache = cache; - } - - public T GetMetadata(ILocation location, string metadataKey) - where T : class - { - Argument.VerifyNotNull("location", location); - Argument.VerifyNotNullOrEmpty("metadataKey", metadataKey); - - var metadataRoot = this.GetCachedMetadata(location); - var untypedMetadata = metadataRoot.GetValueOrDefault(metadataKey); - if (untypedMetadata == null) - return null; - - return JsonConvert.DeserializeObject( - JsonConvert.SerializeObject(untypedMetadata) - ); - } - - public void ApplyMetadata(ILocation location, string metadataKey, T metadata) - where T : class - { - Argument.VerifyNotNull("location", location); - Argument.VerifyNotNullOrEmpty("metadataKey", metadataKey); - Argument.VerifyNotNull("metadata", metadata); - - var file = GetMetadataFile(location, false); - - var metadataRoot = this.GetCachedMetadata(location); - metadataRoot[metadataKey] = metadata; - - // http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/8d7aa093-5038-4b4a-a63e-bf949f0d0d13 - if (file.Exists) - file.SetHidden(false); - - file.WriteAllText( - JsonConvert.SerializeObject(metadataRoot, Formatting.Indented) - ); - file.SetHidden(true); - } - - private IDictionary GetCachedMetadata(ILocation location) { - var cacheKey = "metadata:" + location.Path; - var metadata = (IDictionary)this.cache.Get(cacheKey); - if (metadata != null) - return metadata; - - var file = GetMetadataFile(location, true); - if (file == null) { - metadata = new Dictionary(); - this.cache.Add(cacheKey, metadata, new CacheItemPolicy { - ChangeMonitors = { new FixedFileChangeMonitor(new[] { location.Path }) } - }); - return metadata; - } - - metadata = JsonConvert.DeserializeObject>(file.ReadAllText()); - this.cache.Add(cacheKey, metadata, new CacheItemPolicy { - ChangeMonitors = { new FixedFileChangeMonitor(new[] { file.Path }) } - }); - return metadata; - } - - private IFile GetMetadataFile(ILocation location, bool nullUnlessExists) { - return location.GetFile(".gallery.info", nullUnlessExists); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Caching; + +using Newtonsoft.Json; + +using AshMind.Extensions; + +using AshMind.Gallery.Core.IO; +using AshMind.Gallery.Core.Fixes; + +namespace AshMind.Gallery.Core.Metadata.Internal { + public class JsonLocationMetadataProvider : ILocationMetadataProvider { + private readonly ObjectCache cache; + + public JsonLocationMetadataProvider(ObjectCache cache) { + this.cache = cache; + } + + public T GetMetadata(ILocation location, string metadataKey) + where T : class + { + Argument.VerifyNotNull("location", location); + Argument.VerifyNotNullOrEmpty("metadataKey", metadataKey); + + var metadataRoot = this.GetCachedMetadata(location); + var untypedMetadata = metadataRoot.GetValueOrDefault(metadataKey); + if (untypedMetadata == null) + return null; + + return JsonConvert.DeserializeObject( + JsonConvert.SerializeObject(untypedMetadata) + ); + } + + public void ApplyMetadata(ILocation location, string metadataKey, T metadata) + where T : class + { + Argument.VerifyNotNull("location", location); + Argument.VerifyNotNullOrEmpty("metadataKey", metadataKey); + Argument.VerifyNotNull("metadata", metadata); + + var file = GetMetadataFile(location, false); + + var metadataRoot = this.GetCachedMetadata(location); + metadataRoot[metadataKey] = metadata; + + // http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/8d7aa093-5038-4b4a-a63e-bf949f0d0d13 + if (file.Exists) + file.SetHidden(false); + + file.WriteAllText( + JsonConvert.SerializeObject(metadataRoot, Formatting.Indented) + ); + file.SetHidden(true); + } + + private IDictionary GetCachedMetadata(ILocation location) { + var cacheKey = "metadata:" + location.Path; + var metadata = (IDictionary)this.cache.Get(cacheKey); + if (metadata != null) + return metadata; + + var file = GetMetadataFile(location, true); + if (file == null) { + metadata = new Dictionary(); + this.cache.Add(cacheKey, metadata, new CacheItemPolicy { + ChangeMonitors = { new FixedFileChangeMonitor(new[] { location.Path }) } + }); + return metadata; + } + + metadata = JsonConvert.DeserializeObject>(file.ReadAllText()); + this.cache.Add(cacheKey, metadata, new CacheItemPolicy { + ChangeMonitors = { new FixedFileChangeMonitor(new[] { file.Path }) } + }); + return metadata; + } + + private IFile GetMetadataFile(ILocation location, bool nullUnlessExists) { + return location.GetFile(".gallery.info", nullUnlessExists); + } + } +} diff --git a/Core/PreviewFacade.cs b/Core/PreviewFacade.cs index d853a45..dab4e3a 100644 --- a/Core/PreviewFacade.cs +++ b/Core/PreviewFacade.cs @@ -1,46 +1,44 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Drawing; - -using AshMind.Gallery.Core.ImageProcessing; -using AshMind.Gallery.Core.IO; -using AshMind.Gallery.Core.Metadata; - -namespace AshMind.Gallery.Core { - public class PreviewFacade { - private readonly ImageCache cache; - private readonly IOrientationProvider[] orientationProviders; - - public PreviewFacade( - ImageCache cache, - IOrientationProvider[] orientationProviders - ) { - this.cache = cache; - this.orientationProviders = orientationProviders; - } - - public IFile GetPreview(IFile originalFile, int size) { - return this.cache.GetTransform( - originalFile, size, - (image, desiredSize) => { - var orientation = this.orientationProviders - .OrderByDescending(p => p.Priority) - .Select(p => p.GetOrientation(image, originalFile)) - .Where(o => o != null) - .FirstOrDefault(); - image = ImageProcessor.ReduceSize(image, desiredSize); - if (orientation != null) - image = ImageProcessor.CorrectOrientation(image, orientation); - - return image; - } - ); - } - - public string ImageMimeType { - get { return this.cache.Format.MimeType; } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Gallery.Core.ImageProcessing; +using AshMind.Gallery.Core.IO; +using AshMind.Gallery.Core.Metadata; + +namespace AshMind.Gallery.Core { + public class PreviewFacade { + private readonly ImageCache cache; + private readonly IOrientationProvider[] orientationProviders; + + public PreviewFacade( + ImageCache cache, + IOrientationProvider[] orientationProviders + ) { + this.cache = cache; + this.orientationProviders = orientationProviders; + } + + public IFile GetPreview(IFile originalFile, int size) { + return this.cache.GetTransform( + originalFile, size, + (image, desiredSize) => { + var orientation = this.orientationProviders + .OrderByDescending(p => p.Priority) + .Select(p => p.GetOrientation(image, originalFile)) + .Where(o => o != null) + .FirstOrDefault(); + image = ImageProcessor.ReduceSize(image, desiredSize); + if (orientation != null) + image = ImageProcessor.CorrectOrientation(image, orientation); + + return image; + } + ); + } + + public string ImageMimeType { + get { return this.cache.Format.MimeType; } + } + } +} diff --git a/Core/Properties/AssemblyInfo.cs b/Core/Properties/AssemblyInfo.cs index ef10161..e720c74 100644 --- a/Core/Properties/AssemblyInfo.cs +++ b/Core/Properties/AssemblyInfo.cs @@ -1,39 +1,33 @@ -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("Gallery.Core")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Handy Labs")] -[assembly: AssemblyProduct("Gallery.Core")] -[assembly: AssemblyCopyright("Copyright © Handy Labs 2007")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// 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("f45e73f7-332c-4e04-b565-41f135b4d097")] - -// 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 Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] - -[assembly: InternalsVisibleTo("AshMind.Gallery.Core.Tests")] +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("AshMind.Gallery.Core")] +[assembly: AssemblyProduct("AshMind.Gallery.Core")] + +// 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("f45e73f7-332c-4e04-b565-41f135b4d097")] + +// 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 Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] + +[assembly: InternalsVisibleTo("AshMind.Gallery.Core.Tests")] [assembly: InternalsVisibleTo("Mocks")] \ No newline at end of file diff --git a/Core/Security/AbstractPermissionProvider.cs b/Core/Security/AbstractPermissionProvider.cs index 6cca5ba..0cd3049 100644 --- a/Core/Security/AbstractPermissionProvider.cs +++ b/Core/Security/AbstractPermissionProvider.cs @@ -1,41 +1,40 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.Security { - public abstract class AbstractPermissionProvider : IPermissionProvider { - public virtual bool CanGetPermissions(TTarget target) { - return true; - } - - public virtual bool CanSetPermissions(TTarget target) { - return true; - } - - public abstract IEnumerable GetPermissions(TTarget target); - public abstract void SetPermissions(TTarget target, IEnumerable permissions); - - #region IPermissionProvider Members - - IEnumerable IPermissionProvider.GetPermissions(object target) { - return this.GetPermissions((TTarget)target); - } - - void IPermissionProvider.SetPermissions(object target, IEnumerable permissions) { - this.SetPermissions((TTarget)target, permissions); - } - - bool IPermissionProvider.CanGetPermissions(object target) { - return target is TTarget - && this.CanGetPermissions((TTarget)target); - } - - bool IPermissionProvider.CanSetPermissions(object target) { - return target is TTarget - && this.CanSetPermissions((TTarget)target); - } - - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.Security { + public abstract class AbstractPermissionProvider : IPermissionProvider { + public virtual bool CanGetPermissions(TTarget target) { + return true; + } + + public virtual bool CanSetPermissions(TTarget target) { + return true; + } + + public abstract IEnumerable GetPermissions(TTarget target); + public abstract void SetPermissions(TTarget target, IEnumerable permissions); + + #region IPermissionProvider Members + + IEnumerable IPermissionProvider.GetPermissions(object target) { + return this.GetPermissions((TTarget)target); + } + + void IPermissionProvider.SetPermissions(object target, IEnumerable permissions) { + this.SetPermissions((TTarget)target, permissions); + } + + bool IPermissionProvider.CanGetPermissions(object target) { + return target is TTarget + && this.CanGetPermissions((TTarget)target); + } + + bool IPermissionProvider.CanSetPermissions(object target) { + return target is TTarget + && this.CanSetPermissions((TTarget)target); + } + + #endregion + } +} diff --git a/Core/Security/AnonymousMember.cs b/Core/Security/AnonymousMember.cs index 023f8e4..16e0bb6 100644 --- a/Core/Security/AnonymousMember.cs +++ b/Core/Security/AnonymousMember.cs @@ -1,33 +1,32 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.Security { - public class AnonymousMember : IUser { - private readonly IUserGroup userGroup; - - public AnonymousMember(IUserGroup userGroup) { - this.userGroup = userGroup; - } - - public override bool Equals(object obj) { - if (obj.GetType() != this.GetType()) - return false; - - return this.userGroup.Equals(((AnonymousMember)obj).userGroup); - } - - public override int GetHashCode() { - return this.GetType().GetHashCode() ^ this.userGroup.GetHashCode(); - } - - public string Name { - get { return "Anonymous"; } - } - - IEnumerable IUserGroup.GetUsers() { - yield return this; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.Security { + public class AnonymousMember : IUser { + private readonly IUserGroup userGroup; + + public AnonymousMember(IUserGroup userGroup) { + this.userGroup = userGroup; + } + + public override bool Equals(object obj) { + if (obj.GetType() != this.GetType()) + return false; + + return this.userGroup.Equals(((AnonymousMember)obj).userGroup); + } + + public override int GetHashCode() { + return this.GetType().GetHashCode() ^ this.userGroup.GetHashCode(); + } + + public string Name { + get { return "Anonymous"; } + } + + IEnumerable IUserGroup.GetUsers() { + yield return this; + } + } +} diff --git a/Core/Security/IAuthorizationService.cs b/Core/Security/IAuthorizationService.cs index e61bb3c..0072891 100644 --- a/Core/Security/IAuthorizationService.cs +++ b/Core/Security/IAuthorizationService.cs @@ -1,12 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.Security { - public interface IAuthorizationService { - bool IsAuthorized(IUser user, SecurableAction action, object target); - void AuthorizeTo(SecurableAction action, object target, IEnumerable userGroups); - IEnumerable GetAuthorizedTo(SecurableAction action, object target); - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.Security { + public interface IAuthorizationService { + bool IsAuthorized(IUser user, SecurableAction action, object target); + void AuthorizeTo(SecurableAction action, object target, IEnumerable userGroups); + IEnumerable GetAuthorizedTo(SecurableAction action, object target); + } +} diff --git a/Core/Security/IPermissionProvider.cs b/Core/Security/IPermissionProvider.cs index 418d50b..e815c01 100644 --- a/Core/Security/IPermissionProvider.cs +++ b/Core/Security/IPermissionProvider.cs @@ -1,14 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.Security { - public interface IPermissionProvider { - bool CanGetPermissions(object target); - bool CanSetPermissions(object target); - - IEnumerable GetPermissions(object target); - void SetPermissions(object target, IEnumerable permissions); - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.Security { + public interface IPermissionProvider { + bool CanGetPermissions(object target); + bool CanSetPermissions(object target); + + IEnumerable GetPermissions(object target); + void SetPermissions(object target, IEnumerable permissions); + } +} diff --git a/Core/Security/IUser.cs b/Core/Security/IUser.cs index 491b43f..69431ac 100644 --- a/Core/Security/IUser.cs +++ b/Core/Security/IUser.cs @@ -1,9 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.Security { - public interface IUser : IUserGroup { - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.Security { + public interface IUser : IUserGroup { + } +} diff --git a/Core/Security/IUserGroup.cs b/Core/Security/IUserGroup.cs index a0d0cba..77d8254 100644 --- a/Core/Security/IUserGroup.cs +++ b/Core/Security/IUserGroup.cs @@ -1,11 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.Security { - public interface IUserGroup { - string Name { get; } - IEnumerable GetUsers(); - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.Security { + public interface IUserGroup { + string Name { get; } + IEnumerable GetUsers(); + } +} diff --git a/Core/Security/IUserGroupSecureReferenceStrategy.cs b/Core/Security/IUserGroupSecureReferenceStrategy.cs index 2ecdc68..8f5234e 100644 --- a/Core/Security/IUserGroupSecureReferenceStrategy.cs +++ b/Core/Security/IUserGroupSecureReferenceStrategy.cs @@ -1,13 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Gallery.Core.Security; - -namespace AshMind.Gallery.Core.Security { - public interface IUserGroupSecureReferenceStrategy { - IUserGroup ResolveReference(string reference, IEnumerable userGroups); - string GetReference(IUserGroup userGroup); - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.Security { + public interface IUserGroupSecureReferenceStrategy { + IUserGroup ResolveReference(string reference, IEnumerable userGroups); + string GetReference(IUserGroup userGroup); + } +} diff --git a/Core/Security/Internal/AuthorizationService.cs b/Core/Security/Internal/AuthorizationService.cs index d3b5393..cc73bef 100644 --- a/Core/Security/Internal/AuthorizationService.cs +++ b/Core/Security/Internal/AuthorizationService.cs @@ -1,62 +1,61 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Extensions; - -namespace AshMind.Gallery.Core.Security.Internal { - public class AuthorizationService : IAuthorizationService { - private readonly HashSet superGroups = new HashSet(); - private readonly IPermissionProvider[] providers; - - public AuthorizationService( - IRepository userGroupRepository, - IPermissionProvider[] providers - ) { - superGroups.AddRange(userGroupRepository.Query().Where(g => g.IsSuper)); - this.providers = providers; - } - - public IEnumerable GetAuthorizedTo(SecurableAction action, object target) { - foreach (var group in superGroups) { - yield return group; - } - - if (action == SecurableAction.ManageSecurity) - yield break; - - var otherGroups = ( - from provider in this.providers - where provider.CanGetPermissions(target) - from permission in provider.GetPermissions(target) - where permission.Action == action - && !superGroups.Contains(permission.Group) - select permission.Group - ).Distinct(); - - foreach (var group in otherGroups) { - yield return group; - } - } - - public bool IsAuthorized(IUser user, SecurableAction action, object target) { - return GetAuthorizedTo(action, target) - .Any(g => g.GetUsers().Contains(user)) || user == User.System; - } - - public void AuthorizeTo(SecurableAction action, object target, IEnumerable userGroups) { - var permissions = userGroups.Select(group => new Permission { - Action = action, - Group = group - }); - - var provider = this.providers.FirstOrDefault(p => p.CanSetPermissions(target)); - - if (provider == null) - throw new NotSupportedException(); - - provider.SetPermissions(target, permissions); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Extensions; + +namespace AshMind.Gallery.Core.Security.Internal { + public class AuthorizationService : IAuthorizationService { + private readonly HashSet superGroups = new HashSet(); + private readonly IPermissionProvider[] providers; + + public AuthorizationService( + IRepository userGroupRepository, + IPermissionProvider[] providers + ) { + superGroups.AddRange(userGroupRepository.Query().Where(g => g.IsSuper)); + this.providers = providers; + } + + public IEnumerable GetAuthorizedTo(SecurableAction action, object target) { + foreach (var group in superGroups) { + yield return group; + } + + if (action == SecurableAction.ManageSecurity) + yield break; + + var otherGroups = ( + from provider in this.providers + where provider.CanGetPermissions(target) + from permission in provider.GetPermissions(target) + where permission.Action == action + && !superGroups.Contains(permission.Group) + select permission.Group + ).Distinct(); + + foreach (var group in otherGroups) { + yield return group; + } + } + + public bool IsAuthorized(IUser user, SecurableAction action, object target) { + return GetAuthorizedTo(action, target) + .Any(g => g.GetUsers().Contains(user)) || user == User.System; + } + + public void AuthorizeTo(SecurableAction action, object target, IEnumerable userGroups) { + var permissions = userGroups.Select(group => new Permission { + Action = action, + Group = group + }); + + var provider = this.providers.FirstOrDefault(p => p.CanSetPermissions(target)); + + if (provider == null) + throw new NotSupportedException(); + + provider.SetPermissions(target, permissions); + } + } +} diff --git a/Core/Security/Internal/JsonKeyPermissionProvider.cs b/Core/Security/Internal/JsonKeyPermissionProvider.cs index 0a93474..eb6f6de 100644 --- a/Core/Security/Internal/JsonKeyPermissionProvider.cs +++ b/Core/Security/Internal/JsonKeyPermissionProvider.cs @@ -1,87 +1,83 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using System.Threading; - -using Newtonsoft.Json; - -using AshMind.Extensions; - -using AshMind.Gallery.Core.Internal; -using AshMind.Gallery.Core.IO; -using AshMind.Gallery.Core.Security; - -namespace AshMind.Gallery.Core.Security.Internal { - internal class JsonKeyPermissionProvider : AbstractPermissionProvider { - private readonly IFile permissionStore; - private readonly IRepository userGroupRepository; - - private readonly ReaderWriterLockSlim rawPermissionsLock = new ReaderWriterLockSlim(); - private readonly IDictionary>> rawPermissions = new Dictionary>>(); - - public JsonKeyPermissionProvider( - IFile permissionStore, - IRepository userGroupRepository - ) { - this.permissionStore = permissionStore; - this.userGroupRepository = userGroupRepository; - - if (this.permissionStore.Exists) - JsonConvert.PopulateObject(this.permissionStore.ReadAllText(), rawPermissions); - } - - public override IEnumerable GetPermissions(SecurableUniqueKey key) { - var permissionsOfKey = (IDictionary>)null; - - rawPermissionsLock.EnterReadLock(); - try { - permissionsOfKey = this.rawPermissions.GetValueOrDefault(key.Value); - } - finally { - rawPermissionsLock.ExitReadLock(); - } - - if (permissionsOfKey == null) - return Enumerable.Empty(); - - return from rawPermission in permissionsOfKey - from userGroupKey in rawPermission.Value - select new Permission { - Action = rawPermission.Key, - Group = this.userGroupRepository.Load(userGroupKey) - }; - } - - public override void SetPermissions(SecurableUniqueKey key, IEnumerable permissions) { - rawPermissionsLock.EnterWriteLock(); - try { - var permissionsOfKey = this.rawPermissions.GetValueOrDefault(key.Value); - if (permissionsOfKey == null) { - permissionsOfKey = new Dictionary>(); - this.rawPermissions.Add(key.Value, permissionsOfKey); - } - - foreach (var permissionGroup in permissions.GroupBy(p => p.Action)) { - var permissionsOfAction = permissionsOfKey.GetValueOrDefault(permissionGroup.Key); - if (permissionsOfAction == null) { - permissionsOfAction = new List(); - permissionsOfKey.Add(permissionGroup.Key, permissionsOfAction); - } - - permissionsOfAction.AddRange( - permissionGroup.Select(p => (string)userGroupRepository.GetKey(p.Group)) - ); - } - - this.permissionStore.WriteAllText( - JsonConvert.SerializeObject(rawPermissions, Formatting.Indented) - ); - } - finally { - rawPermissionsLock.ExitWriteLock(); - } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +using Newtonsoft.Json; + +using AshMind.Extensions; + +using AshMind.Gallery.Core.IO; + +namespace AshMind.Gallery.Core.Security.Internal { + internal class JsonKeyPermissionProvider : AbstractPermissionProvider { + private readonly IFile permissionStore; + private readonly IRepository userGroupRepository; + + private readonly ReaderWriterLockSlim rawPermissionsLock = new ReaderWriterLockSlim(); + private readonly IDictionary>> rawPermissions = new Dictionary>>(); + + public JsonKeyPermissionProvider( + IFile permissionStore, + IRepository userGroupRepository + ) { + this.permissionStore = permissionStore; + this.userGroupRepository = userGroupRepository; + + if (this.permissionStore.Exists) + JsonConvert.PopulateObject(this.permissionStore.ReadAllText(), rawPermissions); + } + + public override IEnumerable GetPermissions(SecurableUniqueKey key) { + var permissionsOfKey = (IDictionary>)null; + + rawPermissionsLock.EnterReadLock(); + try { + permissionsOfKey = this.rawPermissions.GetValueOrDefault(key.Value); + } + finally { + rawPermissionsLock.ExitReadLock(); + } + + if (permissionsOfKey == null) + return Enumerable.Empty(); + + return from rawPermission in permissionsOfKey + from userGroupKey in rawPermission.Value + select new Permission { + Action = rawPermission.Key, + Group = this.userGroupRepository.Load(userGroupKey) + }; + } + + public override void SetPermissions(SecurableUniqueKey key, IEnumerable permissions) { + rawPermissionsLock.EnterWriteLock(); + try { + var permissionsOfKey = this.rawPermissions.GetValueOrDefault(key.Value); + if (permissionsOfKey == null) { + permissionsOfKey = new Dictionary>(); + this.rawPermissions.Add(key.Value, permissionsOfKey); + } + + foreach (var permissionGroup in permissions.GroupBy(p => p.Action)) { + var permissionsOfAction = permissionsOfKey.GetValueOrDefault(permissionGroup.Key); + if (permissionsOfAction == null) { + permissionsOfAction = new List(); + permissionsOfKey.Add(permissionGroup.Key, permissionsOfAction); + } + + permissionsOfAction.AddRange( + permissionGroup.Select(p => (string)userGroupRepository.GetKey(p.Group)) + ); + } + + this.permissionStore.WriteAllText( + JsonConvert.SerializeObject(rawPermissions, Formatting.Indented) + ); + } + finally { + rawPermissionsLock.ExitWriteLock(); + } + } + } +} diff --git a/Core/Security/Internal/JsonLocationPermissionProvider.cs b/Core/Security/Internal/JsonLocationPermissionProvider.cs index 2f78a3f..053ec3d 100644 --- a/Core/Security/Internal/JsonLocationPermissionProvider.cs +++ b/Core/Security/Internal/JsonLocationPermissionProvider.cs @@ -1,65 +1,57 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -using Newtonsoft.Json; - -using AshMind.Extensions; - -using AshMind.Gallery.Core.Internal; -using AshMind.Gallery.Core.IO; -using AshMind.Gallery.Core.Metadata; -using AshMind.Gallery.Core.Security; - -namespace AshMind.Gallery.Core.Security.Internal { - internal class JsonLocationPermissionProvider : AbstractPermissionProvider { - private const string PermissionsMetadataKey = "Permissions"; - - private readonly ILocationMetadataProvider metadataProvider; - private readonly IRepository userGroupRepository; - private Func getUserGroupReferenceSupport; - - public JsonLocationPermissionProvider( - ILocationMetadataProvider metadataProvider, - IRepository userGroupRepository, - Func getUserGroupReferenceSupport - ) { - this.metadataProvider = metadataProvider; - this.userGroupRepository = userGroupRepository; - this.getUserGroupReferenceSupport = getUserGroupReferenceSupport; - } - - public override IEnumerable GetPermissions(ILocation location) { - var permissionSet = this.metadataProvider.GetMetadata>>( - location, PermissionsMetadataKey - ); - if (permissionSet == null) - return Enumerable.Empty(); - - var lazyUserGroups = new Lazy>(() => this.userGroupRepository.Query().ToList()); - var referenceSupport = getUserGroupReferenceSupport(); - - return from pair in permissionSet - from key in pair.Value - let @group = referenceSupport.ResolveReference(key, lazyUserGroups.Value) - where @group != null - select new Permission { - Action = pair.Key, - Group = @group - }; - } - - public override void SetPermissions(ILocation location, IEnumerable permissions) { - var referenceSupport = getUserGroupReferenceSupport(); - var permissionSet = permissions.GroupBy(p => p.Action) - .ToDictionary( - g => g.Key, - g => g.Select(p => referenceSupport.GetReference(p.Group)).ToList() - ); - - this.metadataProvider.ApplyMetadata(location, PermissionsMetadataKey, permissionSet); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Gallery.Core.IO; +using AshMind.Gallery.Core.Metadata; + +namespace AshMind.Gallery.Core.Security.Internal { + internal class JsonLocationPermissionProvider : AbstractPermissionProvider { + private const string PermissionsMetadataKey = "Permissions"; + + private readonly ILocationMetadataProvider metadataProvider; + private readonly IRepository userGroupRepository; + private readonly Func getUserGroupReferenceSupport; + + public JsonLocationPermissionProvider( + ILocationMetadataProvider metadataProvider, + IRepository userGroupRepository, + Func getUserGroupReferenceSupport + ) { + this.metadataProvider = metadataProvider; + this.userGroupRepository = userGroupRepository; + this.getUserGroupReferenceSupport = getUserGroupReferenceSupport; + } + + public override IEnumerable GetPermissions(ILocation location) { + var permissionSet = this.metadataProvider.GetMetadata>>( + location, PermissionsMetadataKey + ); + if (permissionSet == null) + return Enumerable.Empty(); + + var lazyUserGroups = new Lazy>(() => this.userGroupRepository.Query().ToList()); + var referenceSupport = getUserGroupReferenceSupport(); + + return from pair in permissionSet + from key in pair.Value + let @group = referenceSupport.ResolveReference(key, lazyUserGroups.Value) + where @group != null + select new Permission { + Action = pair.Key, + Group = @group + }; + } + + public override void SetPermissions(ILocation location, IEnumerable permissions) { + var referenceSupport = getUserGroupReferenceSupport(); + var permissionSet = permissions.GroupBy(p => p.Action) + .ToDictionary( + g => g.Key, + g => g.Select(p => referenceSupport.GetReference(p.Group)).ToList() + ); + + this.metadataProvider.ApplyMetadata(location, PermissionsMetadataKey, permissionSet); + } + } +} diff --git a/Core/Security/Internal/JsonSecurityRepository.cs b/Core/Security/Internal/JsonSecurityRepository.cs index 20cd7ce..dc10dc1 100644 --- a/Core/Security/Internal/JsonSecurityRepository.cs +++ b/Core/Security/Internal/JsonSecurityRepository.cs @@ -1,106 +1,104 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography; -using System.Text; - -using Newtonsoft.Json; - -using AshMind.Extensions; - -using AshMind.Gallery.Core.IO; - -namespace AshMind.Gallery.Core.Security.Internal { - internal class JsonSecurityRepository : IRepository, IRepository, IRepository { - #region UserStore class - - private class UserStore { - public UserStore() { - this.Users = new List(); - this.Groups = new List(); - } - - public IList Users { get; private set; } - public IList Groups { get; private set; } - } - - #endregion - - private readonly IFile file; - private readonly UserStore store; - - public JsonSecurityRepository(IFile file) { - this.file = file; - - if (this.file.Exists) { - this.store = JsonConvert.DeserializeObject(this.file.ReadAllText()); - } - else { - this.store = new UserStore { - Groups = { new UserGroup { Name = UserGroup.SuperName } } - }; - this.file.WriteAllText(JsonConvert.SerializeObject(this.store)); - } - } - - public IQueryable Query() { - return this.store.Users.AsQueryable(); - } - - public object GetKey(User user) { - return user.Email; - } - - public User Load(object key) { - return this.store.Users.SingleOrDefault(u => u.Email == (string)key); - } - - #region IRepository Members - - IQueryable IRepository.Query() { - return this.store.Groups.AsQueryable(); - } - - object IRepository.GetKey(UserGroup entity) { - return entity.Name; - } - - UserGroup IRepository.Load(object key) { - return this.store.Groups.SingleOrDefault(g => g.Name == (string)key); - } - - #endregion - - #region IRepository Members - - IQueryable IRepository.Query() { - return Enumerable.Concat( - this.store.Users, - this.store.Groups - ).AsQueryable(); - } - - object IRepository.GetKey(IUserGroup entity) { - if (entity is User) - return "user:" + (this as IRepository).GetKey(entity as User); - - if (entity is UserGroup) - return "group:" + (this as IRepository).GetKey(entity as UserGroup); - - throw new NotSupportedException(); - } - - IUserGroup IRepository.Load(object key) { - var keyString = (string)key; - if (keyString.StartsWith("user:")) - return (this as IRepository).Load(keyString.RemoveStart("user:")); - - if (keyString.StartsWith("group:")) - return (this as IRepository).Load(keyString.RemoveStart("group:")); - - throw new NotSupportedException(); - } - - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using Newtonsoft.Json; + +using AshMind.Extensions; + +using AshMind.Gallery.Core.IO; + +namespace AshMind.Gallery.Core.Security.Internal { + internal class JsonSecurityRepository : IRepository, IRepository, IRepository { + #region UserStore class + + private class UserStore { + public UserStore() { + this.Users = new List(); + this.Groups = new List(); + } + + public IList Users { get; private set; } + public IList Groups { get; private set; } + } + + #endregion + + private readonly IFile file; + private readonly UserStore store; + + public JsonSecurityRepository(IFile file) { + this.file = file; + + if (this.file.Exists) { + this.store = JsonConvert.DeserializeObject(this.file.ReadAllText()); + } + else { + this.store = new UserStore { + Groups = { new UserGroup { Name = UserGroup.SuperName } } + }; + this.file.WriteAllText(JsonConvert.SerializeObject(this.store)); + } + } + + public IQueryable Query() { + return this.store.Users.AsQueryable(); + } + + public object GetKey(User user) { + return user.Email; + } + + public User Load(object key) { + return this.store.Users.SingleOrDefault(u => u.Email == (string)key); + } + + #region IRepository Members + + IQueryable IRepository.Query() { + return this.store.Groups.AsQueryable(); + } + + object IRepository.GetKey(UserGroup entity) { + return entity.Name; + } + + UserGroup IRepository.Load(object key) { + return this.store.Groups.SingleOrDefault(g => g.Name == (string)key); + } + + #endregion + + #region IRepository Members + + IQueryable IRepository.Query() { + return Enumerable.Concat( + this.store.Users, + this.store.Groups + ).AsQueryable(); + } + + object IRepository.GetKey(IUserGroup entity) { + if (entity is User) + return "user:" + (this as IRepository).GetKey(entity as User); + + if (entity is UserGroup) + return "group:" + (this as IRepository).GetKey(entity as UserGroup); + + throw new NotSupportedException(); + } + + IUserGroup IRepository.Load(object key) { + var keyString = (string)key; + if (keyString.StartsWith("user:")) + return (this as IRepository).Load(keyString.RemoveStart("user:")); + + if (keyString.StartsWith("group:")) + return (this as IRepository).Load(keyString.RemoveStart("group:")); + + throw new NotSupportedException(); + } + + #endregion + } +} diff --git a/Core/Security/Internal/ObsoleteJsonLocationPermissionProvider.cs b/Core/Security/Internal/ObsoleteJsonLocationPermissionProvider.cs index f83ac28..138a75a 100644 --- a/Core/Security/Internal/ObsoleteJsonLocationPermissionProvider.cs +++ b/Core/Security/Internal/ObsoleteJsonLocationPermissionProvider.cs @@ -1,87 +1,85 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography; -using System.Text; - -using Newtonsoft.Json; - -using AshMind.Extensions; - -using AshMind.Gallery.Core.Internal; -using AshMind.Gallery.Core.IO; -using AshMind.Gallery.Core.Security; -using System.IO; - -namespace AshMind.Gallery.Core.Security.Internal { - [Obsolete("Use JsonLocationPermissionProvider instead.")] - internal class ObsoleteJsonLocationPermissionProvider : AbstractPermissionProvider { - private readonly IRepository userRepository; - private readonly IRepository groupRepository; - - public ObsoleteJsonLocationPermissionProvider( - IRepository userRepository, - IRepository groupRepository - ) { - this.userRepository = userRepository; - this.groupRepository = groupRepository; - } - - public override IEnumerable GetPermissions(ILocation location) { - var securityFile = GetSecurityFile(location, true); - if (securityFile == null) - return Enumerable.Empty(); - - var json = securityFile.ReadAllText(); - var permissionSet = JsonConvert.DeserializeObject< - Dictionary> - >(json); - - var lazyUsers = new Lazy>(() => this.userRepository.Query().ToList()); - var lazyGroups = new Lazy>(() => this.groupRepository.Query().ToDictionary(g => g.Name)); - - return Using(MD5.Create(), md5 => - from pair in permissionSet - from key in pair.Value - let @group = ResolveGroup(key, md5, lazyUsers, lazyGroups) - where @group != null - select new Permission { - Action = pair.Key, - Group = @group - } - ); - } - - private IEnumerable Using(TDisposable disposable, Func> enumerateWith) - where TDisposable : IDisposable - { - using (disposable) { - foreach (var element in enumerateWith(disposable)) { - yield return element; - } - } - } - - public override void SetPermissions(ILocation location, IEnumerable permissions) { - throw new NotSupportedException(); - } - - public override bool CanSetPermissions(ILocation target) { - return false; - } - - private IUserGroup ResolveGroup(string key, MD5 md5, Lazy> lazyUsers, Lazy> lazyGroups) { - if (!key.StartsWith("u:")) - return lazyGroups.Value.GetValueOrDefault(key); - - key = key.SubstringAfter("u:"); - return lazyUsers.Value.SingleOrDefault( - u => md5.ComputeHashAsString(Encoding.UTF8.GetBytes(u.Email)) == key - ); - } - - private IFile GetSecurityFile(ILocation location, bool nullUnlessExists) { - return location.GetFile(".album.security", nullUnlessExists); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; + +using Newtonsoft.Json; + +using AshMind.Extensions; + +using AshMind.Gallery.Core.Internal; +using AshMind.Gallery.Core.IO; + +namespace AshMind.Gallery.Core.Security.Internal { + [Obsolete("Use JsonLocationPermissionProvider instead.")] + internal class ObsoleteJsonLocationPermissionProvider : AbstractPermissionProvider { + private readonly IRepository userRepository; + private readonly IRepository groupRepository; + + public ObsoleteJsonLocationPermissionProvider( + IRepository userRepository, + IRepository groupRepository + ) { + this.userRepository = userRepository; + this.groupRepository = groupRepository; + } + + public override IEnumerable GetPermissions(ILocation location) { + var securityFile = GetSecurityFile(location, true); + if (securityFile == null) + return Enumerable.Empty(); + + var json = securityFile.ReadAllText(); + var permissionSet = JsonConvert.DeserializeObject< + Dictionary> + >(json); + + var lazyUsers = new Lazy>(() => this.userRepository.Query().ToList()); + var lazyGroups = new Lazy>(() => this.groupRepository.Query().ToDictionary(g => g.Name)); + + return Using(MD5.Create(), md5 => + from pair in permissionSet + from key in pair.Value + let @group = ResolveGroup(key, md5, lazyUsers, lazyGroups) + where @group != null + select new Permission { + Action = pair.Key, + Group = @group + } + ); + } + + private IEnumerable Using(TDisposable disposable, Func> enumerateWith) + where TDisposable : IDisposable + { + using (disposable) { + foreach (var element in enumerateWith(disposable)) { + yield return element; + } + } + } + + public override void SetPermissions(ILocation location, IEnumerable permissions) { + throw new NotSupportedException(); + } + + public override bool CanSetPermissions(ILocation target) { + return false; + } + + private IUserGroup ResolveGroup(string key, MD5 md5, Lazy> lazyUsers, Lazy> lazyGroups) { + if (!key.StartsWith("u:")) + return lazyGroups.Value.GetValueOrDefault(key); + + key = key.SubstringAfter("u:"); + return lazyUsers.Value.SingleOrDefault( + u => md5.ComputeHashAsString(Encoding.UTF8.GetBytes(u.Email)) == key + ); + } + + private IFile GetSecurityFile(ILocation location, bool nullUnlessExists) { + return location.GetFile(".album.security", nullUnlessExists); + } + } +} diff --git a/Core/Security/Permission.cs b/Core/Security/Permission.cs index d9fba5e..381d0e9 100644 --- a/Core/Security/Permission.cs +++ b/Core/Security/Permission.cs @@ -1,14 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.Security { - public class Permission { - public Permission() { - } - - public IUserGroup Group { get; set; } - public SecurableAction Action { get; set; } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.Security { + public class Permission { + public IUserGroup Group { get; set; } + public SecurableAction Action { get; set; } + } +} diff --git a/Core/Security/SecurableAction.cs b/Core/Security/SecurableAction.cs index cee822b..664b4c2 100644 --- a/Core/Security/SecurableAction.cs +++ b/Core/Security/SecurableAction.cs @@ -1,11 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.Security { - public enum SecurableAction { - View, - ManageSecurity - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.Security { + public enum SecurableAction { + View, + ManageSecurity + } +} diff --git a/Core/Security/SecurableUniqueKey.cs b/Core/Security/SecurableUniqueKey.cs index 7a76f79..78380ad 100644 --- a/Core/Security/SecurableUniqueKey.cs +++ b/Core/Security/SecurableUniqueKey.cs @@ -1,14 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.Security { - public class SecurableUniqueKey { - public SecurableUniqueKey(string value) { - this.Value = value; - } - - public string Value { get; private set; } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.Security { + public class SecurableUniqueKey { + public SecurableUniqueKey(string value) { + this.Value = value; + } + + public string Value { get; private set; } + } +} diff --git a/Core/Security/User.cs b/Core/Security/User.cs index 2699ee9..98aab17 100644 --- a/Core/Security/User.cs +++ b/Core/Security/User.cs @@ -1,30 +1,29 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using AshMind.Extensions; - -namespace AshMind.Gallery.Core.Security { - public class User : IUser { - public static User System { get; private set; } - - static User() { - System = new User("system@gallery.local"); - } - - public User(string email) { - this.Email = email; - } - - public string Email { get; private set; } - - IEnumerable IUserGroup.GetUsers() { - yield return this; - } - - string IUserGroup.Name { - get { return this.Email.SubstringBefore("@"); } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Extensions; + +namespace AshMind.Gallery.Core.Security { + public class User : IUser { + public static User System { get; private set; } + + static User() { + System = new User("system@gallery.local"); + } + + public User(string email) { + this.Email = email; + } + + public string Email { get; private set; } + + IEnumerable IUserGroup.GetUsers() { + yield return this; + } + + string IUserGroup.Name { + get { return this.Email.SubstringBefore("@"); } + } + } +} diff --git a/Core/Security/UserGroup.cs b/Core/Security/UserGroup.cs index eee3e0c..4a3f0a1 100644 --- a/Core/Security/UserGroup.cs +++ b/Core/Security/UserGroup.cs @@ -1,30 +1,29 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.Security { - public class UserGroup : IUserGroup { - public static string SuperName = "*Owners"; - - public UserGroup() { - this.Users = new HashSet(); - this.Keys = new HashSet(); - } - - public string Name { get; set; } - public HashSet Users { get; private set; } - public HashSet Keys { get; private set; } - - public bool IsSuper { - get { return this.Name == SuperName; } - } - - IEnumerable IUserGroup.GetUsers() { - return Enumerable.Concat( - this.Users, - new IUser[] { new AnonymousMember(this) } - ); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.Security { + public class UserGroup : IUserGroup { + public static string SuperName = "*Owners"; + + public UserGroup() { + this.Users = new HashSet(); + this.Keys = new HashSet(); + } + + public string Name { get; set; } + public HashSet Users { get; private set; } + public HashSet Keys { get; private set; } + + public bool IsSuper { + get { return this.Name == SuperName; } + } + + IEnumerable IUserGroup.GetUsers() { + return Enumerable.Concat( + this.Users, + new IUser[] { new AnonymousMember(this) } + ); + } + } +} diff --git a/Core/Security/UserRepositoryExtensions.cs b/Core/Security/UserRepositoryExtensions.cs index 36dc246..3b3577d 100644 --- a/Core/Security/UserRepositoryExtensions.cs +++ b/Core/Security/UserRepositoryExtensions.cs @@ -1,12 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AshMind.Gallery.Core.Security { - public static class UserRepositoryExtensions { - public static User FindByEmail(this IRepository userRepository, string email) { - return userRepository.Query().SingleOrDefault(u => u.Email == email); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Core.Security { + public static class UserRepositoryExtensions { + public static User FindByEmail(this IRepository userRepository, string email) { + return userRepository.Query().SingleOrDefault(u => u.Email == email); + } + } +} diff --git a/Site.Tests/Of.Controllers/GalleryControllerTest.cs b/Site.Tests/Of.Controllers/GalleryControllerTest.cs index b8af3a8..66eda67 100644 --- a/Site.Tests/Of.Controllers/GalleryControllerTest.cs +++ b/Site.Tests/Of.Controllers/GalleryControllerTest.cs @@ -1,65 +1,64 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Web.Mvc; - -using Moq; - -using MbUnit.Framework; - -using AshMind.Gallery.Core; -using AshMind.Gallery.Core.AlbumSupport; -using AshMind.Gallery.Core.Security; -using AshMind.Gallery.Core.Commenting; -using AshMind.Gallery.Site.Controllers; -using AshMind.Gallery.Site.Logic; -using AshMind.Gallery.Site.Models; - -namespace AshMind.Gallery.Site.Tests.Of.Controllers { - [TestFixture] - public class GalleryControllerTest { - [Test] - public void TestStandardAlbumNamesReturnsAlbumsWithoutGaps() { - var now = DateTimeOffset.Now; - var albums = Enumerable.Range(0, 40).Select( - i => MakeAlbum("Album_" + (i + 1), now.AddDays(-i)) - ).ToArray(); - var facadeMock = new Mock(); - facadeMock.Setup(x => x.GetAlbums(AlbumProviderKeys.Default, It.IsAny())) - .Returns(albums); - - var controller = this.CreateController(facadeMock.Object); - - var loadedNames = Enumerable.Concat( - GetModel(controller.StandardAlbumNames(0, 20)).StandardAlbums.List, - GetModel(controller.StandardAlbumNames(21, 40)).StandardAlbums.List - ).Select(a => a.Album.Name); - - Assert.AreElementsEqual(albums.Select(a => a.Name), loadedNames); - } - - private static Album MakeAlbum(string name, DateTimeOffset date) { - return new Album( - new AlbumDescriptor(AlbumProviderKeys.Default, ""), - name, - new[] { new AlbumItem(null, "", AlbumItemType.Image, date, () => new Comment[0]) }, - new object() - ); - } - - private T GetModel(ActionResult result) { - return (T)((ViewResultBase)result).ViewData.Model; - } - - private GalleryController CreateController(IAlbumFacade facade = null) { - return new GalleryController( - facade ?? new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object - ); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web.Mvc; + +using Moq; + +using MbUnit.Framework; + +using AshMind.Gallery.Core; +using AshMind.Gallery.Core.AlbumSupport; +using AshMind.Gallery.Core.Security; +using AshMind.Gallery.Core.Commenting; +using AshMind.Gallery.Site.Controllers; +using AshMind.Gallery.Site.Logic; +using AshMind.Gallery.Site.Models; + +namespace AshMind.Gallery.Site.Tests.Of.Controllers { + [TestFixture] + public class GalleryControllerTest { + [Test] + public void TestStandardAlbumNamesReturnsAlbumsWithoutGaps() { + var now = DateTimeOffset.Now; + var albums = Enumerable.Range(0, 40).Select( + i => MakeAlbum("Album_" + (i + 1), now.AddDays(-i)) + ).ToArray(); + var facadeMock = new Mock(); + facadeMock.Setup(x => x.GetAlbums(AlbumProviderKeys.Default, It.IsAny())) + .Returns(albums); + + var controller = this.CreateController(facadeMock.Object); + + var loadedNames = Enumerable.Concat( + GetModel(controller.StandardAlbumNames(0, 20)).StandardAlbums.List, + GetModel(controller.StandardAlbumNames(21, 40)).StandardAlbums.List + ).Select(a => a.Album.Name); + + Assert.AreElementsEqual(albums.Select(a => a.Name), loadedNames); + } + + private static Album MakeAlbum(string name, DateTimeOffset date) { + return new Album( + new AlbumDescriptor(AlbumProviderKeys.Default, ""), + name, + new[] { new AlbumItem(null, "", AlbumItemType.Image, date, () => new Comment[0]) }, + new object() + ); + } + + private T GetModel(ActionResult result) { + return (T)((ViewResultBase)result).ViewData.Model; + } + + private GalleryController CreateController(IAlbumFacade facade = null) { + return new GalleryController( + facade ?? new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object + ); + } + } +} diff --git a/Site.Tests/Of.Logic/ImageRequest/CookielessImageRequestStrategyTest.cs b/Site.Tests/Of.Logic/ImageRequest/CookielessImageRequestStrategyTest.cs index 48e3816..2c4b870 100644 --- a/Site.Tests/Of.Logic/ImageRequest/CookielessImageRequestStrategyTest.cs +++ b/Site.Tests/Of.Logic/ImageRequest/CookielessImageRequestStrategyTest.cs @@ -1,47 +1,43 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Web; -using System.Web.Routing; - -using Gallio.Framework; - -using MbUnit.Framework; -using MbUnit.Framework.ContractVerifiers; - -using Moq; - -using AshMind.Gallery.Site.Logic.ImageRequest; - -namespace AshMind.Gallery.Site.Tests.Of.Logic.ImageRequest { - [TestFixture] - public class CookielessImageRequestStrategyTest { - [Test] - public void TestUrlIsConsistent() { - var strategy = new CookielessImageRequestStrategy(null); - - var context = new RequestContext { - RouteData = new RouteData(), - HttpContext = MockHttpContextForUrlHelper() - }; - - strategy.MapRoute(RouteTable.Routes, "Image", "Get"); - - var url1 = strategy.GetActionUrl(context, "TestAlbum", "TestItem"); - var url2 = strategy.GetActionUrl(context, "TestAlbum", "TestItem"); - - Assert.AreEqual(url1, url2); - } - - private HttpContextBase MockHttpContextForUrlHelper() { - var httpContextMock = new Mock(); - - httpContextMock.Setup(m => m.Request.ApplicationPath) - .Returns("test"); - httpContextMock.Setup(m => m.Response.ApplyAppPathModifier(It.IsAny())) - .Returns((string value) => value); - - return httpContextMock.Object; - } - } -} +using System; +using System.Collections.Generic; +using System.Web; +using System.Web.Routing; + +using MbUnit.Framework; + +using Moq; + +using AshMind.Gallery.Site.Logic.ImageRequest; + +namespace AshMind.Gallery.Site.Tests.Of.Logic.ImageRequest { + [TestFixture] + public class CookielessImageRequestStrategyTest { + [Test] + public void TestUrlIsConsistent() { + var strategy = new CookielessImageRequestStrategy(null); + + var context = new RequestContext { + RouteData = new RouteData(), + HttpContext = MockHttpContextForUrlHelper() + }; + + strategy.MapRoute(RouteTable.Routes, "Image", "Get"); + + var url1 = strategy.GetActionUrl(context, "TestAlbum", "TestItem"); + var url2 = strategy.GetActionUrl(context, "TestAlbum", "TestItem"); + + Assert.AreEqual(url1, url2); + } + + private HttpContextBase MockHttpContextForUrlHelper() { + var httpContextMock = new Mock(); + + httpContextMock.Setup(m => m.Request.ApplicationPath) + .Returns("test"); + httpContextMock.Setup(m => m.Response.ApplyAppPathModifier(It.IsAny())) + .Returns((string value) => value); + + return httpContextMock.Object; + } + } +} diff --git a/Site.Tests/Properties/AssemblyInfo.cs b/Site.Tests/Properties/AssemblyInfo.cs index 0ab3cbe..98f84ce 100644 --- a/Site.Tests/Properties/AssemblyInfo.cs +++ b/Site.Tests/Properties/AssemblyInfo.cs @@ -1,35 +1,28 @@ -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("Site.Tests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("Site.Tests")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2010")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// 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("9d5f7d78-d9ec-44ec-a99c-b36e9ee4a34c")] - -// 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("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +using System.Reflection; +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("AshMind.Gallery.Site.Tests")] +[assembly: AssemblyProduct("AshMind.Gallery.Site.Tests")] + +// 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("9d5f7d78-d9ec-44ec-a99c-b36e9ee4a34c")] + +// 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("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Site.Tests/Site.Tests.csproj b/Site.Tests/Site.Tests.csproj index c0993e6..5bbbf11 100644 --- a/Site.Tests/Site.Tests.csproj +++ b/Site.Tests/Site.Tests.csproj @@ -76,6 +76,9 @@ + + Properties\AssemblyInfoCommon.cs + diff --git a/Site/Controllers/AccessController.cs b/Site/Controllers/AccessController.cs index acf0bad..f9faf78 100644 --- a/Site/Controllers/AccessController.cs +++ b/Site/Controllers/AccessController.cs @@ -1,150 +1,149 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Net; -using System.Threading; -using System.Web.Mvc; -using System.Web.Security; - -using DotNetOpenAuth.Messaging; -using DotNetOpenAuth.OpenId; -using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; -using DotNetOpenAuth.OpenId.RelyingParty; - -using AshMind.Extensions; - -using AshMind.Gallery.Core; -using AshMind.Gallery.Core.Security; -using AshMind.Gallery.Site.Logic; -using AshMind.Gallery.Site.Models; -using AshMind.Gallery.Site.OpenIdAbstraction; - -namespace AshMind.Gallery.Site.Controllers { - [HandleError] - public class AccessController : ControllerBase { - private readonly IOpenIdAjaxRelyingParty openId; - private readonly IUserAuthentication authentication; - private readonly IRepository userGroupRepository; - private readonly IAuthorizationService authorization; - private readonly IAlbumFacade gallery; - - public AccessController( - IOpenIdAjaxRelyingParty openId, - IUserAuthentication authentication, - IRepository userGroupRepository, - IAuthorizationService authorization, - IAlbumFacade gallery - ) : base(authentication) { - this.openId = openId; - this.authentication = authentication; - this.userGroupRepository = userGroupRepository; - this.authorization = authorization; - this.gallery = gallery; - } - - public ActionResult Login(string error, string returnUrl, string key) { - if (this.authentication.AuthenticateByKey(key)) - return Redirect(returnUrl); - - Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-us"); - var returnToUrl = MakeAbsolute(Url.Action("OpenIdLoginReturnTo")); - var requests = new[] { "https://www.google.com/accounts/o8/id" }.SelectMany( - identifier => openId.CreateRequests(identifier, Realm.AutoDetect, returnToUrl) - ).ToArray(); - requests.ForEach(r => r.AddExtension(new ClaimsRequest { - Email = DemandLevel.Require - })); - var ajax = this.openId.AsAjaxPreloadedDiscoveryResult(requests); - - if (error.IsNotNullOrEmpty()) - ModelState.AddModelError("Login", error); - - return View(new LoginViewModel { PreloadedDiscoveryResults = ajax }); - } - - [AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post), ValidateInput(false)] - public ActionResult OpenIdLoginReturnTo() { - return this.openId.ProcessResponseFromPopup().AsActionResult(); - } - - [AcceptVerbs(HttpVerbs.Post)] - [ValidateInput(false)] - public ActionResult OpenIdLoginPostAssertion(string openid_openidAuthData, string returnUrl) { - IAuthenticationResponse response; - if (!string.IsNullOrEmpty(openid_openidAuthData)) { - var auth = new Uri(openid_openidAuthData); - var headers = new WebHeaderCollection(); - foreach (string header in Request.Headers) { - headers[header] = Request.Headers[header]; - } - - // Always say it's a GET since the payload is all in the URL, even the large ones. - var clientResponseInfo = new HttpRequestInfo("GET", auth, auth.PathAndQuery, headers, null); - response = this.openId.GetResponse(clientResponseInfo); - } - else { - response = this.openId.GetResponse(); - } - - if (response == null) - return RedirectToAction("Login"); - - if (response.Status == AuthenticationStatus.Authenticated) { - var claims = response.GetExtension(); - if (claims == null) { - return RedirectToAction("Login", new { error = "Email not received." }); - } - - var authenticated = this.authentication.AuthenticateByEmail(claims.Email); - if (!authenticated) - return RedirectToAction("Login", new { error = "Could not authenticate this email." }); - - return Redirect(returnUrl); - } - - if (response.Status == AuthenticationStatus.Failed) { - return RedirectToAction("Login", new { error = response.Exception.Message }); - } - - return RedirectToAction("Login"); - } - - [HttpGet] - public ActionResult Grant(string albumID) { - if (!Request.IsAjaxRequest()) - throw new NotImplementedException(); - - var album = this.gallery.GetAlbum(albumID, this.User); - return PartialView("GrantForm", new GrantViewModel( - albumID, - this.authorization.GetAuthorizedTo(SecurableAction.View, album.SecurableToken).ToSet(), - this.GetAllGroups().ToList() - )); - } - - [HttpPost] - public ActionResult Grant(string albumID, HashSet groupKeys) { - if (!Request.IsAjaxRequest()) - throw new NotImplementedException(); - - var album = this.gallery.GetAlbum(albumID, this.User); - var groups = this.GetAllGroups() - .Where(g => groupKeys.Contains(g.Key)) - .Select(g => g.UserGroup); - - this.authorization.AuthorizeTo(SecurableAction.View, album.SecurableToken, groups); - return new EmptyResult(); - } - - private IEnumerable GetAllGroups() { - return this.userGroupRepository.Query().AsEnumerable().Select(g => new UserGroupViewModel(g)); - } - - private Uri MakeAbsolute(string uri) { - return new Uri(Request.Url, uri); - } - } -} - - +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Threading; +using System.Web.Mvc; + +using DotNetOpenAuth.Messaging; +using DotNetOpenAuth.OpenId; +using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; +using DotNetOpenAuth.OpenId.RelyingParty; + +using AshMind.Extensions; + +using AshMind.Gallery.Core; +using AshMind.Gallery.Core.Security; +using AshMind.Gallery.Site.Logic; +using AshMind.Gallery.Site.Models; +using AshMind.Gallery.Site.OpenIdAbstraction; + +namespace AshMind.Gallery.Site.Controllers { + [HandleError] + public class AccessController : ControllerBase { + private readonly IOpenIdAjaxRelyingParty openId; + private readonly IUserAuthentication authentication; + private readonly IRepository userGroupRepository; + private readonly IAuthorizationService authorization; + private readonly IAlbumFacade gallery; + + public AccessController( + IOpenIdAjaxRelyingParty openId, + IUserAuthentication authentication, + IRepository userGroupRepository, + IAuthorizationService authorization, + IAlbumFacade gallery + ) : base(authentication) { + this.openId = openId; + this.authentication = authentication; + this.userGroupRepository = userGroupRepository; + this.authorization = authorization; + this.gallery = gallery; + } + + public ActionResult Login(string error, string returnUrl, string key) { + if (this.authentication.AuthenticateByKey(key)) + return Redirect(returnUrl); + + Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-us"); + var returnToUrl = MakeAbsolute(Url.Action("OpenIdLoginReturnTo")); + var requests = new[] { "https://www.google.com/accounts/o8/id" }.SelectMany( + identifier => openId.CreateRequests(identifier, Realm.AutoDetect, returnToUrl) + ).ToArray(); + requests.ForEach(r => r.AddExtension(new ClaimsRequest { + Email = DemandLevel.Require + })); + var ajax = this.openId.AsAjaxPreloadedDiscoveryResult(requests); + + if (error.IsNotNullOrEmpty()) + ModelState.AddModelError("Login", error); + + return View(new LoginViewModel { PreloadedDiscoveryResults = ajax }); + } + + [AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post), ValidateInput(false)] + public ActionResult OpenIdLoginReturnTo() { + return this.openId.ProcessResponseFromPopup().AsActionResult(); + } + + [AcceptVerbs(HttpVerbs.Post)] + [ValidateInput(false)] + public ActionResult OpenIdLoginPostAssertion(string openid_openidAuthData, string returnUrl) { + IAuthenticationResponse response; + if (!string.IsNullOrEmpty(openid_openidAuthData)) { + var auth = new Uri(openid_openidAuthData); + var headers = new WebHeaderCollection(); + foreach (string header in Request.Headers) { + headers[header] = Request.Headers[header]; + } + + // Always say it's a GET since the payload is all in the URL, even the large ones. + var clientResponseInfo = new HttpRequestInfo("GET", auth, auth.PathAndQuery, headers, null); + response = this.openId.GetResponse(clientResponseInfo); + } + else { + response = this.openId.GetResponse(); + } + + if (response == null) + return RedirectToAction("Login"); + + if (response.Status == AuthenticationStatus.Authenticated) { + var claims = response.GetExtension(); + if (claims == null) { + return RedirectToAction("Login", new { error = "Email not received." }); + } + + var authenticated = this.authentication.AuthenticateByEmail(claims.Email); + if (!authenticated) + return RedirectToAction("Login", new { error = "Could not authenticate this email." }); + + return Redirect(returnUrl); + } + + if (response.Status == AuthenticationStatus.Failed) { + return RedirectToAction("Login", new { error = response.Exception.Message }); + } + + return RedirectToAction("Login"); + } + + [HttpGet] + public ActionResult Grant(string albumID) { + if (!Request.IsAjaxRequest()) + throw new NotImplementedException(); + + var album = this.gallery.GetAlbum(albumID, this.User); + return PartialView("GrantForm", new GrantViewModel( + albumID, + this.authorization.GetAuthorizedTo(SecurableAction.View, album.SecurableToken).ToSet(), + this.GetAllGroups().ToList() + )); + } + + [HttpPost] + public ActionResult Grant(string albumID, HashSet groupKeys) { + if (!Request.IsAjaxRequest()) + throw new NotImplementedException(); + + var album = this.gallery.GetAlbum(albumID, this.User); + var groups = this.GetAllGroups() + .Where(g => groupKeys.Contains(g.Key)) + .Select(g => g.UserGroup); + + this.authorization.AuthorizeTo(SecurableAction.View, album.SecurableToken, groups); + return new EmptyResult(); + } + + private IEnumerable GetAllGroups() { + return this.userGroupRepository.Query().AsEnumerable().Select(g => new UserGroupViewModel(g)); + } + + private Uri MakeAbsolute(string uri) { + return new Uri(Request.Url, uri); + } + } +} + + diff --git a/Site/Controllers/ControllerBase.cs b/Site/Controllers/ControllerBase.cs index f051787..5abef8a 100644 --- a/Site/Controllers/ControllerBase.cs +++ b/Site/Controllers/ControllerBase.cs @@ -1,39 +1,35 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.Mvc; - -using AshMind.Extensions; - -using AshMind.Gallery.Core.Security; -using AshMind.Gallery.Core; -using AshMind.Gallery.Site.Logic; - -namespace AshMind.Gallery.Site.Controllers { - public abstract class ControllerBase : Controller { - private IUser user; - private readonly IUserAuthentication authentication; - - public ControllerBase(IUserAuthentication authentication) { - this.authentication = authentication; - } - - public new IUser User { - get { - if (this.user == null) - this.user = this.authentication.GetUser(base.User); - - return this.user; - } - } - - public HttpUnauthorizedResult Unauthorized() { - return new HttpUnauthorizedResult(); - } - - public EmptyResult Empty() { - return new EmptyResult(); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web.Mvc; + +using AshMind.Gallery.Core.Security; +using AshMind.Gallery.Site.Logic; + +namespace AshMind.Gallery.Site.Controllers { + public abstract class ControllerBase : Controller { + private IUser user; + private readonly IUserAuthentication authentication; + + protected ControllerBase(IUserAuthentication authentication) { + this.authentication = authentication; + } + + public new IUser User { + get { + if (this.user == null) + this.user = this.authentication.GetUser(base.User); + + return this.user; + } + } + + public HttpUnauthorizedResult Unauthorized() { + return new HttpUnauthorizedResult(); + } + + public EmptyResult Empty() { + return new EmptyResult(); + } + } +} diff --git a/Site/Controllers/GalleryController.cs b/Site/Controllers/GalleryController.cs index 3430979..9162ffb 100644 --- a/Site/Controllers/GalleryController.cs +++ b/Site/Controllers/GalleryController.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Net.Mime; -using System.Web; using System.Web.Mvc; using AshMind.Gallery.Core; @@ -148,7 +147,6 @@ IImageRequestStrategy requestStrategy if (album == null) return null; - var id = this.gallery.GetAlbumID(album); if (!manageSecurity || !authorization.IsAuthorized(this.User, SecurableAction.ManageSecurity, null)) return new AlbumViewModel(album, this.gallery.GetAlbumID, this.User, this.requestStrategy, false, null); diff --git a/Site/Controllers/ImageController.cs b/Site/Controllers/ImageController.cs index bd118b7..02e792e 100644 --- a/Site/Controllers/ImageController.cs +++ b/Site/Controllers/ImageController.cs @@ -1,95 +1,86 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Web; -using System.Web.Mvc; -using System.Net.Mime; - -using AshMind.Extensions; - -using AshMind.Gallery.Core; -using AshMind.Gallery.Core.IO; -using AshMind.Gallery.Core.Security; -using AshMind.Gallery.Site.Logic; -using AshMind.Gallery.Site.Models; - -namespace AshMind.Gallery.Site.Controllers { - public class ImageController : ControllerBase { - private IDictionary knownMimeTypes = new Dictionary { - {".jpg", MediaTypeNames.Image.Jpeg}, - {".jpeg", MediaTypeNames.Image.Jpeg}, - {".png", "image/png"} - }; - - private readonly IAlbumFacade gallery; - private readonly PreviewFacade preview; - private readonly IImageRequestStrategy strategy; - - public ImageController( - IAlbumFacade gallery, - PreviewFacade preview, - IUserAuthentication authentication, - IImageRequestStrategy strategy - ) - : base(authentication) - { - this.gallery = gallery; - this.preview = preview; - this.strategy = strategy; - } - - //[OutputCache(Duration = 60, VaryByParam = "*")] - public ActionResult Get(string size) { - if (!this.strategy.IsAuthorized(ControllerContext.RequestContext)) - return Unauthorized(); - - var imageSize = ImageSize.Parse(size); - - var file = this.strategy.GetImageFile(ControllerContext.RequestContext); - var fileName = file.Name; - if (imageSize != ImageSize.Original) { - file = this.preview.GetPreview(file, imageSize.Size); - fileName = string.Format( - "{0}_{1}px.{2}", - fileName.SubstringBefore("."), - imageSize.Size, - fileName.SubstringAfter(".") - ); - } - - var ifModifiedSince = Request.Headers["If-Modified-Since"]; - var lastModifiedDate = file.GetLastWriteTime(); - if (!string.IsNullOrEmpty(ifModifiedSince)) { - var ifModifiedSinceDate = DateTimeOffset.Parse(ifModifiedSince); - - if ((lastModifiedDate - ifModifiedSinceDate).TotalSeconds < 1) { - Response.StatusCode = 304; - return new EmptyResult(); - } - } - - Response.AddFileDependency(file.Path); - Response.Cache.SetCacheability(HttpCacheability.Private); - Response.Cache.SetETagFromFileDependencies(); - Response.Cache.SetLastModifiedFromFileDependencies(); - - var maxAge = TimeSpan.Zero; - if ((DateTimeOffset.Now - lastModifiedDate).TotalDays > 30) { // I think I will not edit these - maxAge = TimeSpan.FromDays(90); - } - else { - maxAge = TimeSpan.FromHours(1); - } - - Response.Cache.SetExpires((DateTimeOffset.Now + maxAge).UtcDateTime); - Response.Cache.SetMaxAge(maxAge); - - return File( - file.Path, - knownMimeTypes[Path.GetExtension(file.Name).ToLower()], - fileName - ); - } - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Web; +using System.Web.Mvc; +using System.Net.Mime; + +using AshMind.Extensions; + +using AshMind.Gallery.Core; +using AshMind.Gallery.Site.Logic; +using AshMind.Gallery.Site.Models; + +namespace AshMind.Gallery.Site.Controllers { + public class ImageController : ControllerBase { + private readonly IDictionary knownMimeTypes = new Dictionary { + {".jpg", MediaTypeNames.Image.Jpeg}, + {".jpeg", MediaTypeNames.Image.Jpeg}, + {".png", "image/png"} + }; + + private readonly PreviewFacade preview; + private readonly IImageRequestStrategy strategy; + + public ImageController( + PreviewFacade preview, + IUserAuthentication authentication, + IImageRequestStrategy strategy + ) + : base(authentication) + { + this.preview = preview; + this.strategy = strategy; + } + + //[OutputCache(Duration = 60, VaryByParam = "*")] + public ActionResult Get(string size) { + if (!this.strategy.IsAuthorized(ControllerContext.RequestContext)) + return Unauthorized(); + + var imageSize = ImageSize.Parse(size); + + var file = this.strategy.GetImageFile(ControllerContext.RequestContext); + var fileName = file.Name; + if (imageSize != ImageSize.Original) { + file = this.preview.GetPreview(file, imageSize.Size); + fileName = string.Format( + "{0}_{1}px.{2}", + fileName.SubstringBefore("."), + imageSize.Size, + fileName.SubstringAfter(".") + ); + } + + var ifModifiedSince = Request.Headers["If-Modified-Since"]; + var lastModifiedDate = file.GetLastWriteTime(); + if (!string.IsNullOrEmpty(ifModifiedSince)) { + var ifModifiedSinceDate = DateTimeOffset.Parse(ifModifiedSince); + + if ((lastModifiedDate - ifModifiedSinceDate).TotalSeconds < 1) { + Response.StatusCode = 304; + return new EmptyResult(); + } + } + + Response.AddFileDependency(file.Path); + Response.Cache.SetCacheability(HttpCacheability.Private); + Response.Cache.SetETagFromFileDependencies(); + Response.Cache.SetLastModifiedFromFileDependencies(); + + var maxAge = (DateTimeOffset.Now - lastModifiedDate).TotalDays > 30 // I think I will not edit these + ? TimeSpan.FromDays(90) + : TimeSpan.FromHours(1); + + Response.Cache.SetExpires((DateTimeOffset.Now + maxAge).UtcDateTime); + Response.Cache.SetMaxAge(maxAge); + + return File( + file.Path, + knownMimeTypes[Path.GetExtension(file.Name).ToLower()], + fileName + ); + } + } +} diff --git a/Site/Fixes/IVType.cs b/Site/Fixes/IVType.cs index f91a01b..5d16229 100644 --- a/Site/Fixes/IVType.cs +++ b/Site/Fixes/IVType.cs @@ -1,12 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; - -namespace AshMind.Gallery.Site.Fixes { - public enum IVType { - None, - Random, - Hash - } +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Site.Fixes { + public enum IVType { + None, + Random, + Hash + } } \ No newline at end of file diff --git a/Site/Fixes/MachineKeySectionMethods.cs b/Site/Fixes/MachineKeySectionMethods.cs index d0e256c..a09ae69 100644 --- a/Site/Fixes/MachineKeySectionMethods.cs +++ b/Site/Fixes/MachineKeySectionMethods.cs @@ -1,33 +1,32 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Web; -using System.Web.Configuration; - -using AshMind.Extensions; - -// This is a hack -- this should be done using MachineKey. -// But I need IVType.Hash to get consistent URLs from encrypted data. -// No idea if this is insecure (it probably is, I should improve it later). - -// Pull requests will be very appreciated. - -namespace AshMind.Gallery.Site.Fixes { - using EncryptOrDecryptDataFunc = Func; - - public static class MachineKeySectionMethods { - private static readonly EncryptOrDecryptDataFunc EncryptOrDecryptDataFunc - = (EncryptOrDecryptDataFunc)Delegate.CreateDelegate( - typeof(EncryptOrDecryptDataFunc), - typeof(MachineKeySection).GetMethods(BindingFlags.Static | BindingFlags.NonPublic) - .Where(m => m.Name == "EncryptOrDecryptData") - .HavingMax(m => m.GetParameters().Length) - .Single() - ); - - public static byte[] EncryptOrDecryptData(bool fEncrypt, byte[] buf, byte[] modifier, int start, int length, bool useValidationSymAlgo, bool useLegacyMode, IVType ivType, bool signData) { - return EncryptOrDecryptDataFunc(fEncrypt, buf, modifier, start, length, useValidationSymAlgo, useLegacyMode, ivType, signData); - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Web.Configuration; + +using AshMind.Extensions; + +// This is a hack -- this should be done using MachineKey. +// But I need IVType.Hash to get consistent URLs from encrypted data. +// No idea if this is insecure (it probably is, I should improve it later). + +// Pull requests will be very appreciated. + +namespace AshMind.Gallery.Site.Fixes { + using EncryptOrDecryptDataFunc = Func; + + public static class MachineKeySectionMethods { + private static readonly EncryptOrDecryptDataFunc EncryptOrDecryptDataFunc + = (EncryptOrDecryptDataFunc)Delegate.CreateDelegate( + typeof(EncryptOrDecryptDataFunc), + typeof(MachineKeySection).GetMethods(BindingFlags.Static | BindingFlags.NonPublic) + .Where(m => m.Name == "EncryptOrDecryptData") + .HavingMax(m => m.GetParameters().Length) + .Single() + ); + + public static byte[] EncryptOrDecryptData(bool fEncrypt, byte[] buf, byte[] modifier, int start, int length, bool useValidationSymAlgo, bool useLegacyMode, IVType ivType, bool signData) { + return EncryptOrDecryptDataFunc(fEncrypt, buf, modifier, start, length, useValidationSymAlgo, useLegacyMode, ivType, signData); + } + } } \ No newline at end of file diff --git a/Site/Fixes/WebCache.cs b/Site/Fixes/WebCache.cs index 0117995..1b7fb81 100644 --- a/Site/Fixes/WebCache.cs +++ b/Site/Fixes/WebCache.cs @@ -1,141 +1,141 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Runtime.Caching; -using System.Web.Caching; - -using Web = System.Web; -using Runtime = System.Runtime; - -namespace AshMind.Gallery.Site.Fixes { - // superhacky fix for the MemoryCache not being able to define file dependencies due to - // https://connect.microsoft.com/VisualStudio/feedback/details/565313/breaking-problem-with-datetimeoffset-that-affects-multiple-framework-classes - public class WebCache : ObjectCache { - private readonly IDictionary reasonMap = new Dictionary { - { CacheItemRemovedReason.DependencyChanged, CacheEntryRemovedReason.ChangeMonitorChanged }, - { CacheItemRemovedReason.Expired, CacheEntryRemovedReason.Expired }, - { CacheItemRemovedReason.Removed, CacheEntryRemovedReason.Removed }, - { CacheItemRemovedReason.Underused, CacheEntryRemovedReason.Evicted } - }; - - public override object AddOrGetExisting(string key, object value, CacheItemPolicy policy, string regionName = null) { - return this.AddOrInsert( - HttpContext.Current.Cache.Add, - key, value, policy, regionName - ); - } - - public override CacheItem AddOrGetExisting(CacheItem value, CacheItemPolicy policy) { - var existing = this.AddOrGetExisting(value.Key, value.Value, policy, value.RegionName); - return existing != null ? new CacheItem(value.Key, existing, value.RegionName) : null; - } - - public override object AddOrGetExisting(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null) { - return this.AddOrGetExisting(key, value, new CacheItemPolicy { AbsoluteExpiration = absoluteExpiration }, regionName); - } - - public override bool Contains(string key, string regionName = null) { - throw new NotSupportedException(); - } - - public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(IEnumerable keys, string regionName = null) { - throw new NotImplementedException(); - } - - public override object Get(string key, string regionName = null) { - return HttpContext.Current.Cache.Get(key); - } - - public override CacheItem GetCacheItem(string key, string regionName = null) { - return new CacheItem(key, this.Get(key, regionName), regionName); - } - - public override long GetCount(string regionName = null) { - return HttpContext.Current.Cache.Count; - } - - protected override IEnumerator> GetEnumerator() { - throw new NotImplementedException(); - } - - public override IDictionary GetValues(IEnumerable keys, string regionName = null) { - throw new NotImplementedException(); - } - - public override object Remove(string key, string regionName = null) { - return HttpContext.Current.Cache.Remove(key); - } - - public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null) { - this.AddOrInsert( - (k, v, dependencies, absoluteExpiration, slidingExpiration, priority, onRemoveCallback) => { - HttpContext.Current.Cache.Insert(k, v, dependencies, absoluteExpiration, slidingExpiration, priority, onRemoveCallback); - return null; - }, - key, value, policy, regionName - ); - } - - public override void Set(CacheItem item, CacheItemPolicy policy) { - this.Set(item.Key, item.Value, policy, item.RegionName); - } - - public override void Set(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null) { - this.Set(key, value, new CacheItemPolicy { AbsoluteExpiration = absoluteExpiration }, regionName); - } - - private object AddOrInsert( - Func action, - string key, object value, CacheItemPolicy policy, string regionName = null - ) { - var fileNames = new List(); - foreach (var monitor in policy.ChangeMonitors) { - var fileMonitor = monitor as FileChangeMonitor; - if (fileMonitor == null) - throw new NotImplementedException(); - - fileNames.AddRange(fileMonitor.FilePaths); - } - - var dependency = fileNames.Count > 0 - ? new CacheDependency(fileNames.ToArray()) - : null; - - var callback = (CacheItemRemovedCallback)null; - if (policy.RemovedCallback != null) { - callback = (removedKey, removedValue, reason) => policy.RemovedCallback( - new CacheEntryRemovedArguments(this, reasonMap[reason], new CacheItem(removedKey, removedValue)) - ); - } - - return action( - key, value, dependency, - policy.AbsoluteExpiration != ObjectCache.InfiniteAbsoluteExpiration ? policy.AbsoluteExpiration.UtcDateTime : Cache.NoAbsoluteExpiration, - policy.SlidingExpiration != ObjectCache.NoSlidingExpiration ? policy.SlidingExpiration : Cache.NoSlidingExpiration, - policy.Priority == Runtime.Caching.CacheItemPriority.NotRemovable - ? Web.Caching.CacheItemPriority.NotRemovable - : Web.Caching.CacheItemPriority.Default, - callback - ); - } - - public override object this[string key] { - get { return this.Get(key); } - set { this.Set(key, value, ObjectCache.InfiniteAbsoluteExpiration, null); } - } - - public override string Name { - get { return "Default"; } - } - - public override DefaultCacheCapabilities DefaultCacheCapabilities { - get { - return DefaultCacheCapabilities.AbsoluteExpirations - | DefaultCacheCapabilities.SlidingExpirations - | DefaultCacheCapabilities.CacheEntryChangeMonitors - | DefaultCacheCapabilities.InMemoryProvider; - } - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Runtime.Caching; +using System.Web.Caching; + +using Web = System.Web; +using Runtime = System.Runtime; + +namespace AshMind.Gallery.Site.Fixes { + // superhacky fix for the MemoryCache not being able to define file dependencies due to + // https://connect.microsoft.com/VisualStudio/feedback/details/565313/breaking-problem-with-datetimeoffset-that-affects-multiple-framework-classes + public class WebCache : ObjectCache { + private readonly IDictionary reasonMap = new Dictionary { + { CacheItemRemovedReason.DependencyChanged, CacheEntryRemovedReason.ChangeMonitorChanged }, + { CacheItemRemovedReason.Expired, CacheEntryRemovedReason.Expired }, + { CacheItemRemovedReason.Removed, CacheEntryRemovedReason.Removed }, + { CacheItemRemovedReason.Underused, CacheEntryRemovedReason.Evicted } + }; + + public override object AddOrGetExisting(string key, object value, CacheItemPolicy policy, string regionName = null) { + return this.AddOrInsert( + HttpContext.Current.Cache.Add, + key, value, policy, regionName + ); + } + + public override CacheItem AddOrGetExisting(CacheItem value, CacheItemPolicy policy) { + var existing = this.AddOrGetExisting(value.Key, value.Value, policy, value.RegionName); + return existing != null ? new CacheItem(value.Key, existing, value.RegionName) : null; + } + + public override object AddOrGetExisting(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null) { + return this.AddOrGetExisting(key, value, new CacheItemPolicy { AbsoluteExpiration = absoluteExpiration }, regionName); + } + + public override bool Contains(string key, string regionName = null) { + throw new NotSupportedException(); + } + + public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(IEnumerable keys, string regionName = null) { + throw new NotImplementedException(); + } + + public override object Get(string key, string regionName = null) { + return HttpContext.Current.Cache.Get(key); + } + + public override CacheItem GetCacheItem(string key, string regionName = null) { + return new CacheItem(key, this.Get(key, regionName), regionName); + } + + public override long GetCount(string regionName = null) { + return HttpContext.Current.Cache.Count; + } + + protected override IEnumerator> GetEnumerator() { + throw new NotImplementedException(); + } + + public override IDictionary GetValues(IEnumerable keys, string regionName = null) { + throw new NotImplementedException(); + } + + public override object Remove(string key, string regionName = null) { + return HttpContext.Current.Cache.Remove(key); + } + + public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null) { + this.AddOrInsert( + (k, v, dependencies, absoluteExpiration, slidingExpiration, priority, onRemoveCallback) => { + HttpContext.Current.Cache.Insert(k, v, dependencies, absoluteExpiration, slidingExpiration, priority, onRemoveCallback); + return null; + }, + key, value, policy, regionName + ); + } + + public override void Set(CacheItem item, CacheItemPolicy policy) { + this.Set(item.Key, item.Value, policy, item.RegionName); + } + + public override void Set(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null) { + this.Set(key, value, new CacheItemPolicy { AbsoluteExpiration = absoluteExpiration }, regionName); + } + + private object AddOrInsert( + Func action, + string key, object value, CacheItemPolicy policy, string regionName = null + ) { + var fileNames = new List(); + foreach (var monitor in policy.ChangeMonitors) { + var fileMonitor = monitor as FileChangeMonitor; + if (fileMonitor == null) + throw new NotImplementedException(); + + fileNames.AddRange(fileMonitor.FilePaths); + } + + var dependency = fileNames.Count > 0 + ? new CacheDependency(fileNames.ToArray()) + : null; + + var callback = (CacheItemRemovedCallback)null; + if (policy.RemovedCallback != null) { + callback = (removedKey, removedValue, reason) => policy.RemovedCallback( + new CacheEntryRemovedArguments(this, reasonMap[reason], new CacheItem(removedKey, removedValue)) + ); + } + + return action( + key, value, dependency, + policy.AbsoluteExpiration != ObjectCache.InfiniteAbsoluteExpiration ? policy.AbsoluteExpiration.UtcDateTime : Cache.NoAbsoluteExpiration, + policy.SlidingExpiration != ObjectCache.NoSlidingExpiration ? policy.SlidingExpiration : Cache.NoSlidingExpiration, + policy.Priority == Runtime.Caching.CacheItemPriority.NotRemovable + ? Web.Caching.CacheItemPriority.NotRemovable + : Web.Caching.CacheItemPriority.Default, + callback + ); + } + + public override object this[string key] { + get { return this.Get(key); } + set { this.Set(key, value, ObjectCache.InfiniteAbsoluteExpiration); } + } + + public override string Name { + get { return "Default"; } + } + + public override DefaultCacheCapabilities DefaultCacheCapabilities { + get { + return DefaultCacheCapabilities.AbsoluteExpirations + | DefaultCacheCapabilities.SlidingExpirations + | DefaultCacheCapabilities.CacheEntryChangeMonitors + | DefaultCacheCapabilities.InMemoryProvider; + } + } + } } \ No newline at end of file diff --git a/Site/Global.asax.cs b/Site/Global.asax.cs index 7bf4700..1e42fa0 100644 --- a/Site/Global.asax.cs +++ b/Site/Global.asax.cs @@ -12,8 +12,6 @@ using Autofac; using Autofac.Integration.Mvc; -using AshMind.Extensions; - using AshMind.Gallery.Core; using AshMind.Gallery.Site.Fixes; using AshMind.Gallery.Site.Routing; diff --git a/Site/Logic/IImageRequestStrategy.cs b/Site/Logic/IImageRequestStrategy.cs index 6086274..6745040 100644 --- a/Site/Logic/IImageRequestStrategy.cs +++ b/Site/Logic/IImageRequestStrategy.cs @@ -1,18 +1,16 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Web.Routing; - -using AshMind.Gallery.Core.IO; -using AshMind.Gallery.Core.Security; - -namespace AshMind.Gallery.Site.Logic { - public interface IImageRequestStrategy { - void MapRoute(RouteCollection routes, string imageControllerName, string getImageActionName); - string GetActionUrl(RequestContext requestContext, string albumID, string itemID); - - bool IsAuthorized(RequestContext requestContext); - IFile GetImageFile(RequestContext requestContext); - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web.Routing; + +using AshMind.Gallery.Core.IO; + +namespace AshMind.Gallery.Site.Logic { + public interface IImageRequestStrategy { + void MapRoute(RouteCollection routes, string imageControllerName, string getImageActionName); + string GetActionUrl(RequestContext requestContext, string albumID, string itemID); + + bool IsAuthorized(RequestContext requestContext); + IFile GetImageFile(RequestContext requestContext); + } } \ No newline at end of file diff --git a/Site/Logic/IUserAuthentication.cs b/Site/Logic/IUserAuthentication.cs index f240ede..66ccf20 100644 --- a/Site/Logic/IUserAuthentication.cs +++ b/Site/Logic/IUserAuthentication.cs @@ -1,16 +1,15 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Principal; -using System.Web; - -using AshMind.Gallery.Core.Security; - -namespace AshMind.Gallery.Site.Logic { - public interface IUserAuthentication { - bool AuthenticateByEmail(string email); - bool AuthenticateByKey(string key); - - IUser GetUser(IPrincipal principal); - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Principal; + +using AshMind.Gallery.Core.Security; + +namespace AshMind.Gallery.Site.Logic { + public interface IUserAuthentication { + bool AuthenticateByEmail(string email); + bool AuthenticateByKey(string key); + + IUser GetUser(IPrincipal principal); + } } \ No newline at end of file diff --git a/Site/Logic/ImageRequest/CookielessImageRequestStrategy.cs b/Site/Logic/ImageRequest/CookielessImageRequestStrategy.cs index 5f99c88..8d8cfea 100644 --- a/Site/Logic/ImageRequest/CookielessImageRequestStrategy.cs +++ b/Site/Logic/ImageRequest/CookielessImageRequestStrategy.cs @@ -1,99 +1,98 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Web; -using System.Web.Mvc; -using System.Web.Routing; - -using AshMind.Gallery.Core; -using AshMind.Gallery.Core.IO; -using AshMind.Gallery.Core.Security; -using AshMind.Gallery.Site.Models; -using AshMind.Gallery.Site.Fixes; - -namespace AshMind.Gallery.Site.Logic.ImageRequest { - public class CookielessImageRequestStrategy : IImageRequestStrategy { - private readonly IAlbumFacade gallery; - protected string RouteName { get; private set; } - - public CookielessImageRequestStrategy(IAlbumFacade gallery) { - this.gallery = gallery; - this.RouteName = "Images (" + this.GetType() + ")"; - } - - public virtual void MapRoute(RouteCollection routes, string imageControllerName, string getImageActionName) { - routes.MapRoute( - this.RouteName, - "image/{key}/{size}", - new { controller = imageControllerName, action = getImageActionName, size = ImageSize.Original } - ); - } - - public virtual string GetActionUrl(RequestContext requestContext, string albumID, string itemID) { - var bytes = Encoding.UTF8.GetBytes(albumID + "/" + itemID); - var keyBytes = EncryptOrDecrypt(bytes, encrypt: true); - var key = BytesToString(keyBytes); - - return GetActionUrl(new UrlHelper(requestContext), new { key }); - } - - protected virtual string GetActionUrl(UrlHelper urlHelper, object routeValues) { - return urlHelper.RouteUrl(this.RouteName, routeValues); - } - - public virtual bool IsAuthorized(RequestContext requestContext) { - return true; - } - - public virtual IFile GetImageFile(RequestContext requestContext) { - var key = (string)requestContext.RouteData.Values["key"]; - - var keyBytes = StringToBytes(key); - var bytes = EncryptOrDecrypt(keyBytes, encrypt: false); - var arguments = Encoding.UTF8.GetString(bytes).Split('/'); - - return this.gallery.GetItem(arguments[0], arguments[1], User.System).File; - } - - private static byte[] EncryptOrDecrypt(byte[] bytes, bool encrypt) { - return MachineKeySectionMethods.EncryptOrDecryptData( - encrypt, bytes, - modifier: null, - start: 0, - length: bytes.Length, - useValidationSymAlgo: false, - useLegacyMode: false, - ivType: IVType.Hash, - signData: true - ); - } - - private static readonly char[] Base64CharsUnsafeForUrl = { '/', '=', '+' }; - - private string BytesToString(byte[] bytes) { - var base64 = Convert.ToBase64String(bytes); - return Regex.Replace( - base64, - "[" + new string(Base64CharsUnsafeForUrl) + "]", - match => { - var @char = match.Value[0]; - return "_" + (char)('a' + Array.IndexOf(Base64CharsUnsafeForUrl, @char)); - } - ); - } - - private byte[] StringToBytes(string value) { - var base64 = Regex.Replace( - value, "_[a-z]", - match => { - var @char = match.Value[1]; - return Base64CharsUnsafeForUrl[@char - 'a'].ToString(); - } - ); - - return Convert.FromBase64String(base64); - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Web.Mvc; +using System.Web.Routing; + +using AshMind.Gallery.Core; +using AshMind.Gallery.Core.IO; +using AshMind.Gallery.Core.Security; +using AshMind.Gallery.Site.Models; +using AshMind.Gallery.Site.Fixes; + +namespace AshMind.Gallery.Site.Logic.ImageRequest { + public class CookielessImageRequestStrategy : IImageRequestStrategy { + private readonly IAlbumFacade gallery; + protected string RouteName { get; private set; } + + public CookielessImageRequestStrategy(IAlbumFacade gallery) { + this.gallery = gallery; + this.RouteName = "Images (" + this.GetType() + ")"; + } + + public virtual void MapRoute(RouteCollection routes, string imageControllerName, string getImageActionName) { + routes.MapRoute( + this.RouteName, + "image/{key}/{size}", + new { controller = imageControllerName, action = getImageActionName, size = ImageSize.Original } + ); + } + + public virtual string GetActionUrl(RequestContext requestContext, string albumID, string itemID) { + var bytes = Encoding.UTF8.GetBytes(albumID + "/" + itemID); + var keyBytes = EncryptOrDecrypt(bytes, encrypt: true); + var key = BytesToString(keyBytes); + + return GetActionUrl(new UrlHelper(requestContext), new { key }); + } + + protected virtual string GetActionUrl(UrlHelper urlHelper, object routeValues) { + return urlHelper.RouteUrl(this.RouteName, routeValues); + } + + public virtual bool IsAuthorized(RequestContext requestContext) { + return true; + } + + public virtual IFile GetImageFile(RequestContext requestContext) { + var key = (string)requestContext.RouteData.Values["key"]; + + var keyBytes = StringToBytes(key); + var bytes = EncryptOrDecrypt(keyBytes, encrypt: false); + var arguments = Encoding.UTF8.GetString(bytes).Split('/'); + + return this.gallery.GetItem(arguments[0], arguments[1], User.System).File; + } + + private static byte[] EncryptOrDecrypt(byte[] bytes, bool encrypt) { + return MachineKeySectionMethods.EncryptOrDecryptData( + encrypt, bytes, + modifier: null, + start: 0, + length: bytes.Length, + useValidationSymAlgo: false, + useLegacyMode: false, + ivType: IVType.Hash, + signData: true + ); + } + + private static readonly char[] Base64CharsUnsafeForUrl = { '/', '=', '+' }; + + private string BytesToString(byte[] bytes) { + var base64 = Convert.ToBase64String(bytes); + return Regex.Replace( + base64, + "[" + new string(Base64CharsUnsafeForUrl) + "]", + match => { + var @char = match.Value[0]; + return "_" + (char)('a' + Array.IndexOf(Base64CharsUnsafeForUrl, @char)); + } + ); + } + + private byte[] StringToBytes(string value) { + var base64 = Regex.Replace( + value, "_[a-z]", + match => { + var @char = match.Value[1]; + return Base64CharsUnsafeForUrl[@char - 'a'].ToString(); + } + ); + + return Convert.FromBase64String(base64); + } + } } \ No newline at end of file diff --git a/Site/Logic/ImageRequest/FriendlyImageUrlStrategy.cs b/Site/Logic/ImageRequest/FriendlyImageUrlStrategy.cs index 0cfbd2f..8c338bc 100644 --- a/Site/Logic/ImageRequest/FriendlyImageUrlStrategy.cs +++ b/Site/Logic/ImageRequest/FriendlyImageUrlStrategy.cs @@ -1,59 +1,58 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.Mvc; -using System.Web.Routing; - -using AshMind.Gallery.Site.Models; -using AshMind.Gallery.Core; -using AshMind.Gallery.Core.IO; -using AshMind.Gallery.Core.Security; - -namespace AshMind.Gallery.Site.Logic.ImageRequest { - public class FriendlyImageUrlStrategy : IImageRequestStrategy { - private static readonly string RouteName = "Images (" + typeof(FriendlyImageUrlStrategy) + ")"; - - private readonly IAlbumFacade gallery; - private readonly IUserAuthentication authentication; - - public FriendlyImageUrlStrategy( - IAlbumFacade gallery, - IUserAuthentication authentication - ) { - this.gallery = gallery; - this.authentication = authentication; - } - - public void MapRoute(RouteCollection routes, string imageControllerName, string getImageActionName) { - routes.MapRoute( - RouteName, - "{album}/{item}/{size}", - new { controller = imageControllerName, action = getImageActionName, size = ImageSize.Original } - ); - } - - public string GetActionUrl(RequestContext requestContext, string albumID, string itemID) { - return new UrlHelper(requestContext).RouteUrl(RouteName, new { album = albumID, item = itemID }); - } - - public bool IsAuthorized(RequestContext requestContext) { - var user = this.authentication.GetUser(requestContext.HttpContext.User); - if (user == null) - return false; - - var albumID = (string)requestContext.RouteData.Values["album"]; - var item = (string)requestContext.RouteData.Values["item"]; - - // TODO: rewrite it as a true authorization check - return this.gallery.GetItem(albumID, item, user) != null; - } - - public IFile GetImageFile(RequestContext requestContext) { - var albumID = (string)requestContext.RouteData.Values["album"]; - var item = (string)requestContext.RouteData.Values["item"]; - - return this.gallery.GetItem(albumID, item, User.System).File; - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web.Mvc; +using System.Web.Routing; + +using AshMind.Gallery.Site.Models; +using AshMind.Gallery.Core; +using AshMind.Gallery.Core.IO; +using AshMind.Gallery.Core.Security; + +namespace AshMind.Gallery.Site.Logic.ImageRequest { + public class FriendlyImageUrlStrategy : IImageRequestStrategy { + private static readonly string RouteName = "Images (" + typeof(FriendlyImageUrlStrategy) + ")"; + + private readonly IAlbumFacade gallery; + private readonly IUserAuthentication authentication; + + public FriendlyImageUrlStrategy( + IAlbumFacade gallery, + IUserAuthentication authentication + ) { + this.gallery = gallery; + this.authentication = authentication; + } + + public void MapRoute(RouteCollection routes, string imageControllerName, string getImageActionName) { + routes.MapRoute( + RouteName, + "{album}/{item}/{size}", + new { controller = imageControllerName, action = getImageActionName, size = ImageSize.Original } + ); + } + + public string GetActionUrl(RequestContext requestContext, string albumID, string itemID) { + return new UrlHelper(requestContext).RouteUrl(RouteName, new { album = albumID, item = itemID }); + } + + public bool IsAuthorized(RequestContext requestContext) { + var user = this.authentication.GetUser(requestContext.HttpContext.User); + if (user == null) + return false; + + var albumID = (string)requestContext.RouteData.Values["album"]; + var item = (string)requestContext.RouteData.Values["item"]; + + // TODO: rewrite it as a true authorization check + return this.gallery.GetItem(albumID, item, user) != null; + } + + public IFile GetImageFile(RequestContext requestContext) { + var albumID = (string)requestContext.RouteData.Values["album"]; + var item = (string)requestContext.RouteData.Values["item"]; + + return this.gallery.GetItem(albumID, item, User.System).File; + } + } } \ No newline at end of file diff --git a/Site/Logic/ImageRequest/SubdomainImageRequestStrategy.cs b/Site/Logic/ImageRequest/SubdomainImageRequestStrategy.cs index f797eda..1bbaecc 100644 --- a/Site/Logic/ImageRequest/SubdomainImageRequestStrategy.cs +++ b/Site/Logic/ImageRequest/SubdomainImageRequestStrategy.cs @@ -1,38 +1,36 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.Mvc; -using System.Web.Routing; - -using AshMind.Gallery.Core; -using System.Threading; - -namespace AshMind.Gallery.Site.Logic.ImageRequest { - public class SubdomainImageRequestStrategy : CookielessImageRequestStrategy { - private int currentIndex; - private string subdomainPattern; - - public SubdomainImageRequestStrategy(IAlbumFacade gallery) : this(gallery, "img{0}") { - } - - public SubdomainImageRequestStrategy(IAlbumFacade gallery, string subdomainPattern) : base(gallery) { - this.currentIndex = 1; - this.subdomainPattern = subdomainPattern.TrimEnd('.'); - } - - protected override string GetActionUrl(UrlHelper urlHelper, object routeValues) { - var urlString = urlHelper.RouteUrl(this.RouteName, routeValues, urlHelper.RequestContext.HttpContext.Request.Url.Scheme); - var urlBuilder = new UriBuilder(urlString); - - if (!urlBuilder.Host.Contains(".")) // this should be overridable in future, but not now - return urlBuilder.ToString(); - - Interlocked.Increment(ref currentIndex); - Interlocked.CompareExchange(ref currentIndex, 1, 6); - - urlBuilder.Host = string.Format(subdomainPattern, currentIndex) + "." + urlBuilder.Host; - return urlBuilder.ToString(); - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web.Mvc; + +using AshMind.Gallery.Core; +using System.Threading; + +namespace AshMind.Gallery.Site.Logic.ImageRequest { + public class SubdomainImageRequestStrategy : CookielessImageRequestStrategy { + private int currentIndex; + private readonly string subdomainPattern; + + public SubdomainImageRequestStrategy(IAlbumFacade gallery) : this(gallery, "img{0}") { + } + + public SubdomainImageRequestStrategy(IAlbumFacade gallery, string subdomainPattern) : base(gallery) { + this.currentIndex = 1; + this.subdomainPattern = subdomainPattern.TrimEnd('.'); + } + + protected override string GetActionUrl(UrlHelper urlHelper, object routeValues) { + var urlString = urlHelper.RouteUrl(this.RouteName, routeValues, urlHelper.RequestContext.HttpContext.Request.Url.Scheme); + var urlBuilder = new UriBuilder(urlString); + + if (!urlBuilder.Host.Contains(".")) // this should be overridable in future, but not now + return urlBuilder.ToString(); + + Interlocked.Increment(ref currentIndex); + Interlocked.CompareExchange(ref currentIndex, 1, 6); + + urlBuilder.Host = string.Format(subdomainPattern, currentIndex) + "." + urlBuilder.Host; + return urlBuilder.ToString(); + } + } } \ No newline at end of file diff --git a/Site/Logic/PeopleAlbumNameRegexTransform.cs b/Site/Logic/PeopleAlbumNameRegexTransform.cs index fb90f6c..25f9cfb 100644 --- a/Site/Logic/PeopleAlbumNameRegexTransform.cs +++ b/Site/Logic/PeopleAlbumNameRegexTransform.cs @@ -1,27 +1,26 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using System.Web; - -using AshMind.Gallery.Core; -using AshMind.Gallery.Core.AlbumSupport; - -namespace AshMind.Gallery.Site.Logic { - public class PeopleAlbumNameRegexTransform : IAlbumNameTransform { - private readonly Regex regex; - private readonly string replacement; - - public PeopleAlbumNameRegexTransform(Regex regex, string replacement) { - this.regex = regex; - this.replacement = replacement; - } - - public string Transform(string albumName, AlbumDescriptor descriptor) { - if (descriptor.ProviderKey != AlbumProviderKeys.People) - return albumName; - - return regex.Replace(albumName, this.replacement); - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +using AshMind.Gallery.Core; +using AshMind.Gallery.Core.AlbumSupport; + +namespace AshMind.Gallery.Site.Logic { + public class PeopleAlbumNameRegexTransform : IAlbumNameTransform { + private readonly Regex regex; + private readonly string replacement; + + public PeopleAlbumNameRegexTransform(Regex regex, string replacement) { + this.regex = regex; + this.replacement = replacement; + } + + public string Transform(string albumName, AlbumDescriptor descriptor) { + if (descriptor.ProviderKey != AlbumProviderKeys.People) + return albumName; + + return regex.Replace(albumName, this.replacement); + } + } } \ No newline at end of file diff --git a/Site/Logic/UserAuthentication.cs b/Site/Logic/UserAuthentication.cs index 15bb3f4..baee2aa 100644 --- a/Site/Logic/UserAuthentication.cs +++ b/Site/Logic/UserAuthentication.cs @@ -1,61 +1,60 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Principal; -using System.Web; -using System.Web.Security; - -using AshMind.Extensions; - -using AshMind.Gallery.Core; -using AshMind.Gallery.Core.Security; - -namespace AshMind.Gallery.Site.Logic { - public class UserAuthentication : IUserAuthentication { - private static string GroupMemberNamePrefix = "anonymous-member-of:"; - - private readonly IRepository userRepository; - private readonly IRepository userGroupRepository; - - public UserAuthentication( - IRepository userRepository, - IRepository userGroupRepository - ) { - this.userRepository = userRepository; - this.userGroupRepository = userGroupRepository; - } - - public bool AuthenticateByKey(string key) { - if (key.IsNullOrEmpty()) - return false; - - var group = this.userGroupRepository.Query().SingleOrDefault(u => u.Keys.Contains(key)); - if (group == null) - return false; - - var groupMemberKey = GroupMemberNamePrefix + this.userGroupRepository.GetKey(group); - FormsAuthentication.SetAuthCookie(groupMemberKey, false); - return true; - } - - public bool AuthenticateByEmail(string email) { - var user = this.userRepository.FindByEmail(email); - if (user == null) - return false; - - FormsAuthentication.SetAuthCookie((string)this.userRepository.GetKey(user), false); - return true; - } - - public IUser GetUser(IPrincipal principal) { - if (!principal.Identity.IsAuthenticated) - return null; - - var userName = principal.Identity.Name; - if (userName.StartsWith(GroupMemberNamePrefix)) - return new AnonymousMember(this.userGroupRepository.Load(userName.SubstringAfter(GroupMemberNamePrefix))); - - return this.userRepository.Load(userName); - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Principal; +using System.Web.Security; + +using AshMind.Extensions; + +using AshMind.Gallery.Core; +using AshMind.Gallery.Core.Security; + +namespace AshMind.Gallery.Site.Logic { + public class UserAuthentication : IUserAuthentication { + private const string GroupMemberNamePrefix = "anonymous-member-of:"; + + private readonly IRepository userRepository; + private readonly IRepository userGroupRepository; + + public UserAuthentication( + IRepository userRepository, + IRepository userGroupRepository + ) { + this.userRepository = userRepository; + this.userGroupRepository = userGroupRepository; + } + + public bool AuthenticateByKey(string key) { + if (key.IsNullOrEmpty()) + return false; + + var group = this.userGroupRepository.Query().SingleOrDefault(u => u.Keys.Contains(key)); + if (group == null) + return false; + + var groupMemberKey = GroupMemberNamePrefix + this.userGroupRepository.GetKey(group); + FormsAuthentication.SetAuthCookie(groupMemberKey, false); + return true; + } + + public bool AuthenticateByEmail(string email) { + var user = this.userRepository.FindByEmail(email); + if (user == null) + return false; + + FormsAuthentication.SetAuthCookie((string)this.userRepository.GetKey(user), false); + return true; + } + + public IUser GetUser(IPrincipal principal) { + if (!principal.Identity.IsAuthenticated) + return null; + + var userName = principal.Identity.Name; + if (userName.StartsWith(GroupMemberNamePrefix)) + return new AnonymousMember(this.userGroupRepository.Load(userName.SubstringAfter(GroupMemberNamePrefix))); + + return this.userRepository.Load(userName); + } + } } \ No newline at end of file diff --git a/Site/Models/AlbumListViewModel.cs b/Site/Models/AlbumListViewModel.cs index e050bf7..5331b6d 100644 --- a/Site/Models/AlbumListViewModel.cs +++ b/Site/Models/AlbumListViewModel.cs @@ -1,23 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Web; - -using AshMind.Extensions; - -using AshMind.Gallery.Core; - -namespace AshMind.Gallery.Site.Models { - public class AlbumListViewModel : PagedListViewModel { - public AlbumListViewModel( - IList albums, - int start, - int? yearBeforeStart - ) : base(albums, start) { - this.YearBeforeStart = yearBeforeStart; - } - - public int? YearBeforeStart { get; private set; } - } +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Site.Models { + public class AlbumListViewModel : PagedListViewModel { + public AlbumListViewModel( + IList albums, + int start, + int? yearBeforeStart + ) : base(albums, start) { + this.YearBeforeStart = yearBeforeStart; + } + + public int? YearBeforeStart { get; private set; } + } } \ No newline at end of file diff --git a/Site/Models/AlbumViewModel.cs b/Site/Models/AlbumViewModel.cs index b552259..be4b125 100644 --- a/Site/Models/AlbumViewModel.cs +++ b/Site/Models/AlbumViewModel.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; -using System.Web; using AshMind.Extensions; diff --git a/Site/Models/DeleteProposalGroupModel.cs b/Site/Models/DeleteProposalGroupModel.cs index e666935..a7c86c4 100644 --- a/Site/Models/DeleteProposalGroupModel.cs +++ b/Site/Models/DeleteProposalGroupModel.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Web; -using AshMind.Extensions; using AshMind.Gallery.Core.Security; namespace AshMind.Gallery.Site.Models { diff --git a/Site/Models/GalleryViewModel.cs b/Site/Models/GalleryViewModel.cs index 430b784..6c01ce2 100644 --- a/Site/Models/GalleryViewModel.cs +++ b/Site/Models/GalleryViewModel.cs @@ -1,35 +1,34 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Web; - -using AshMind.Extensions; - -using AshMind.Gallery.Core; - -namespace AshMind.Gallery.Site.Models { - public class GalleryViewModel { - public GalleryViewModel( - AlbumViewModel currentUserAlbum, - IList otherPeopleAlbums, - AlbumListViewModel standardAlbums, - AlbumViewModel selected - ) { - this.CurrentUserAlbum = currentUserAlbum; - this.OtherPeopleAlbums = otherPeopleAlbums.AsReadOnly(); - this.StandardAlbums = standardAlbums; - this.Selected = selected; - } - - public AlbumViewModel CurrentUserAlbum { get; private set; } - public ReadOnlyCollection OtherPeopleAlbums { get; private set; } - public AlbumListViewModel StandardAlbums { get; private set; } - public AlbumViewModel Selected { get; private set; } - - public bool IsSelected(Album album) { - return this.Selected != null - && this.Selected.Album == album; - } - } +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +using AshMind.Extensions; + +using AshMind.Gallery.Core; + +namespace AshMind.Gallery.Site.Models { + public class GalleryViewModel { + public GalleryViewModel( + AlbumViewModel currentUserAlbum, + IList otherPeopleAlbums, + AlbumListViewModel standardAlbums, + AlbumViewModel selected + ) { + this.CurrentUserAlbum = currentUserAlbum; + this.OtherPeopleAlbums = otherPeopleAlbums.AsReadOnly(); + this.StandardAlbums = standardAlbums; + this.Selected = selected; + } + + public AlbumViewModel CurrentUserAlbum { get; private set; } + public ReadOnlyCollection OtherPeopleAlbums { get; private set; } + public AlbumListViewModel StandardAlbums { get; private set; } + public AlbumViewModel Selected { get; private set; } + + public bool IsSelected(Album album) { + return this.Selected != null + && this.Selected.Album == album; + } + } } \ No newline at end of file diff --git a/Site/Models/GrantViewModel.cs b/Site/Models/GrantViewModel.cs index 745f6d5..e2e46ea 100644 --- a/Site/Models/GrantViewModel.cs +++ b/Site/Models/GrantViewModel.cs @@ -1,31 +1,30 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Web; - -using AshMind.Extensions; -using AshMind.Gallery.Core.Security; - -namespace AshMind.Gallery.Site.Models { - public class GrantViewModel { - private readonly HashSet grantedUserGroups; - - public GrantViewModel( - string albumID, - HashSet grantedUserGroups, - IList allUserGroups - ) { - this.AlbumID = albumID; - this.grantedUserGroups = grantedUserGroups; - this.AllUserGroups = allUserGroups.AsReadOnly(); - } - - public string AlbumID { get; private set; } - public ReadOnlyCollection AllUserGroups { get; private set; } - - public bool HasAccess(UserGroupViewModel model) { - return this.grantedUserGroups.Contains(model.UserGroup); - } - } +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +using AshMind.Extensions; +using AshMind.Gallery.Core.Security; + +namespace AshMind.Gallery.Site.Models { + public class GrantViewModel { + private readonly HashSet grantedUserGroups; + + public GrantViewModel( + string albumID, + HashSet grantedUserGroups, + IList allUserGroups + ) { + this.AlbumID = albumID; + this.grantedUserGroups = grantedUserGroups; + this.AllUserGroups = allUserGroups.AsReadOnly(); + } + + public string AlbumID { get; private set; } + public ReadOnlyCollection AllUserGroups { get; private set; } + + public bool HasAccess(UserGroupViewModel model) { + return this.grantedUserGroups.Contains(model.UserGroup); + } + } } \ No newline at end of file diff --git a/Site/Models/ImageSize.cs b/Site/Models/ImageSize.cs index 45f28d2..631b148 100644 --- a/Site/Models/ImageSize.cs +++ b/Site/Models/ImageSize.cs @@ -1,43 +1,41 @@ -using System; -using System.Data; -using System.Collections.Generic; -using System.Configuration; -using System.Linq; - -namespace AshMind.Gallery.Site.Models { - public sealed class ImageSize { - public static ImageSize Thumbnail { get; private set; } - public static ImageSize Small { get; private set; } - public static ImageSize Medium { get; private set; } - public static ImageSize Large { get; private set; } - public static ImageSize Original { get; private set; } - - private static IDictionary sizesByName; - - static ImageSize() { - Thumbnail = new ImageSize("Thumbnail", 250); - Small = new ImageSize("Small", 500); - Medium = new ImageSize("Medium", 1000); - Large = new ImageSize("Large", 3000); - Original = new ImageSize("Original", 10000); - - sizesByName = new[] { Thumbnail, Small, Medium, Large, Original }.ToDictionary(s => s.Name, StringComparer.InvariantCultureIgnoreCase); - } - - public ImageSize(string name, int size) { - this.Name = name; - this.Size = size; - } - - public static ImageSize Parse(string name) { - return sizesByName[name]; - } - - public string Name { get; private set; } - public int Size { get; set; } - - public override string ToString() { - return this.Name; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AshMind.Gallery.Site.Models { + public sealed class ImageSize { + public static ImageSize Thumbnail { get; private set; } + public static ImageSize Small { get; private set; } + public static ImageSize Medium { get; private set; } + public static ImageSize Large { get; private set; } + public static ImageSize Original { get; private set; } + + private static readonly IDictionary sizesByName; + + static ImageSize() { + Thumbnail = new ImageSize("Thumbnail", 250); + Small = new ImageSize("Small", 500); + Medium = new ImageSize("Medium", 1000); + Large = new ImageSize("Large", 3000); + Original = new ImageSize("Original", 10000); + + sizesByName = new[] { Thumbnail, Small, Medium, Large, Original }.ToDictionary(s => s.Name, StringComparer.InvariantCultureIgnoreCase); + } + + public ImageSize(string name, int size) { + this.Name = name; + this.Size = size; + } + + public static ImageSize Parse(string name) { + return sizesByName[name]; + } + + public string Name { get; private set; } + public int Size { get; set; } + + public override string ToString() { + return this.Name; + } + } +} diff --git a/Site/Models/ItemDetailsViewModel.cs b/Site/Models/ItemDetailsViewModel.cs index 5a16a88..2f35ed5 100644 --- a/Site/Models/ItemDetailsViewModel.cs +++ b/Site/Models/ItemDetailsViewModel.cs @@ -1,21 +1,20 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; - -using AshMind.Gallery.Core; -using AshMind.Gallery.Core.Security; - -namespace AshMind.Gallery.Site.Models { - public class ItemDetailsViewModel { - public ItemDetailsViewModel(string albumID, AlbumItem item, IUser currentUser) { - this.AlbumID = albumID; - this.Item = item; - this.CurrentUser = currentUser; - } - - public string AlbumID { get; private set; } - public AlbumItem Item { get; private set; } - public IUser CurrentUser { get; private set; } - } +using System; +using System.Collections.Generic; +using System.Linq; + +using AshMind.Gallery.Core; +using AshMind.Gallery.Core.Security; + +namespace AshMind.Gallery.Site.Models { + public class ItemDetailsViewModel { + public ItemDetailsViewModel(string albumID, AlbumItem item, IUser currentUser) { + this.AlbumID = albumID; + this.Item = item; + this.CurrentUser = currentUser; + } + + public string AlbumID { get; private set; } + public AlbumItem Item { get; private set; } + public IUser CurrentUser { get; private set; } + } } \ No newline at end of file diff --git a/Site/Models/PagedListViewModel.cs b/Site/Models/PagedListViewModel.cs index 91e3a1c..f339b3e 100644 --- a/Site/Models/PagedListViewModel.cs +++ b/Site/Models/PagedListViewModel.cs @@ -1,26 +1,23 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Web; - -using AshMind.Extensions; - -using AshMind.Gallery.Core; - -namespace AshMind.Gallery.Site.Models { - public class PagedListViewModel { - public PagedListViewModel( - IList items, int start - ) { - this.List = items.AsReadOnly(); - this.Start = start; - } - - public ReadOnlyCollection List { get; private set; } - public int Start { get; private set; } - public int Count { - get { return this.List.Count; } - } - } +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +using AshMind.Extensions; + +namespace AshMind.Gallery.Site.Models { + public class PagedListViewModel { + public PagedListViewModel( + IList items, int start + ) { + this.List = items.AsReadOnly(); + this.Start = start; + } + + public ReadOnlyCollection List { get; private set; } + public int Start { get; private set; } + public int Count { + get { return this.List.Count; } + } + } } \ No newline at end of file diff --git a/Site/Models/UserGroupViewModel.cs b/Site/Models/UserGroupViewModel.cs index 13c3abe..6909408 100644 --- a/Site/Models/UserGroupViewModel.cs +++ b/Site/Models/UserGroupViewModel.cs @@ -1,38 +1,35 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Web; - -using AshMind.Extensions; - -using AshMind.Gallery.Core.Security; - -namespace AshMind.Gallery.Site.Models { - public class UserGroupViewModel { - public UserGroupViewModel(IUserGroup userGroup) { - var user = userGroup as User; - if (user != null) { - this.Key = "user:" + user.Email; - } - else { - var concreteGroup = userGroup as UserGroup; - if (concreteGroup == null) - throw new ArgumentException("group"); - - this.Key = "group:" + concreteGroup.Name; - } - - this.UserGroup = userGroup; - this.Users = userGroup.GetUsers().OfType().ToList().AsReadOnly(); - } - - public string Key { get; private set; } - public string Name { - get { return this.UserGroup.Name; } - } - - public IUserGroup UserGroup { get; private set; } - public ReadOnlyCollection Users { get; private set; } - } +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +using AshMind.Gallery.Core.Security; + +namespace AshMind.Gallery.Site.Models { + public class UserGroupViewModel { + public UserGroupViewModel(IUserGroup userGroup) { + var user = userGroup as User; + if (user != null) { + this.Key = "user:" + user.Email; + } + else { + var concreteGroup = userGroup as UserGroup; + if (concreteGroup == null) + throw new ArgumentException("group"); + + this.Key = "group:" + concreteGroup.Name; + } + + this.UserGroup = userGroup; + this.Users = userGroup.GetUsers().OfType().ToList().AsReadOnly(); + } + + public string Key { get; private set; } + public string Name { + get { return this.UserGroup.Name; } + } + + public IUserGroup UserGroup { get; private set; } + public ReadOnlyCollection Users { get; private set; } + } } \ No newline at end of file diff --git a/Site/OpenIdAbstraction/OpenIdRelyingParty.cs b/Site/OpenIdAbstraction/OpenIdRelyingParty.cs index 9813cd7..ee6702d 100644 --- a/Site/OpenIdAbstraction/OpenIdRelyingParty.cs +++ b/Site/OpenIdAbstraction/OpenIdRelyingParty.cs @@ -1,50 +1,49 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web.Mvc; - -using DotNetOpenAuth.Messaging; -using DotNetOpenAuth.OpenId; -using DotNetOpenAuth.OpenId.RelyingParty; - -namespace AshMind.Gallery.Site.OpenIdAbstraction { - public class OpenIdAjaxRelyingParty : IOpenIdAjaxRelyingParty { - private readonly DotNetOpenAuth.OpenId.RelyingParty.OpenIdAjaxRelyingParty actual; - - public OpenIdAjaxRelyingParty(DotNetOpenAuth.OpenId.RelyingParty.OpenIdAjaxRelyingParty actual) { - this.actual = actual; - } - - public Channel Channel { - get { return this.actual.Channel; } - } - - public IAuthenticationRequest CreateRequest(Identifier userSuppliedIdentifier, Realm realm, Uri returnToUrl) { - return this.actual.CreateRequest(userSuppliedIdentifier, realm, returnToUrl); - } - - public IEnumerable CreateRequests(Identifier userSuppliedIdentifier, Realm realm, Uri returnToUrl) { - return this.actual.CreateRequests(userSuppliedIdentifier, realm, returnToUrl); - } - - public OutgoingWebResponse AsAjaxDiscoveryResult(IEnumerable requests) { - return this.actual.AsAjaxDiscoveryResult(requests); - } - - public string AsAjaxPreloadedDiscoveryResult(IEnumerable requests) { - return this.actual.AsAjaxPreloadedDiscoveryResult(requests); - } - - public OutgoingWebResponse ProcessResponseFromPopup() { - return this.actual.ProcessResponseFromPopup(); - } - - public IAuthenticationResponse GetResponse() { - return this.actual.GetResponse(); - } - - public IAuthenticationResponse GetResponse(HttpRequestInfo request) { - return this.actual.GetResponse(request); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +using DotNetOpenAuth.Messaging; +using DotNetOpenAuth.OpenId; +using DotNetOpenAuth.OpenId.RelyingParty; + +namespace AshMind.Gallery.Site.OpenIdAbstraction { + public class OpenIdAjaxRelyingParty : IOpenIdAjaxRelyingParty { + private readonly DotNetOpenAuth.OpenId.RelyingParty.OpenIdAjaxRelyingParty actual; + + public OpenIdAjaxRelyingParty(DotNetOpenAuth.OpenId.RelyingParty.OpenIdAjaxRelyingParty actual) { + this.actual = actual; + } + + public Channel Channel { + get { return this.actual.Channel; } + } + + public IAuthenticationRequest CreateRequest(Identifier userSuppliedIdentifier, Realm realm, Uri returnToUrl) { + return this.actual.CreateRequest(userSuppliedIdentifier, realm, returnToUrl); + } + + public IEnumerable CreateRequests(Identifier userSuppliedIdentifier, Realm realm, Uri returnToUrl) { + return this.actual.CreateRequests(userSuppliedIdentifier, realm, returnToUrl); + } + + public OutgoingWebResponse AsAjaxDiscoveryResult(IEnumerable requests) { + return this.actual.AsAjaxDiscoveryResult(requests); + } + + public string AsAjaxPreloadedDiscoveryResult(IEnumerable requests) { + return this.actual.AsAjaxPreloadedDiscoveryResult(requests); + } + + public OutgoingWebResponse ProcessResponseFromPopup() { + return this.actual.ProcessResponseFromPopup(); + } + + public IAuthenticationResponse GetResponse() { + return this.actual.GetResponse(); + } + + public IAuthenticationResponse GetResponse(HttpRequestInfo request) { + return this.actual.GetResponse(request); + } + } +} diff --git a/Site/Properties/AssemblyInfo.cs b/Site/Properties/AssemblyInfo.cs index ef74353..806bd3e 100644 --- a/Site/Properties/AssemblyInfo.cs +++ b/Site/Properties/AssemblyInfo.cs @@ -1,35 +1,28 @@ -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("Site")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("Site")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2010")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// 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("27a9e74f-cbb8-4ca1-907c-b79bd5a4379c")] - -// 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("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +using System.Reflection; +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("AshMind.Gallery.Site")] +[assembly: AssemblyProduct("AshMind.Gallery.Site")] + +// 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("27a9e74f-cbb8-4ca1-907c-b79bd5a4379c")] + +// 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("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Site/Scripts/gallery.js b/Site/Scripts/gallery.js index bd1ee8b..adb1920 100644 --- a/Site/Scripts/gallery.js +++ b/Site/Scripts/gallery.js @@ -13,7 +13,7 @@ if (Array.prototype.filter) { result.push(this[i]); } return result; - } + }; } $(function() { @@ -185,14 +185,14 @@ function setupAlbum() { var download = "Download: "; var downloadData = data.actions.download; for (var name in downloadData.sizes) { - download += "" + name + "" + download += "" + name + ""; } download += ""; return "" + [ locate, deleteOrRestore, download, "Comment" ] .filter(function(item) { return !!item; }) - .join('|') + .join('|') + ""; }, diff --git a/Site/Site.csproj b/Site/Site.csproj index a4a0829..6502e1d 100644 --- a/Site/Site.csproj +++ b/Site/Site.csproj @@ -87,6 +87,9 @@ + + Properties\AssemblyInfoCommon.cs +