WebFileServer
Changes
.gitignore 1(+1 -0)
FileServer/.vs/FileServer/v15/.suo 0(+0 -0)
FileServer/BLL/BLL.csproj 3(+3 -0)
FileServer/BLL/Services/PermissionServices.cs 105(+105 -0)
FileServer/BLL/Services/ScanServices.cs 23(+19 -4)
FileServer/Console/Program.cs 24(+13 -11)
FileServer/Model/Entities/Files/FS_Item.cs 13(+10 -3)
FileServer/Model/Entities/Users/Group.cs 97(+80 -17)
FileServer/Model/Entities/Users/User.cs 21(+18 -3)
FileServer/Model/UnitsOfWork/UOW.cs 77(+68 -9)
FileServer/Web/Controllers/API/ExplorerController.cs 132(+103 -29)
FileServer/Web/Global.asax.cs 5(+3 -2)
FileServer/Web/Views/Home/Index.cshtml 19(+12 -7)
FileServer/Web/Web.config 2(+1 -1)
FileServer/Web/Web.csproj 20(+14 -6)
Диаграммы/Диаграмма1.graphml 650(+650 -0)
Details
.gitignore 1(+1 -0)
diff --git a/.gitignore b/.gitignore
index d9ae6e1..edbe56e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,3 +26,4 @@ FileServer/Web/Scripts/*
!FileServer/Web/Scripts/Tools/
FileServer/Web/Content
+!FileServer/Web/Content/Site.css
FileServer/.vs/FileServer/v15/.suo 0(+0 -0)
diff --git a/FileServer/.vs/FileServer/v15/.suo b/FileServer/.vs/FileServer/v15/.suo
index 6edc2ed..cd5ecb8 100644
Binary files a/FileServer/.vs/FileServer/v15/.suo and b/FileServer/.vs/FileServer/v15/.suo differ
FileServer/BLL/BLL.csproj 3(+3 -0)
diff --git a/FileServer/BLL/BLL.csproj b/FileServer/BLL/BLL.csproj
index 733abb9..aff73f9 100644
--- a/FileServer/BLL/BLL.csproj
+++ b/FileServer/BLL/BLL.csproj
@@ -51,9 +51,12 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Base\BaseServices.cs" />
+ <Compile Include="Services\FSExplorerServices.cs" />
+ <Compile Include="Services\FS_WathcerServices.cs" />
<Compile Include="Services\GarbageUploadServices.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\ConfigurationServices.cs" />
+ <Compile Include="Services\PermissionServices.cs" />
<Compile Include="Services\ScanServices.cs" />
<Compile Include="Services\UploadServices.cs" />
</ItemGroup>
diff --git a/FileServer/BLL/Services/ConfigurationServices.cs b/FileServer/BLL/Services/ConfigurationServices.cs
index 1c89670..59871b3 100644
--- a/FileServer/BLL/Services/ConfigurationServices.cs
+++ b/FileServer/BLL/Services/ConfigurationServices.cs
@@ -15,6 +15,9 @@ using Model.Entities.Files.Repo;
namespace BLL.Services
{
+ /// <summary>
+ /// Сервис, иницилизирующий корень фс в базе на основе файла конфигурации
+ /// </summary>
public class ConfigurationServices : BaseServices
{
const string FolderPrefix = "WorkFolder";
@@ -27,12 +30,12 @@ namespace BLL.Services
var config = ConfigurationManager.AppSettings;
var folder_values = new LinkedList<string>(config.GetValues(FolderPrefix).First().Split(';'));
- var db_dir = UOW.Repo_rootDirectory.All;
+ var db_dir = UOW.Repo_SRootDirectory.All;
foreach (var elem in db_dir)
{
if (!folder_values.Contains(elem.PhysicalPath))
- UOW.Repo_rootDirectory.DeleteInList(elem);
+ UOW.Repo_SRootDirectory.DeleteInList(elem);
else
folder_values.Remove(elem.PhysicalPath);
}
@@ -42,8 +45,10 @@ namespace BLL.Services
var nRoot = new List<SRootDirectory>(folder_values.Count);
foreach (var elem in folder_values)
- nRoot.Add(UOW.Repo_rootDirectory.
+ {
+ nRoot.Add(UOW.Repo_SRootDirectory.
Create(new SRootDirectory(elem, Path.GetFileName(elem))));
+ }
}
}
}
diff --git a/FileServer/BLL/Services/FS_WathcerServices.cs b/FileServer/BLL/Services/FS_WathcerServices.cs
new file mode 100644
index 0000000..ee0a484
--- /dev/null
+++ b/FileServer/BLL/Services/FS_WathcerServices.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using System.IO;
+
+namespace BLL.Services
+{
+ public class FS_WathcerServices
+ {
+
+ }
+}
diff --git a/FileServer/BLL/Services/FSExplorerServices.cs b/FileServer/BLL/Services/FSExplorerServices.cs
new file mode 100644
index 0000000..fab8c99
--- /dev/null
+++ b/FileServer/BLL/Services/FSExplorerServices.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Model.Entities.Files.FS_Entities;
+using Model.UnitsOfWork;
+
+namespace BLL.Services
+{
+ public class FSExplorerServices : Base.BaseServices
+ {
+ public FSExplorerServices(UOW UOW) : base(UOW) { }
+
+ //public SDirectory GetDirectory(int ID, bool IncludeItems = false, bool IncludeParent = false)
+ //{
+ // if (ID == -1)
+ // {
+ // //UOW.Repo_rootDirectory
+ // }
+ // else
+ // {
+
+ // }
+ //}
+
+ }
+}
diff --git a/FileServer/BLL/Services/GarbageUploadServices.cs b/FileServer/BLL/Services/GarbageUploadServices.cs
index a5aca85..bffb45f 100644
--- a/FileServer/BLL/Services/GarbageUploadServices.cs
+++ b/FileServer/BLL/Services/GarbageUploadServices.cs
@@ -13,6 +13,9 @@ using Model.Entities.Files.FS_Entities;
namespace BLL.Services
{
+ /// <summary>
+ /// Сервис для уничтожения метрвых загрузок
+ /// </summary>
public class GarbageUploadServices : BaseServices
{
readonly static TimeSpan UploadDeadTimeout = new TimeSpan(0, 5, 0);
FileServer/BLL/Services/PermissionServices.cs 105(+105 -0)
diff --git a/FileServer/BLL/Services/PermissionServices.cs b/FileServer/BLL/Services/PermissionServices.cs
new file mode 100644
index 0000000..4cb358b
--- /dev/null
+++ b/FileServer/BLL/Services/PermissionServices.cs
@@ -0,0 +1,105 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Model.Entities.Files;
+using Model.Entities.Files.FS_Entities;
+using Model.Entities.Users;
+using Model.UnitsOfWork;
+
+
+namespace BLL.Services
+{
+ /// <summary>
+ /// Сервис проверки прав пользователя на основе групп
+ /// </summary>
+ public class PermissionServices : Base.BaseServices
+ {
+ public PermissionServices(UOW UOW) : base(UOW) { }
+
+ /// <summary>
+ /// Метод получает права на папку для пользователя
+ /// на основе того, к каким группам он относиться
+ /// </summary>
+ /// <param name="user"></param>
+ /// <param name="rootDirectory"></param>
+ /// <returns></returns>
+ protected Permission GetPermissionForRootDirectory(User user, SRootDirectory rootDirectory)
+ {
+ IEnumerable<Group> groups;
+
+ if (user != null)
+ {
+ groups = user.Groups.ToArray();
+ }
+ else
+ {
+ groups = new List<Group>() { UOW.Repo_Group.GetDefaultGroup(EnumDefaultGroups.Анонимные) };
+ }
+
+
+
+ if (groups.FirstOrDefault(e =>
+ e.Name == EnumDefaultGroups.Администраторы.ToString()) != null)
+ return new Permission()
+ {
+ DirectoryID = rootDirectory.ID,
+ RootDirectory = rootDirectory,
+ CanDownload = true,
+ CanUpload = true,
+ CanOpen = true
+ };
+
+
+ Permission res = new Permission()
+ {
+ DirectoryID = rootDirectory.ID,
+ RootDirectory = rootDirectory,
+ CanDownload = false,
+ CanUpload = false,
+ CanOpen = false
+ };
+
+ foreach (var elem in groups)
+ {
+ var dir_permission = elem.DirectoryPermissions.
+ Permissions[rootDirectory.ID];
+
+
+ if (!res.CanDownload && dir_permission.CanDownload)
+ res.CanDownload = true;
+ if (!res.CanOpen && dir_permission.CanOpen)
+ res.CanOpen = true;
+ if (!res.CanUpload && dir_permission.CanUpload)
+ res.CanUpload = true;
+ }
+
+ return res;
+ }
+ public Permission GetPermissionForDirectory(User user, SDirectory directory)
+ {
+ if (directory.IsRoot)
+ return GetPermissionForRootDirectory(user, (SRootDirectory)directory);
+ else
+ return GetPermissionForRootDirectory(user, directory.Root);
+ }
+
+ public bool CanDownload(User user, SDirectory directory)
+ {
+ return GetPermissionForDirectory(user, directory).CanDownload;
+ }
+
+ public bool CanUpload(User user, SDirectory directory)
+ {
+ return GetPermissionForDirectory(user, directory).CanUpload;
+ }
+
+ public bool CanOpen(User user, SDirectory directory)
+ {
+ return GetPermissionForDirectory(user, directory).CanOpen;
+ }
+
+ }
+}
FileServer/BLL/Services/ScanServices.cs 23(+19 -4)
diff --git a/FileServer/BLL/Services/ScanServices.cs b/FileServer/BLL/Services/ScanServices.cs
index a92ec84..f43dc13 100644
--- a/FileServer/BLL/Services/ScanServices.cs
+++ b/FileServer/BLL/Services/ScanServices.cs
@@ -14,6 +14,9 @@ using Model.Entities.Files;
namespace BLL.Services
{
+ /// <summary>
+ /// Сервис выполняет сканирование файловой системы
+ /// </summary>
public class ScanServices : BaseServices
{
public ScanServices(UOW uOW) : base(uOW) { }
@@ -43,7 +46,7 @@ namespace BLL.Services
{
await Task.Run(async () =>
{
- await RecursScanDirectoryAsync(UOW.Repo_rootDirectory.All.ToArray(), true);//, IsParalle);
+ await RecursScanDirectoryAsync(UOW.Repo_SRootDirectory.All.ToArray(), true);//, IsParalle);
});
}
@@ -120,12 +123,24 @@ namespace BLL.Services
foreach (var elem in db.files)
{
+ var file = fs.files.FirstOrDefault(e => e == elem.Name);
+
//Если файл из базы не найден в фс
- if (!fs.files.Contains(elem.Name))
+ if (file != null)
UOW.Repo_SFile.DeleteInList(elem);
//Если файл найден, то удаляем из списка файловой системы
else
+ {
+ //Проверка что размеры файла совпадают
+ var size = new FileInfo(file).Length;
+ if (elem.Size != size)
+ {
+ elem._Size = size;
+ UOW.Repo_SFile.Update(elem);
+ }
+
fs.files.Remove(elem.Name);
+ }
}
//Если в файловой системе есть файлы, не зафиксированные в базе
@@ -141,7 +156,7 @@ namespace BLL.Services
//Файл вносится в базу
FileInfo file = new FileInfo(Path.Combine(directory.PhysicalPath, elem));
- db.files.Add(UOW.Repo_SFile.Create(new SFile(directory, file.Name, file.Length)));
+ db.files.Add(UOW.Repo_SFile.Create(new SFile(directory, file.Name, file.Length, null)));
}
}
@@ -163,7 +178,7 @@ namespace BLL.Services
{
foreach (var elem in fs.dirs)
{
- db.dirs.Add(UOW.Repo_SDirectory.Create(new SDirectory(directory, elem)));
+ db.dirs.Add(UOW.Repo_SDirectory.Create(new SDirectory(directory, elem, null)));
}
}
diff --git a/FileServer/BLL/Services/UploadServices.cs b/FileServer/BLL/Services/UploadServices.cs
index f08a07b..af029c1 100644
--- a/FileServer/BLL/Services/UploadServices.cs
+++ b/FileServer/BLL/Services/UploadServices.cs
@@ -9,11 +9,14 @@ using BLL.Base;
using Model.UnitsOfWork;
using Model.Entities.Files.FS_Entities;
using Model.Entities.Files.Repo;
+using Model.Entities.Users;
namespace BLL.Services
{
-
+ /// <summary>
+ /// Сервис, контролирующий загрузку файлов
+ /// </summary>
public class UploadServices : BaseServices
{
const int ChunkSize = 102400;
@@ -24,16 +27,17 @@ namespace BLL.Services
Repo_SFileUpload = UOW.Repo_SFileUpload;
}
- public SFileUpload StartUpload(SDirectory directory, string Name, long size)
+ public SFileUpload StartUpload(SDirectory directory, string Name, long size, User user)
{
var res = Repo_SFileUpload.Create(
- new SFileUpload(directory, Name, size, ChunkSize));
+ new SFileUpload(directory, Name, size, ChunkSize, user));
return res;
}
public bool UploadChunk(SFileUpload project, byte[] data)
{
project.CurrentChunk = data;
+ project.LastChunkUploaded = DateTime.Now;
Repo_SFileUpload.Update(project);
@@ -55,11 +59,12 @@ namespace BLL.Services
private void Done(SFileUpload project)
{
var p = project.Parent;
+ var upload_user = project.User;
Repo_SFileUpload.DeleteInList(project);
UOW.Repo_SFile.Create(
- new SFile(p, project.Name, (long)project.Size));
+ new SFile(p, project.Name, (long)project.Size, upload_user));
}
}
FileServer/Console/Program.cs 24(+13 -11)
diff --git a/FileServer/Console/Program.cs b/FileServer/Console/Program.cs
index d887699..ce65d94 100644
--- a/FileServer/Console/Program.cs
+++ b/FileServer/Console/Program.cs
@@ -84,7 +84,7 @@ namespace Console
configurationServices.ReadConfiguration();
- var files = UOW.context.FS_Items.ToList();
+ //var files = UOW.context.FS_Items.ToList();
}
static void Test2()
@@ -102,18 +102,18 @@ namespace Console
UploadServices uploadServices = new UploadServices(UOW);
- var upload = uploadServices.StartUpload(
- UOW.Repo_rootDirectory.All.First(), UploadFile.Name, UploadFile.Length);
+ //var upload = uploadServices.StartUpload(
+ // UOW.Repo_rootDirectory.All.First(), UploadFile.Name, UploadFile.Length);
- using (var stream = new FileStream(UploadFile.FullName, FileMode.Open, FileAccess.Read))
- {
- byte[] data = new byte[upload.NextChunkSize];
+ //using (var stream = new FileStream(UploadFile.FullName, FileMode.Open, FileAccess.Read))
+ //{
+ // byte[] data = new byte[upload.NextChunkSize];
- do
- {
- stream.Read(data, (int)upload.UploadedSize, upload.NextChunkSize);
- } while (uploadServices.UploadChunk(upload, data));
- }
+ // do
+ // {
+ // stream.Read(data, (int)upload.UploadedSize, upload.NextChunkSize);
+ // } while (uploadServices.UploadChunk(upload, data));
+ //}
}
static void Main(string[] args)
@@ -123,6 +123,8 @@ namespace Console
var UOW = new UOW();
new ConfigurationServices(UOW).ReadConfiguration();
Task.WaitAll(new ScanServices(UOW).ScanAllDirs());
+
+ var dirs = UOW.Repo_SDirectory.All_List;
}
}
}
diff --git a/FileServer/Model/Entities/Base/Base_FS_Repository.cs b/FileServer/Model/Entities/Base/Base_FS_Repository.cs
index ddaef32..b8c631c 100644
--- a/FileServer/Model/Entities/Base/Base_FS_Repository.cs
+++ b/FileServer/Model/Entities/Base/Base_FS_Repository.cs
@@ -12,10 +12,54 @@ namespace Model.Entities.Base
{
public abstract class Base_FS_Repository<T> : BaseRepository<T> where T : FS_Item
{
- public DbSet<FS_Item> Set_Fs => context.Set<FS_Item>();
+ protected DbSet<FS_Item> Set_Fs => context.Set<FS_Item>();
+ protected readonly Enum_BaseDirectoryEntity RepoType;
+ public Base_FS_Repository(UOW UOW, Enum_BaseDirectoryEntity type) : base(UOW)
+ {
+ this.RepoType = type;
+ }
+
+ public override T GetFromDBNoChange(T elem)
+ {
+ using (var context = new Context())
+ {
+ var en = context.Set<FS_Item>().
+ FirstOrDefault(e => e.ID == elem.ID);//.
+ //Include(e => e.Parent).
+ //FirstOrDefault();
+
+ //context.Entry(en).Reference(e => e.Parent).Load();
+ var p = en.PhysicalPath;
+
+ return (T)en;
+ }
+ }
+
+ public override T Create(T elem)
+ {
+ if (elem.Type != RepoType)
+ throw Repo_Exception<T>.Factory(this, elem, Repo_Exceptions.FSRepo_TypeException);
+
+ return base.Create(elem);
+ }
+
+ public override void Update(T elem)
+ {
+ if (elem.Type != RepoType)
+ throw Repo_Exception<T>.Factory(this, elem, Repo_Exceptions.FSRepo_TypeException);
+
+ base.Update(elem);
+ }
+
+ public override void Delete(T elem)
+ {
+ if (elem.Type != RepoType)
+ throw Repo_Exception<T>.Factory(this, elem, Repo_Exceptions.FSRepo_TypeException);
+
+ base.Delete(elem);
+ }
- public Base_FS_Repository(Context context) : base(context) { }
/// <summary>
/// Рекурсия
@@ -23,23 +67,21 @@ namespace Model.Entities.Base
/// Обычно вызывается при сканировании ФС, когда папка/файл из базы не найдена в ФС
/// </summary>
/// <param name="elem"></param>
- public void DeleteInList(FS_Item elem)
+ public virtual void DeleteInList(FS_Item elem)
{
- foreach (var item in elem.Items)
- {
- _DeleteInList(item);
- }
-
- Set_Fs.Remove(elem);
+ _DeleteInList(elem);
context.SaveChanges();
}
private void _DeleteInList(FS_Item elem)
{
- foreach (var item in elem.Items)
+ var items = elem.Items;
+ //При foreach вылетает exception изменение коллекции
+ for (int i = 0; i < items.Count; i++)
{
- DeleteInList(item);
+ DeleteInList(items[i]);
}
+
Set_Fs.Remove(elem);
}
diff --git a/FileServer/Model/Entities/Base/BaseRepository.cs b/FileServer/Model/Entities/Base/BaseRepository.cs
index f1e0e7b..465a96b 100644
--- a/FileServer/Model/Entities/Base/BaseRepository.cs
+++ b/FileServer/Model/Entities/Base/BaseRepository.cs
@@ -5,6 +5,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Data.Entity;
+using System.Data.Entity.Infrastructure;
using Model.Entities.Base;
using Model.Entities.Files;
@@ -15,11 +16,14 @@ namespace Model.Entities.Base
{
public abstract class BaseRepository<T> where T : BaseEntity
{
- public readonly Context context;
- public BaseRepository(Context context)
+ protected readonly UOW UOW;
+ protected Context context => UOW.context;
+
+ public BaseRepository(UOW UOW)
{
- this.context = context;
+ //this.context = context;
+ this.UOW = UOW;
}
@@ -31,6 +35,29 @@ namespace Model.Entities.Base
public List<T> All_NoTrack_List => Set.AsNoTracking().ToList();
+ public DbEntityEntry<T> GetEntry(T elem)
+ {
+ return context.Entry<T>(elem);
+ }
+ /// <summary>
+ /// Используется чтобы запросить из базы сущность до модификации
+ /// Есть некоторын проблемы с подрузкой связанных свойств см. Base_FS_Repository
+ /// Общий вопрос: каким образом получить сущность до изменений (С запросом или без запроса к базе)?
+ /// Context Entry позволяет отследить список измененнных свойсв связанных с базой, но не вычисляемых свойств.
+ /// </summary>
+ /// <param name="elem"></param>
+ /// <returns></returns>
+ public virtual T GetFromDBNoChange(T elem)
+ {
+ using (var context = new Context())
+ {
+ return context.Set<T>().
+ FirstOrDefault(e => e.ID == elem.ID);
+ }
+ }
+
+
+
protected abstract void Validation_Create(T elem);
public virtual T Create(T elem)
{
@@ -45,15 +72,15 @@ namespace Model.Entities.Base
protected abstract void Validation_Update(T old, T elem);
public virtual void Update(T elem)
- {
- var change_entity = Set.FirstOrDefault(e => e.ID == elem.ID);
+ {
+ var change_entity = GetFromDBNoChange(elem);
if (change_entity == null)
throw new Exception("old value not found");
Validation_Update(change_entity, elem);
- context.Entry(change_entity).CurrentValues.SetValues(elem);
+
context.SaveChanges();
}
diff --git a/FileServer/Model/Entities/Base/Repo_Exception.cs b/FileServer/Model/Entities/Base/Repo_Exception.cs
index 13e2943..1a9a8d2 100644
--- a/FileServer/Model/Entities/Base/Repo_Exception.cs
+++ b/FileServer/Model/Entities/Base/Repo_Exception.cs
@@ -45,7 +45,8 @@ namespace Model.Entities.Base
Elem_items_not_empty,
Name_cant_change,
File_not_found,
- Size_not_equils
+ Size_not_equils,
+ FSRepo_TypeException
}
#endregion
diff --git a/FileServer/Model/Entities/Files/FS_Entities/SDirectory.cs b/FileServer/Model/Entities/Files/FS_Entities/SDirectory.cs
index 0e0fd90..3d69ccd 100644
--- a/FileServer/Model/Entities/Files/FS_Entities/SDirectory.cs
+++ b/FileServer/Model/Entities/Files/FS_Entities/SDirectory.cs
@@ -9,6 +9,7 @@ using System.Numerics;
using System.ComponentModel.DataAnnotations.Schema;
using Model.Entities.Base;
+using Model.Entities.Users;
namespace Model.Entities.Files.FS_Entities
{
@@ -32,7 +33,14 @@ namespace Model.Entities.Files.FS_Entities
[Obsolete]
public SDirectory() { }
- public SDirectory(SDirectory parent, string Name) : base(Enum_BaseDirectoryEntity.Directory, parent, Name) { }
- protected SDirectory(Enum_BaseDirectoryEntity type, SDirectory parent, string Name) : base(type, parent, Name) { }
+ public SDirectory(SDirectory parent, string Name, User user) : base(Enum_BaseDirectoryEntity.Directory, parent, Name, user) { }
+
+ /// <summary>
+ /// For SRootDirectory
+ /// </summary>
+ /// <param name="type"></param>
+ /// <param name="parent"></param>
+ /// <param name="Name"></param>
+ protected SDirectory(Enum_BaseDirectoryEntity type, SDirectory parent, string Name) : base(type, parent, Name, null) { }
}
}
diff --git a/FileServer/Model/Entities/Files/FS_Entities/SFile.cs b/FileServer/Model/Entities/Files/FS_Entities/SFile.cs
index 783e334..3dd271f 100644
--- a/FileServer/Model/Entities/Files/FS_Entities/SFile.cs
+++ b/FileServer/Model/Entities/Files/FS_Entities/SFile.cs
@@ -6,9 +6,12 @@ using System.Threading.Tasks;
using System.IO;
using System.ComponentModel.DataAnnotations.Schema;
+using System.Numerics;
+
using Model.Entities.Base;
-using System.Numerics;
+using Model.Entities.Users;
+
namespace Model.Entities.Files.FS_Entities
{
@@ -42,11 +45,11 @@ namespace Model.Entities.Files.FS_Entities
[Obsolete]
public SFile() { }
- public SFile(SDirectory parent, string Name, long size) : base(Enum_BaseDirectoryEntity.File, parent, Name)
+ public SFile(SDirectory parent, string Name, long size, User user) : base(Enum_BaseDirectoryEntity.File, parent, Name, user)
{
this._Size = size;
}
- protected SFile(Enum_BaseDirectoryEntity type, SDirectory parent, string Name, long size) : base(type, parent, Name)
+ protected SFile(Enum_BaseDirectoryEntity type, SDirectory parent, string Name, long size, User user) : base(type, parent, Name, user)
{
this._Size = size;
}
diff --git a/FileServer/Model/Entities/Files/FS_Entities/SFileUpload.cs b/FileServer/Model/Entities/Files/FS_Entities/SFileUpload.cs
index 1a915eb..68e2bbf 100644
--- a/FileServer/Model/Entities/Files/FS_Entities/SFileUpload.cs
+++ b/FileServer/Model/Entities/Files/FS_Entities/SFileUpload.cs
@@ -9,6 +9,7 @@ using System.ComponentModel.DataAnnotations.Schema;
using Model.Entities.Base;
using Model.Entities.Files.FS_Entities;
+using Model.Entities.Users;
namespace Model.Entities.Files.FS_Entities
{
@@ -42,7 +43,7 @@ namespace Model.Entities.Files.FS_Entities
public SFileUpload() { }
- public SFileUpload(SDirectory parent, string name, long size, int ChunkSize) : base(Enum_BaseDirectoryEntity.UploadFile, parent, name, size)
+ public SFileUpload(SDirectory parent, string name, long size, int ChunkSize, User user) : base(Enum_BaseDirectoryEntity.UploadFile, parent, name, size, user)
{
this.ChunkSize = ChunkSize;
}
diff --git a/FileServer/Model/Entities/Files/FS_Entities/SRootDirectory.cs b/FileServer/Model/Entities/Files/FS_Entities/SRootDirectory.cs
index d1644e5..252c770 100644
--- a/FileServer/Model/Entities/Files/FS_Entities/SRootDirectory.cs
+++ b/FileServer/Model/Entities/Files/FS_Entities/SRootDirectory.cs
@@ -13,45 +13,7 @@ using Model.Entities.Users;
namespace Model.Entities.Files.FS_Entities
{
- public class Permission
- {
- public int GroupID { set; get; }
- [XmlIgnore]
- public Group Group { set; get; }
-
- public bool CanRead { set; get; }
- public bool CanWrite { set; get; }
- public bool CanOpen { set; get; }
- }
-
- public class DirectoryPermissions
- {
- public List<Permission> Permissions { private set; get; } = new List<Permission>();
-
- public string Export()
- {
- XmlSerializer formatter = new XmlSerializer(typeof(Permission[]));
-
- using (StringWriter wr = new StringWriter())
- {
- formatter.Serialize(wr, Permissions.ToArray());
- return wr.ToString();
- }
- }
- public void Import(string val)
- {
- XmlSerializer formatter = new XmlSerializer(typeof(Permission[]));
-
- using (StringReader rd = new StringReader(val))
- {
- Permissions = new List<Permission>(
- (Permission[])formatter.Deserialize(rd));
- }
- }
- }
-
-
-
+
public class SRootDirectory : SDirectory
{
/// <summary>
@@ -60,15 +22,6 @@ namespace Model.Entities.Files.FS_Entities
public string _PhysicalPath { set; get; }
- [Column("XML_Permissions")]
- public string XML_Permissions
- {
- set => DirectoryPermissions.Import(value);
- get => DirectoryPermissions.Export();
- }
- public DirectoryPermissions DirectoryPermissions = new DirectoryPermissions();
-
-
public override string PhysicalPath => _PhysicalPath;
public override string LogicPath => Path.Combine("\\", Name);
FileServer/Model/Entities/Files/FS_Item.cs 13(+10 -3)
diff --git a/FileServer/Model/Entities/Files/FS_Item.cs b/FileServer/Model/Entities/Files/FS_Item.cs
index 95d4da5..fc46620 100644
--- a/FileServer/Model/Entities/Files/FS_Item.cs
+++ b/FileServer/Model/Entities/Files/FS_Item.cs
@@ -12,6 +12,7 @@ using System.ComponentModel.DataAnnotations;
using Model.Entities.Base;
using Model.Entities.Files.FS_Entities;
+using Model.Entities.Users;
namespace Model.Entities.Files
{
@@ -50,6 +51,11 @@ namespace Model.Entities.Files
public string Name { private set; get; }
+ [ForeignKey("User")]
+ public int? UserID { set; get; }
+ public virtual User User { private set; get; }
+
+
/// <summary>
/// Коренная папка для данного элемента
/// </summary>
@@ -65,7 +71,7 @@ namespace Model.Entities.Files
[ForeignKey("Parent")]
public int? Parent_ID { private set; get; }
//[InverseProperty("")]
- public virtual SDirectory Parent { private set; get; }
+ public virtual SDirectory Parent { set; get; }
/// <summary>
/// Содержимое папки
@@ -115,7 +121,7 @@ namespace Model.Entities.Files
[NotMapped]
- public IEnumerable<SFile> Files { get { return (IEnumerable<SFile>)Items.Where(e => e.Type == Enum_BaseDirectoryEntity.File).Cast<SFile>(); } }
+ public IEnumerable<SFile> Files { get { return Items.Where(e => e.Type == Enum_BaseDirectoryEntity.File).Cast<SFile>(); } }
[NotMapped]
public IEnumerable<SDirectory> Directories => Items.Where(e => e.Type == Enum_BaseDirectoryEntity.Directory).Cast<SDirectory>();
[NotMapped]
@@ -132,10 +138,11 @@ namespace Model.Entities.Files
[Obsolete]
public FS_Item() { }
- protected FS_Item(Enum_BaseDirectoryEntity type, SDirectory parent, string Name)
+ protected FS_Item(Enum_BaseDirectoryEntity type, SDirectory parent, string Name, User user)
{
this.Type = type;
this.Parent = parent;
+ this.User = user;
if (Parent != null)
{
Root_ID = (parent.IsRoot) ? parent.ID : parent.Root_ID;
diff --git a/FileServer/Model/Entities/Files/Repo/Repo_SDirectory.cs b/FileServer/Model/Entities/Files/Repo/Repo_SDirectory.cs
index 8641ea0..a896500 100644
--- a/FileServer/Model/Entities/Files/Repo/Repo_SDirectory.cs
+++ b/FileServer/Model/Entities/Files/Repo/Repo_SDirectory.cs
@@ -14,7 +14,7 @@ namespace Model.Entities.Files.Repo
{
public class Repo_SDirectory : Base_FS_Repository<SDirectory>
{
- public Repo_SDirectory(Context context) : base(context) { }
+ public Repo_SDirectory(UOW uOW) : base(uOW, Enum_BaseDirectoryEntity.Directory) { }
protected override void Validation_Create(SDirectory elem)
{
@@ -24,6 +24,9 @@ namespace Model.Entities.Files.Repo
if (elem.Parent == null)
throw Repo_Exception<SDirectory>.Factory(this, elem, Repo_Exceptions.Parent_is_null);
+ if (elem.Parent.ID == elem.ID)
+ throw new Exception();
+
if (elem.Parent.ContainsName(elem))
throw Repo_Exception<SDirectory>.Factory(this, elem, Repo_Exceptions.Name_already_exists_in_parents);
@@ -42,10 +45,12 @@ namespace Model.Entities.Files.Repo
if (elem.Parent == null)
throw Repo_Exception<SDirectory>.Factory(this, elem, Repo_Exceptions.Parent_is_null);
- if (old.Name != elem.Name)
+ if (elem.Parent.ID == elem.ID)
+ throw new Exception();
+
+ if (old.Parent != elem.Parent)
{
if (elem.Parent.ContainsName(elem))
- if (elem.Parent.ContainsName(elem))
throw Repo_Exception<SDirectory>.Factory(this, elem, Repo_Exceptions.Name_already_exists_in_parents);
try
diff --git a/FileServer/Model/Entities/Files/Repo/Repo_SFile.cs b/FileServer/Model/Entities/Files/Repo/Repo_SFile.cs
index 7918fb5..8bf8723 100644
--- a/FileServer/Model/Entities/Files/Repo/Repo_SFile.cs
+++ b/FileServer/Model/Entities/Files/Repo/Repo_SFile.cs
@@ -14,7 +14,7 @@ namespace Model.Entities.Files.Repo
{
public class Repo_SFile : Base_FS_Repository<SFile>
{
- public Repo_SFile(Context context) : base(context) { }
+ public Repo_SFile(UOW UOW) : base(UOW, Enum_BaseDirectoryEntity.File) { }
protected override void Validation_Create(SFile elem)
@@ -37,13 +37,28 @@ namespace Model.Entities.Files.Repo
if (elem.Parent == null)
throw new Exception();
- if (old.Name != elem.Name)
+ //Перемещение
+ if (old.Parent != elem.Parent)
{
+ //В данной папке уже есть элемент с таким именем
if (elem.Parent.ContainsName(elem))
throw new Exception();
- File.Move(old.PhysicalPath, elem.PhysicalPath);
+ try
+ {
+ File.Move(old.PhysicalPath, elem.PhysicalPath);
+ }
+ catch (Exception ex)
+ {
+ throw ex;
+ }
}
+
+ //if (old.Name != elem.Name)
+ //{
+ // if (elem.Parent.ContainsName(elem))
+ // throw new Exception();
+ //}
}
protected override void Validation_Delete(SFile elem)
diff --git a/FileServer/Model/Entities/Files/Repo/Repo_SFileUpload.cs b/FileServer/Model/Entities/Files/Repo/Repo_SFileUpload.cs
index 0bee131..121da22 100644
--- a/FileServer/Model/Entities/Files/Repo/Repo_SFileUpload.cs
+++ b/FileServer/Model/Entities/Files/Repo/Repo_SFileUpload.cs
@@ -15,7 +15,7 @@ namespace Model.Entities.Files.Repo
public class Repo_SFileUpload : Base_FS_Repository<SFileUpload>
{
- public Repo_SFileUpload(Context context) : base(context) { }
+ public Repo_SFileUpload(UOW UOW) : base(UOW, Enum_BaseDirectoryEntity.UploadFile) { }
protected override void Validation_Create(SFileUpload elem)
{
diff --git a/FileServer/Model/Entities/Files/Repo/Repo_SRootDirectory.cs b/FileServer/Model/Entities/Files/Repo/Repo_SRootDirectory.cs
index c2a595d..392fb74 100644
--- a/FileServer/Model/Entities/Files/Repo/Repo_SRootDirectory.cs
+++ b/FileServer/Model/Entities/Files/Repo/Repo_SRootDirectory.cs
@@ -16,7 +16,7 @@ namespace Model.Entities.Files.Repo
public class Repo_SRootDirectory : Base_FS_Repository<SRootDirectory>
{
- public Repo_SRootDirectory(Context context) : base(context) { }
+ public Repo_SRootDirectory(UOW UOW) : base(UOW, Enum_BaseDirectoryEntity.RootDirectory) { }
protected override void Validation_Create(SRootDirectory elem)
@@ -28,7 +28,7 @@ namespace Model.Entities.Files.Repo
throw Repo_Exception<SRootDirectory>.Factory(this, elem, Repo_Exceptions.Parent_is_null);
if (All.FirstOrDefault(e => e.Name == elem.Name) != null)
- throw new Repo_Exception<SRootDirectory>(this, elem, "Root directory name already exist");
+ throw Repo_Exception<SRootDirectory>.Factory(this, elem, Repo_Exceptions.Name_already_exists_in_parents);
try
{
@@ -39,25 +39,44 @@ namespace Model.Entities.Files.Repo
throw ex;
}
}
-
- protected override void Validation_Update(SRootDirectory old, SRootDirectory elem)
+ public override SRootDirectory Create(SRootDirectory elem)
{
- if (elem.Parent != null)
- throw new Exception();
+ var dir = base.Create(elem);
- if (old.Name != elem.Name && All.FirstOrDefault(e => e.Name == elem.Name) != null)
- {
- throw new Exception();
- }
-
- try
- {
- Directory.Move(old.PhysicalPath, elem.PhysicalPath);
- }
- catch (Exception ex)
+ var groups = UOW.Repo_Group.All_List;
+ foreach (var e in groups)
{
- throw ex;
+ var permission = (e.Name == EnumDefaultGroups.Администраторы.ToString())
+ ? Permission.Factory_AllPermission(dir)
+ : Permission.Factory_NoPermission(dir);
+
+ e.DirectoryPermissions.Permissions.Add(dir.ID, permission);
+ UOW.Repo_Group.Update(e);
}
+
+ return dir;
+ }
+
+ protected override void Validation_Update(SRootDirectory old, SRootDirectory elem)
+ {
+ throw new Exception();
+
+ //if (elem.Parent != null)
+ // throw new Exception();
+
+ //if (old.Name != elem.Name && All.FirstOrDefault(e => e.Name == elem.Name) != null)
+ //{
+ // throw new Exception();
+ //}
+
+ //try
+ //{
+ // Directory.Move(old.PhysicalPath, elem.PhysicalPath);
+ //}
+ //catch (Exception ex)
+ //{
+ // throw ex;
+ //}
}
protected override void Validation_Delete(SRootDirectory elem)
@@ -74,23 +93,31 @@ namespace Model.Entities.Files.Repo
throw ex;
}
}
- public void DeleteInList()
+ public override void Delete(SRootDirectory elem)
{
+ var id = elem.ID;
+ base.Delete(elem);
+ var groups = UOW.Repo_Group.All_List;
+ foreach (var e in groups)
+ {
+ e.DirectoryPermissions.Permissions.Remove(id);
+ UOW.Repo_Group.Update(e);
+ }
}
- public void UploadGroups(DirectoryPermissions permissions)
+ public override void DeleteInList(FS_Item elem)
{
- var Group_ID = permissions.Permissions.
- Select(e => e.GroupID);
+ var id = elem.ID;
+ base.DeleteInList(elem);
- //Select Groups from groups repo
- IEnumerable<Group> groups = context.Groups.Where(e => Group_ID.Contains(e.ID));
-
- foreach (var elem in permissions.Permissions)
- elem.Group = groups.
- FirstOrDefault(e => e.ID == elem.GroupID);
+ var groups = UOW.Repo_Group.All_List;
+ foreach (var e in groups)
+ {
+ e.DirectoryPermissions.Permissions.Remove(id);
+ UOW.Repo_Group.Update(e);
+ }
}
}
FileServer/Model/Entities/Users/Group.cs 97(+80 -17)
diff --git a/FileServer/Model/Entities/Users/Group.cs b/FileServer/Model/Entities/Users/Group.cs
index 9b40872..b5ca3b3 100644
--- a/FileServer/Model/Entities/Users/Group.cs
+++ b/FileServer/Model/Entities/Users/Group.cs
@@ -5,6 +5,9 @@ using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations.Schema;
+using System.ComponentModel.DataAnnotations;
+using System.IO;
+using System.Xml.Serialization;
using Model.Entities.Base;
using Model.Entities.Files.FS_Entities;
@@ -12,32 +15,92 @@ using Model.Entities.Files.FS_Entities;
namespace Model.Entities.Users
{
- //public class Permission
- //{
- // public bool Accesse { set; get; }
- // public bool Read { set; get; }
- // public bool Write { set; get; }
- //}
+ [Serializable]
+ public class Permission
+ {
+ public int DirectoryID { set; get; }
+ [XmlIgnore]
+ public SRootDirectory RootDirectory { set; get; }
+
+ public bool CanDownload { set; get; }
+ public bool CanUpload { set; get; }
+ public bool CanOpen { set; get; }
+
- //public class PermissionsManager
- //{
- // List<Permission> Permissions = new List<Permission>();
+ public static Permission Factory_AllPermission(SRootDirectory directory)
+ {
+ return new Permission()
+ {
+ DirectoryID = directory.ID,
+ RootDirectory = directory,
+ CanDownload = true,
+ CanOpen = true,
+ CanUpload = true
+ };
+ }
- // [NonSerialized]
- // List<SRootDirectory> sRootDirectories = new List<SRootDirectory>();
- // List<int> sRootDirectories_ID = new List<int>();
+ public static Permission Factory_NoPermission(SRootDirectory directory)
+ {
+ return new Permission()
+ {
+ DirectoryID = directory.ID,
+ RootDirectory = directory,
+ CanDownload = false,
+ CanOpen = false,
+ CanUpload = false
+ };
+ }
- //}
+ }
+
+ [Serializable]
+ public class DirectoryPermissions
+ {
+ /// <summary>
+ /// key - GroupID
+ /// </summary>
+ public Dictionary<int, Permission> Permissions { private set; get; } = new Dictionary<int, Permission>();
+ public string Export()
+ {
+ XmlSerializer formatter = new XmlSerializer(typeof(Permission[]));
+
+ using (StringWriter wr = new StringWriter())
+ {
+ formatter.Serialize(wr, Permissions.Values.ToArray());
+ return wr.ToString();
+ }
+ }
+ public void Import(string val)
+ {
+ XmlSerializer formatter = new XmlSerializer(typeof(Permission[]));
+
+ using (StringReader rd = new StringReader(val))
+ {
+ var data = (Permission[])formatter.Deserialize(rd);
+ Permissions.Clear();
+ foreach (var elem in data)
+ Permissions.Add(elem.DirectoryID, elem);
+ }
+ }
+
+ }
public class Group : BaseEntity
{
+ [Required]
public string Name { set; get; }
- //[Column("XML_Permissions")]
- //public string XML_Permissions { set; get; }
- [InverseProperty("Groups")]
- public virtual IEnumerable<User> Users { set; get; }
+ public virtual List<User> Users { set; get; } = new List<User>();
+
+ [NotMapped]
+ public DirectoryPermissions DirectoryPermissions { private set; get; } = new DirectoryPermissions();
+ [Column("XML_Permissions")]
+ public string XML_Permissions
+ {
+ set => DirectoryPermissions.Import(value);
+ get => DirectoryPermissions.Export();
+ }
}
}
diff --git a/FileServer/Model/Entities/Users/Repo/Repo_Group.cs b/FileServer/Model/Entities/Users/Repo/Repo_Group.cs
index 0e30899..3b66aa5 100644
--- a/FileServer/Model/Entities/Users/Repo/Repo_Group.cs
+++ b/FileServer/Model/Entities/Users/Repo/Repo_Group.cs
@@ -4,6 +4,8 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
+
+using Model.Entities.Files.Repo;
using Model.Entities.Base;
using Model.UnitsOfWork;
@@ -21,12 +23,14 @@ namespace Model.Entities.Users
{
public readonly static string[] DefaultGroupsNames = new string[]
{
- EnumDefaultGroups.Администраторы.ToString(),
- EnumDefaultGroups.Модераторы.ToString(),
- EnumDefaultGroups.Пользователи.ToString(),
- EnumDefaultGroups.Анонимные.ToString()
+ EnumDefaultGroups.Администраторы.ToString(),
+ EnumDefaultGroups.Модераторы.ToString(),
+ EnumDefaultGroups.Пользователи.ToString(),
+ EnumDefaultGroups.Анонимные.ToString()
};
+ public Repo_Group(UOW UOW) : base(UOW) { }
+
public Group GetDefaultGroup(EnumDefaultGroups en)
{
var group = All.FirstOrDefault(e => e.Name == en.ToString());
@@ -39,27 +43,46 @@ namespace Model.Entities.Users
return group;
}
-
- public Repo_Group(Context context) : base(context) { }
-
protected override void Validation_Create(Group elem)
{
if (All_NoTrack.FirstOrDefault(e => e.Name == elem.Name) != null)
throw new Exception();
}
+ public override Group Create(Group elem)
+ {
+ UOW.Repo_SRootDirectory.All_NoTrack_List.ForEach(e =>
+ {
+ elem.DirectoryPermissions.Permissions.Add(e.ID, Permission.Factory_NoPermission(e));
+ });
+
+ return base.Create(elem);
+ }
+
+ protected override void Validation_Update(Group old, Group elem)
+ {
+ //Изменение названия
+ if (old.Name != elem.Name)
+ {
+ //Не менять название дефолтных групп
+ if (DefaultGroupsNames.Contains(elem.Name))
+ throw new Exception();
+
+ //Группа с таким именем уже существует
+ if (All_NoTrack.FirstOrDefault(e => e.Name == elem.Name) != null)
+ throw new Exception();
+ }
+ }
protected override void Validation_Delete(Group elem)
{
if (DefaultGroupsNames.Contains(elem.Name))
throw new Exception();
- }
- protected override void Validation_Update(Group old, Group elem)
- {
- throw new NotImplementedException();
+ elem.Users.Clear();
}
+
}
}
diff --git a/FileServer/Model/Entities/Users/Repo/Repo_User.cs b/FileServer/Model/Entities/Users/Repo/Repo_User.cs
index fec2d2b..c3ea78a 100644
--- a/FileServer/Model/Entities/Users/Repo/Repo_User.cs
+++ b/FileServer/Model/Entities/Users/Repo/Repo_User.cs
@@ -11,12 +11,15 @@ namespace Model.Entities.Users
{
public class Repo_User : BaseRepository<User>
{
- public Repo_User(Context context) : base(context) { }
+ public Repo_User(UOW UOW) : base(UOW) { }
protected override void Validation_Create(User elem)
{
if (All_NoTrack.FirstOrDefault(e => e.Login == elem.Login) != null)
- throw new Exception();
+ throw new Exception();
+
+ if (elem.Groups.Count() == 0)
+ throw new Exception();
}
protected override void Validation_Delete(User elem)
@@ -26,7 +29,8 @@ namespace Model.Entities.Users
protected override void Validation_Update(User old, User elem)
{
- throw new NotImplementedException();
+ if (All_NoTrack.FirstOrDefault(e => e.Login == elem.Login) != null)
+ throw new Exception();
}
}
}
FileServer/Model/Entities/Users/User.cs 21(+18 -3)
diff --git a/FileServer/Model/Entities/Users/User.cs b/FileServer/Model/Entities/Users/User.cs
index bfbe111..9ed95d7 100644
--- a/FileServer/Model/Entities/Users/User.cs
+++ b/FileServer/Model/Entities/Users/User.cs
@@ -5,21 +5,36 @@ using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations.Schema;
+using System.ComponentModel.DataAnnotations;
using Model.Entities.Base;
+using Model.Entities.Files;
namespace Model.Entities.Users
{
public class User : BaseEntity
{
- //[Index]
+ [Required]
public string Login { set; get; }
+ [Required]
public string Password { set; get; }
public bool IsActive { set; get; }
+ public bool IsAdmin
+ {
+ get
+ {
+ EnumDefaultGroups res;
+ return Groups.FirstOrDefault(e => Enum.TryParse<EnumDefaultGroups>(e.Name, out res)) != null;
+ }
+ }
- [InverseProperty("Users")]
- public virtual IEnumerable<Group> Groups { set; get; }
+
+
+
+ public virtual List<Group> Groups { set; get; } = new List<Group>();
+
+ public virtual List<FS_Item> FS_Items { set; get; } = new List<FS_Item>();
}
}
FileServer/Model/UnitsOfWork/UOW.cs 77(+68 -9)
diff --git a/FileServer/Model/UnitsOfWork/UOW.cs b/FileServer/Model/UnitsOfWork/UOW.cs
index 4605fb6..3c9ac69 100644
--- a/FileServer/Model/UnitsOfWork/UOW.cs
+++ b/FileServer/Model/UnitsOfWork/UOW.cs
@@ -16,11 +16,11 @@ namespace Model.UnitsOfWork
{
public readonly Context context = new Context();
- public readonly Repo_SRootDirectory Repo_rootDirectory;
+ public readonly Repo_SRootDirectory Repo_SRootDirectory;
public readonly Repo_SDirectory Repo_SDirectory;
public readonly Repo_SFile Repo_SFile;
public readonly Repo_SFileUpload Repo_SFileUpload;
-
+ public IQueryable<FS_Item> FS_Items => context.FS_Items;
public readonly Repo_User Repo_User;
public readonly Repo_Group Repo_Group;
@@ -37,14 +37,14 @@ namespace Model.UnitsOfWork
public UOW()
{
- Repo_rootDirectory = new Repo_SRootDirectory(context);
- Repo_SDirectory = new Repo_SDirectory(context);
- Repo_SFile = new Repo_SFile(context);
- Repo_SFileUpload = new Repo_SFileUpload(context);
-
- Repo_Group = new Repo_Group(context);
- Repo_User = new Repo_User(context);
+ Repo_Group = new Repo_Group(this);
+ Repo_User = new Repo_User(this);
+ Repo_SRootDirectory = new Repo_SRootDirectory(this);
+ Repo_SDirectory = new Repo_SDirectory(this);
+ Repo_SFile = new Repo_SFile(this);
+ Repo_SFileUpload = new Repo_SFileUpload(this);
+
if (Repo_User.All_NoTrack.Count() == 0)
{
@@ -60,5 +60,64 @@ namespace Model.UnitsOfWork
});
}
}
+
+
+ public FS_Item Create(FS_Item elem)
+ {
+ switch (elem.Type)
+ {
+ case Enum_BaseDirectoryEntity.File:
+ return Repo_SFile.Create((SFile)elem);
+ case Enum_BaseDirectoryEntity.Directory:
+ return Repo_SDirectory.Create((SDirectory)elem);
+ case Enum_BaseDirectoryEntity.RootDirectory:
+ return Repo_SRootDirectory.Create((SRootDirectory)elem);
+ case Enum_BaseDirectoryEntity.UploadFile:
+ return Repo_SFileUpload.Create((SFileUpload)elem);
+ default:
+ throw new Exception();
+ }
+ }
+ public void Update(FS_Item elem)
+ {
+ switch (elem.Type)
+ {
+ case Enum_BaseDirectoryEntity.File:
+ Repo_SFile.Update((SFile)elem);
+ return;
+ case Enum_BaseDirectoryEntity.Directory:
+ Repo_SDirectory.Update((SDirectory)elem);
+ return;
+ case Enum_BaseDirectoryEntity.RootDirectory:
+ Repo_SRootDirectory.Update((SRootDirectory)elem);
+ return;
+ case Enum_BaseDirectoryEntity.UploadFile:
+ Repo_SFileUpload.Update((SFileUpload)elem);
+ return;
+ default:
+ throw new Exception();
+ }
+ }
+ public void Delete(FS_Item elem)
+ {
+ switch (elem.Type)
+ {
+ case Enum_BaseDirectoryEntity.File:
+ Repo_SFile.Delete((SFile)elem);
+ return;
+ case Enum_BaseDirectoryEntity.Directory:
+ Repo_SDirectory.Delete((SDirectory)elem);
+ return;
+ case Enum_BaseDirectoryEntity.RootDirectory:
+ Repo_SRootDirectory.Delete((SRootDirectory)elem);
+ return;
+ case Enum_BaseDirectoryEntity.UploadFile:
+ Repo_SFileUpload.Delete((SFileUpload)elem);
+ return;
+ default:
+ throw new Exception();
+ }
+ }
+
}
}
diff --git a/FileServer/Model/ViewModel/Files/UploadBlob.cs b/FileServer/Model/ViewModel/Files/UploadBlob.cs
index 132b0da..3d7c13f 100644
--- a/FileServer/Model/ViewModel/Files/UploadBlob.cs
+++ b/FileServer/Model/ViewModel/Files/UploadBlob.cs
@@ -9,11 +9,11 @@ namespace Model.ViewModel.Files
public class UploadBlob
{
public int ID { set; get; }
- public string Data { set; get; }
+ public string chunk { set; get; }
public byte[] ToByte()
{
- var base64 = Data.Substring(@"data:application/octet-stream;base64,".Length);
+ var base64 = chunk.Substring(@"data:application/octet-stream;base64,".Length);
return Convert.FromBase64String(base64);
}
}
FileServer/Web/Controllers/API/ExplorerController.cs 132(+103 -29)
diff --git a/FileServer/Web/Controllers/API/ExplorerController.cs b/FileServer/Web/Controllers/API/ExplorerController.cs
index ce1a786..b8b2433 100644
--- a/FileServer/Web/Controllers/API/ExplorerController.cs
+++ b/FileServer/Web/Controllers/API/ExplorerController.cs
@@ -7,6 +7,7 @@ using System.Web.Mvc;
using System.IO;
using System.Threading.Tasks;
using System.Data.Entity;
+using System.Data;
using Web.Models.Base;
@@ -29,10 +30,10 @@ namespace Web.Controllers
public List<ViewModelItem> items = new List<ViewModelItem>();
- public ViewModelItems(bool Successe, string ResMessage)
+ public ViewModelItems(bool Successe, string ResMessage)
: base(Successe, ResMessage, "GetDirectoryItems") { }
- }
+ }
class ViewModelItem
{
public int ID { set; get; }
@@ -47,18 +48,21 @@ namespace Web.Controllers
: base(Successe, ResMessage, "DeleteFile") { }
}
- public class ExplorerController : BaseController
+ public class ExplorerController : BaseApiController
{
-
-
[HttpGet]
public JsonResult GetDirectoryItems(int ID)
{
+ ViewModelItems json;
+
if (ID == -1)
{
- var data = UOW.Repo_rootDirectory.All_NoTrack_List;
+ //#Data #Permission
+ //Выбрать корренные папки, к которым имеет доступ текущий пользователь
+ var data = UOW.Repo_SRootDirectory.All_NoTrack_List.
+ Where(e => permissionServices.CanOpen(CurrentUser, e)).ToList();
- var json = new ViewModelItems(true, "")
+ json = new ViewModelItems(true, "")
{
ParentID = -1,
LogicPath = @"\",
@@ -80,13 +84,27 @@ namespace Web.Controllers
}
else
{
- var data = UOW.context.FS_Items.
+ //#Data
+ var data = UOW.Repo_SDirectory.All_NoTrack.
Where(e => e.ID == ID).
- AsNoTracking().
+ Include(e => e.Root).
Include("_Items").
First();
- var json = new ViewModelItems(true, "")
+ //#Permission
+ if (!permissionServices.CanOpen(CurrentUser, data))
+ {
+ var json_PError = new ViewModelItems(false, "CanOpen permission error")
+ {
+ PermissionError = true
+ };
+
+ return Json(json_PError, JsonRequestBehavior.AllowGet);
+ }
+
+
+
+ json = new ViewModelItems(true, "")
{
ParentID = data.IsRoot ? -1 : data.Parent_ID.Value,
LogicPath = data.LogicPath,
@@ -115,7 +133,18 @@ namespace Web.Controllers
[HttpGet]
public FileResult GetFile(int ID)
{
- var file = UOW.Repo_SFile.All_NoTrack.FirstOrDefault(e => e.ID == ID);
+ //#Data
+ var file = UOW.Repo_SFile.All_NoTrack.
+ FirstOrDefault(e => e.ID == ID);
+
+ //#Permission
+ if (!permissionServices.CanDownload(CurrentUser, file.Root))
+ {
+ MemoryStream res = new MemoryStream();
+
+ return File(res, "application/octet-stream", "CanDownload permission error");
+ }
+
return File(file.Info.Open(FileMode.Open, FileAccess.Read), "application/octet-stream", file.Name);
}
@@ -123,43 +152,58 @@ namespace Web.Controllers
[HttpPost]
public JsonResult DeleteFile(int ID)
{
- try
- {
- var file = UOW.Repo_SFile.All.FirstOrDefault(e => e.ID == ID);
- UOW.Repo_SFile.Delete(file);
- }
- catch (Exception ex)
- {
- var json_err = new DeleteResult(false, ex.Message);
+ //try
+ //{
+ //#Data
+ var file = UOW.FS_Items.FirstOrDefault(e => e.ID == ID);
- return Json(json_err, JsonRequestBehavior.AllowGet);
- }
+ //#Permission
+ if (!permissionServices.CanUpload(CurrentUser, file.Root))
+ {
+ var json_PError = new ViewModelItems(false, "CanOpen permission error")
+ {
+ PermissionError = true
+ };
+
+ return Json(json_PError);
+ }
+
+ UOW.Delete(file);
+ //}
+ //catch (Exception ex)
+ //{
+ //var json_err = new DeleteResult(false, ex.Message);
+
+ //return Json(json_err, JsonRequestBehavior.AllowGet);
+ // }
var json = new DeleteResult(true, "");
- return Json(json, JsonRequestBehavior.AllowGet);
+ return Json(json);
}
+
+
[HttpGet]
public async Task<JsonResult> ScanDirectory(int ID)
{
if (ID == -1)
{
- var dir = UOW.Repo_rootDirectory.All_List;
+ var dir = UOW.Repo_SRootDirectory.All_List;
new ConfigurationServices(UOW).
ReadConfiguration();
await new ScanServices(UOW).ScanAllDirs();
}
else
{
- var dir = UOW.context.FS_Items.
+ var dir = UOW.Repo_SDirectory.All.
FirstOrDefault(e => e.ID == ID);
await new ScanServices(UOW).
RecursScanDirectoryAsync((SDirectory)dir, false);
- var items = UOW.context.FS_Items.
- FirstOrDefault(e => e.ID == ID).Items.ToList();
+ //var items = UOW.context.FS_Items.
+ // FirstOrDefault(e => e.ID == ID).Items.ToList();
}
@@ -167,15 +211,45 @@ namespace Web.Controllers
}
+
+ [HttpPost]
+ public JsonResult MoveElement(int ID, int NewParent)
+ {
+ //Перемещаемый элемент
+ var elem = UOW.FS_Items.FirstOrDefault(e => e.ID == ID);
+ //Папка в которую будет выполняться перемещение
+ var new_parent = UOW.Repo_SDirectory.All.FirstOrDefault(e => e.ID == NewParent);
+
+
+ if (permissionServices.CanUpload(CurrentUser, elem.Parent) &&
+ permissionServices.CanUpload(CurrentUser, new_parent))
+ {
+ return Json(false);
+ }
+
+ elem.Parent = new_parent;
+ UOW.Update(elem);
+
+ return Json(true);
+ }
+
+ [HttpPost]
public JsonResult CreateDirectory(int ParentID, string Name)
{
- var parent = UOW.context.FS_Items.
+ //#Data
+ var parent = UOW.Repo_SDirectory.All.
FirstOrDefault(e => e.ID == ParentID);
+ //#Permission
+ if (!permissionServices.CanUpload(CurrentUser, parent))
+ {
+ return Json(false);
+ }
+
UOW.Repo_SDirectory.
- Create(new SDirectory((SDirectory)parent, Name));
+ Create(new SDirectory((SDirectory)parent, Name, CurrentUser));
- return Json(true, JsonRequestBehavior.AllowGet);
+ return Json(true);
}
}
diff --git a/FileServer/Web/Controllers/API/TestController.cs b/FileServer/Web/Controllers/API/TestController.cs
new file mode 100644
index 0000000..928ceb9
--- /dev/null
+++ b/FileServer/Web/Controllers/API/TestController.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+using System.Web.Mvc;
+
+namespace Web.Controllers
+{
+
+ public class Entity
+ {
+ public int id { set; get; }
+ public string name { set; get; }
+ }
+
+ public class TestController : BaseApiController
+ {
+ [HttpPost]
+ public void Test(int id, string name)
+ {
+
+
+ }
+
+ //[HttpPost]
+ //public void Test()
+ //{
+ // var data = GetJson<Entity>();
+
+ // //using (var stream = HttpContext.Request.InputStream)
+ // //{
+ // // stream.read
+ // //}
+
+ // int a = 2;
+
+ //}
+ }
+}
\ No newline at end of file
diff --git a/FileServer/Web/Controllers/API/UploadFilesController.cs b/FileServer/Web/Controllers/API/UploadFilesController.cs
index c743eeb..5a4eb7a 100644
--- a/FileServer/Web/Controllers/API/UploadFilesController.cs
+++ b/FileServer/Web/Controllers/API/UploadFilesController.cs
@@ -6,6 +6,7 @@ using System.Web.Mvc;
using Model.Entities.Base;
using Model.Entities.Files.FS_Entities;
+using Model.Entities.Users;
using Model.UnitsOfWork;
using BLL.Services;
@@ -22,14 +23,8 @@ namespace Web.Controllers
}
- public class UploadFilesController : BaseController
+ public class UploadFilesController : BaseApiController
{
-
- //[HttpGet]
- //public ActionResult UploadPage()
- //{
- // return View();
- //}
[HttpPost]
public JsonResult StartUpload(int ParentID, string Name, long size)
@@ -38,8 +33,8 @@ namespace Web.Controllers
try
{
- var parent = UOW.context.FS_Items.FirstOrDefault(e => e.ID == ParentID);
- var proj = uploadServices.StartUpload((SDirectory)parent, Name, size);
+ var parent = UOW.Repo_SDirectory.All.FirstOrDefault(e => e.ID == ParentID);
+ var proj = uploadServices.StartUpload(parent, Name, size, CurrentUser);
ID = proj.ID;
}
@@ -59,16 +54,13 @@ namespace Web.Controllers
});
}
+
+
[HttpPost]
- public JsonResult UploadBlob()
+ public JsonResult UploadBlob(UploadBlob model)
{
- UploadBlob model = new UploadBlob();
-
try
{
- model.ID = int.Parse(Request.Form["ID"]);
- model.Data = Request.Unvalidated.Form["chunk"];
-
var prog = UOW.Repo_SFileUpload.All.
FirstOrDefault(e => e.ID == model.ID);
@@ -100,5 +92,6 @@ namespace Web.Controllers
return Json(true);
}
+
}
}
\ No newline at end of file
diff --git a/FileServer/Web/Controllers/API/UserController.cs b/FileServer/Web/Controllers/API/UserController.cs
index 92cad53..6e3124d 100644
--- a/FileServer/Web/Controllers/API/UserController.cs
+++ b/FileServer/Web/Controllers/API/UserController.cs
@@ -5,6 +5,7 @@ using System.Web;
using System.Web.Mvc;
using Model.Entities.Users;
+using Web.Models.Base;
namespace Web.Controllers
{
@@ -19,7 +20,7 @@ namespace Web.Controllers
}
- public class UserController : BaseController
+ public class UserController : BaseApiController
{
/// <summary>
@@ -29,7 +30,7 @@ namespace Web.Controllers
/// <param name="Password"></param>
/// <returns></returns>
///
- [HttpGet]
+ [HttpPost]
public JsonResult Auth(string Login, string Password)
{
var user = UOW.Repo_User.All_NoTrack.
@@ -60,6 +61,32 @@ namespace Web.Controllers
+ [HttpPost]
+ public JsonResult UserInfo()
+ {
+ if (CurrentUser != null)
+ {
+ return Json(new AuthResult()
+ {
+ Successe = true,
+
+ Token = CurrentUser.ID.ToString(),
+ UserName = CurrentUser.Login
+ }, JsonRequestBehavior.AllowGet);
+ }
+ else
+ {
+ return Json(new AuthResult()
+ {
+ Successe = false,
+ ResMsg = "Пользователь не найден или заблокирован",
+
+ Token = ""
+ }, JsonRequestBehavior.AllowGet);
+ }
+ }
+
+ [HttpPost]
public JsonResult CreateUser(string Login, string Password)
{
UOW.Repo_User.Create(new User()
@@ -77,5 +104,48 @@ namespace Web.Controllers
}
+ [HttpPost]
+ public JsonResult UserList()
+ {
+ UserListMode json;
+
+ if (CurrentUser.IsAdmin)
+ {
+ var data = UOW.Repo_User.All_NoTrack_List;
+ data.ForEach(e => e.Password = "");
+
+ json = new UserListMode(true)
+ {
+ Users = data
+ };
+
+ }
+ else
+ {
+ json = new UserListMode(false)
+ {
+ PermissionError = true,
+ ResMessage = "Need admin"
+ };
+ }
+
+ return Json(json);
+ }
+
+ [HttpPost]
+ public JsonResult UserList(List<User> users)
+ {
+
+ return Json(true);
+ }
+
+ public class UserListMode : BaseApiResult
+ {
+ public UserListMode(bool Successe) : base(Successe, "", "UserList") { }
+
+ public List<User> Users;
+ }
+
+
}
}
\ No newline at end of file
diff --git a/FileServer/Web/Controllers/Base/BaseApiController.cs b/FileServer/Web/Controllers/Base/BaseApiController.cs
new file mode 100644
index 0000000..fc95d7d
--- /dev/null
+++ b/FileServer/Web/Controllers/Base/BaseApiController.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+using System.Web.Mvc;
+
+using System.Web.Routing;
+using System.Web.Security;
+using System.Configuration;
+using System.IO;
+using System.Web.Script.Serialization;
+
+using BLL.Services;
+using Model.Entities.Users;
+
+namespace Web.Controllers
+{
+ public abstract class BaseApiController : BaseController
+ {
+ protected User CurrentUser { private set; get; }
+ protected readonly PermissionServices permissionServices;
+
+ /// <summary>
+ /// Http Post Json request
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <returns></returns>
+ protected T GetJson<T>()
+ {
+ using (var stream = HttpContext.Request.InputStream)
+ {
+ using (StreamReader rd = new StreamReader(stream))
+ {
+ return new JavaScriptSerializer().
+ Deserialize<T>(rd.ReadToEnd());
+ }
+ }
+ }
+
+
+ protected BaseApiController()
+ {
+ permissionServices = new PermissionServices(UOW);
+ }
+
+ protected override void Initialize(RequestContext requestContext)
+ {
+ base.Initialize(requestContext);
+
+ var cookie = HttpContext.Request.Cookies;
+
+ if (cookie.AllKeys.Contains("AuthToken"))
+ {
+ var auth = cookie.Get("AuthToken");
+
+ if (auth.Value.Count() != 0)
+ {
+ int ID = -1;
+
+ if (int.TryParse(auth.Value.First().ToString(), out ID))
+ {
+ CurrentUser = UOW.Repo_User.All.
+ FirstOrDefault(e => e.ID == ID);
+ }
+ }
+
+ }
+
+ }
+ }
+}
\ No newline at end of file
FileServer/Web/Global.asax.cs 5(+3 -2)
diff --git a/FileServer/Web/Global.asax.cs b/FileServer/Web/Global.asax.cs
index 2d1dfa9..1c5b04b 100644
--- a/FileServer/Web/Global.asax.cs
+++ b/FileServer/Web/Global.asax.cs
@@ -26,10 +26,11 @@ namespace Web
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
-
+ //Для обработки POST json запросов
+ ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());
//Очистить базу данных
- //new Context(true);
+ new Context(true);
var UOW = new UOW();
//Прочитать корневые папки
new ConfigurationServices(UOW).ReadConfiguration();
diff --git a/FileServer/Web/Models/Base/BaseApiResult.cs b/FileServer/Web/Models/Base/BaseApiResult.cs
index 46f992a..8f78432 100644
--- a/FileServer/Web/Models/Base/BaseApiResult.cs
+++ b/FileServer/Web/Models/Base/BaseApiResult.cs
@@ -11,6 +11,9 @@ namespace Web.Models.Base
public string ResMessage { set; get; }
public string ActionName { set; get; }
+
+ public bool PermissionError { set; get; }
+
public BaseApiResult(bool Successe, string ResMessage, string ActionName)
{
this.Successe = Successe;
diff --git a/FileServer/Web/Scripts/React/Controls/MenuControl.jsx b/FileServer/Web/Scripts/React/Controls/MenuControl.jsx
new file mode 100644
index 0000000..9f6f45c
--- /dev/null
+++ b/FileServer/Web/Scripts/React/Controls/MenuControl.jsx
@@ -0,0 +1,49 @@
+
+
+
+class MenuControl extends React.Component {
+
+ constructor(props) {
+ super(props);
+ console.log('MenuControl start');
+
+ //this.state = { counter: 0 };
+ this.userServices = new UserServices();
+
+ this.OnAuthChange = this.OnAuthChange.bind(this);
+ }
+
+ OnAuthChange() {
+ this.forceUpdate();
+ }
+
+ render() {
+ return (
+ <div>
+
+ <table>
+ <tr>
+ <th>Menu:</th>
+ <th>
+ <Link to={`/`}>
+ <button>FS</button>
+ </Link>
+ </th>
+
+ {this.userServices.IsAuth()
+ ?
+ <th>
+ <Link to={`/Admin`}>
+ <button>Admin</button>
+ </Link>
+ </th>
+ : ""
+ }
+ </tr>
+ </table>
+
+ </div>
+ );
+ }
+}
+
diff --git a/FileServer/Web/Scripts/React/Pages/AdministratorPage.jsx b/FileServer/Web/Scripts/React/Pages/AdministratorPage.jsx
new file mode 100644
index 0000000..cf54d76
--- /dev/null
+++ b/FileServer/Web/Scripts/React/Pages/AdministratorPage.jsx
@@ -0,0 +1,28 @@
+
+
+
+class AdministratorPage extends React.Component {
+
+ constructor(props) {
+ super(props);
+ console.log('AdministratorPage start');
+
+ }
+
+ OnAuthChange() {
+ this.refs.MenuControl.OnAuthChange();
+ }
+
+ render() {
+ return (
+ <div>
+ <hr />
+ <MenuControl ref="MenuControl"
+ ParentComponent={this}
+ />
+
+ </div>
+ );
+ }
+}
+
diff --git a/FileServer/Web/Scripts/React/Pages/FileExplorerPage.jsx b/FileServer/Web/Scripts/React/Pages/FileExplorerPage.jsx
new file mode 100644
index 0000000..5abf9aa
--- /dev/null
+++ b/FileServer/Web/Scripts/React/Pages/FileExplorerPage.jsx
@@ -0,0 +1,92 @@
+
+
+class FileExplorerPage extends React.Component {
+
+ constructor(props) {
+ super(props);
+ console.log('FileExplorerPage start');
+
+ //this.state = { counter: 0 };
+
+ this.OnAuthChange = this.OnAuthChange.bind(this);
+ this.OnItemsChange = this.OnItemsChange.bind(this);
+
+ this.SetID = this.SetID.bind(this);
+ this.GetID = this.GetID.bind(this);
+
+ //Получить ID из url
+ var Url_ID = new URL(window.location.href).
+ searchParams.get("ID");
+
+ if (Url_ID != null)
+ this.SetID(Url_ID);
+ //if (this.props.match != undefined
+ // && this.props.match.params != undefined
+ // && this.props.match.params.ID != undefined)
+ // this.SetID(this.props.match.params.ID);
+ //Получить id от родительского компонента
+ else
+ this.SetID(-1);
+ }
+
+
+ OnAuthChange() {
+ this.OnItemsChange();
+ this.refs.MenuControl.OnAuthChange();
+ }
+ OnItemsChange() {
+ this.refs.FileExplorerControl.
+ LoadDirectory();
+ }
+
+ SetID(val) {
+ this.CurrentID = val;
+ console.log(this.CurrentID);
+ }
+
+ GetID() {
+ return this.CurrentID;
+ }
+
+
+ render() {
+ return (
+ <div>
+ <hr />
+ <MenuControl ref="MenuControl"
+ ParentComponent={this}
+ />
+ <hr />
+ <UserControl ref="UserControl"
+ ParentComponent={this}
+ />
+
+ <hr />
+ <FileExplorerControl ref="FileExplorerControl"
+ ParentComponent={this}
+ ShoSelect={true}
+ />
+ <hr />
+ {this.GetID() != -1
+ ?
+ <div>
+ <ExplorerActionsControl ref="ExplorerActionsControl"
+ ParentComponent={this}
+ />
+ <hr />
+ <UploaderControl ref="UploaderControl"
+ ParentComponent={this}
+ />
+ <hr />
+ </div>
+ : ""
+ }
+ <BootstrapControl
+ />
+ <hr />
+
+ </div>
+ );
+ }
+}
+
diff --git a/FileServer/Web/Scripts/React/RouteSystem.jsx b/FileServer/Web/Scripts/React/RouteSystem.jsx
index 2835f46..a042675 100644
--- a/FileServer/Web/Scripts/React/RouteSystem.jsx
+++ b/FileServer/Web/Scripts/React/RouteSystem.jsx
@@ -6,14 +6,17 @@ class RouteSystem extends React.Component {
constructor(props) {
super(props);
console.log('RouteSystem start');
+
+
}
render() {
return (
<Router>
<Switch>
- <Route path="/" component={() => <App ID="-1" />} />
- <Route path="/?ID=:ID" component={App} />
+ <Route ref="CurrentPage" exact path="/" component={FileExplorerPage} />
+ <Route ref="CurrentPage" path="/?ID=:ID" component={FileExplorerPage} />
+ <Route ref="CurrentPage" path="/Admin" component={AdministratorPage} />
</Switch>
</Router>
);
diff --git a/FileServer/Web/Scripts/Services/FileExplorerServices.js b/FileServer/Web/Scripts/Services/FileExplorerServices.js
index fdcefc3..e8625fe 100644
--- a/FileServer/Web/Scripts/Services/FileExplorerServices.js
+++ b/FileServer/Web/Scripts/Services/FileExplorerServices.js
@@ -5,11 +5,11 @@ class FileExplorerServices {
this.URL_DirectoryItems = "/Explorer/GetDirectoryItems?ID=";
this.URL_ScanDirectory = "/Explorer/ScanDirectory?ID=";
- this.URL_Delete = "/Explorer/DeleteFile?ID=";
+ this.URL_Delete = "/Explorer/DeleteFile";
+ this.URL_Move = "/Explorer/MoveElement";
this.URL_Download = "/Explorer/GetFile?ID=";
this.URL_CreateDirectory = "/Explorer/CreateDirectory?";
-
}
@@ -19,28 +19,55 @@ class FileExplorerServices {
return fetch(
url,
{
- ID: ID
+ credentials: 'include'
})
}
async ScanDirectoryAsync(ID) {
let url = this.URL_ScanDirectory + ID;
- return fetch(url);
+ return fetch(url,
+ {
+ credentials: 'include'
+ }
+ );
}
-
async DeleteAsync(ID) {
- let url = this.URL_Delete + ID;
return fetch(
- url,
+ this.URL_Delete,
{
- method: 'POST'
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ credentials: 'include',
+ body: JSON.stringify({
+ ID: ID
+ })
}
)
}
+ async MoveAsync(elemID, directoryID) {
+ return fetch(
+ this.URL_Move,
+ {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ credentials: 'include',
+ body: JSON.stringify({
+ ID: elemID,
+ NewParent: directoryID
+ })
+ }
+ )
+ }
+
+
OpenDownload(ID) {
let url = this.URL_Download + ID;
@@ -48,10 +75,19 @@ class FileExplorerServices {
}
async CreateDirectoryAsync(dirname, id) {
- let url = this.URL_CreateDirectory+"ParentID = " + id
- + "&Name=" + dirname;
-
- return fetch(url);
+ return fetch(this.URL_CreateDirectory,
+ {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ credentials: 'include',
+ body: JSON.stringify({
+ ParentID: id,
+ Name: dirname
+ })
+ }
+ );
}
}
\ No newline at end of file
diff --git a/FileServer/Web/Scripts/Services/UploadServices.js b/FileServer/Web/Scripts/Services/UploadServices.js
index a930cb2..6b91660 100644
--- a/FileServer/Web/Scripts/Services/UploadServices.js
+++ b/FileServer/Web/Scripts/Services/UploadServices.js
@@ -46,21 +46,38 @@ class UploadServices {
//Сообщает серверу о начале загрузки файла, получает ID загрузки
async _StartUploadAsync() {
- return await $.post(this.URL_Start,
+ let res;
+
+ await fetch(
+ this.URL_Start,
{
- //ID папки
- 'ParentID': this._ParentID,
- //Имя файла
- 'Name': this._file.name,
- //Размеры
- 'Size': this._file.size
+ method: "Post",
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ //ID папки
+ ParentID: this._ParentID,
+ //Имя файла
+ Name: this._file.name,
+ //Размеры
+ Size: this._file.size
+ })
}
- );
+ ).then(function (response) {
+ return response.json();
+ }).then(function (data) {
+ res = data;
+ }.bind(this));
+
+ return res;
}
//Выполняет загрузка блока
async _UploadBlobAsync(bin_data, ChunkNumb) {
+ console.log("_UploadBlobAsync");
+
if (!this._ContinueDownload)
return { State: true };
@@ -68,18 +85,31 @@ class UploadServices {
if (bin_data != '') {
console.log(this.URL_Upload + ChunkNumb);
- var state = await $.post(this.URL_Upload,
+ let res;
+
+ await fetch(
+ this.URL_Upload,
{
- //ID загрузки
- 'ID': this.ID,
- //Кусок файла
- 'chunk': bin_data,
- //Номер куска
- //'ChunkNumb': ChunkNumb
+ method: "Post",
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ //ID загрузки
+ ID: this.ID,
+ //Кусок файла
+ chunk: bin_data
+ //Номер куска
+ //'ChunkNumb': ChunkNumb
+ })
}
- )
+ ).then(function (response) {
+ return response.json();
+ }).then(function (data) {
+ res = data;
+ }.bind(this));
- return state;
+ return res;
}
}
@@ -110,6 +140,7 @@ class UploadServices {
let bin_data = await this._ReadBlobAsync(pos, pos + this.upload_chunk_size);
let state = await this._UploadBlobAsync(bin_data, ChunkNumb);
+
if (!state.State) {
//alert('Загрузка прервана');
@@ -135,13 +166,22 @@ class UploadServices {
//Костыль задержка, чтобы асинхронный загрузчик точно прервал работу
//и не попытался получить доступ к ужаленному проекту загрузки
//Возможно зависит от размера блока
- setTimeout(function () {
- $.post(this.URL_Cansel,
+ setTimeout(function () {
+
+ fetch(
+ this.URL_Cansel,
{
- //Имя файла
- 'ID': this.ID,
+ method: "Post",
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ //ID файла
+ ID: this.ID,
+ })
}
- )
+ );
+
}.bind(this),
500);
}
diff --git a/FileServer/Web/Scripts/Services/UserServices.js b/FileServer/Web/Scripts/Services/UserServices.js
index cf8f52c..0713979 100644
--- a/FileServer/Web/Scripts/Services/UserServices.js
+++ b/FileServer/Web/Scripts/Services/UserServices.js
@@ -3,21 +3,37 @@ class UserServices {
constructor() {
- this.URL_Auth = "/User/Auth?";
+ this.URL_Auth = "/User/Auth";
+ this.URL_UserInfo = "/User/UserInfo"
+
+ this.AuthCoockieName = "AuthToken";
}
- Auth(login, password) {
+ AuthAsync(login, password) {
- let url = this.URL_Auth + "Login=" + login + "&Password=" + password;
+ return fetch(
+ this.URL_Auth,
+ {
+ method: "Post",
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ Login: login,
+ Password: password
+ })
+ });
+ }
+ GetUserInfoAsync() {
return fetch(
- url,
+ this.URL_UserInfo,
{
- //Login: login,
- //Password: password
+ method: "Post",
+ credentials: 'include'
});
}
@@ -36,11 +52,11 @@ class UserServices {
//Токен авторизации
GetTocken() {
- return this._getCookie("AuthToken");
+ return this._getCookie(this.AuthCoockieName);
}
//задать токен
SetTocken(val) {
- document.cookie = "AuthToken=" + val;
+ document.cookie = this.AuthCoockieName + "=" + val;
}
//Возвращает true если пользователь авторизован
diff --git a/FileServer/Web/Scripts/Tools/PromisesParallel.js b/FileServer/Web/Scripts/Tools/PromisesParallel.js
new file mode 100644
index 0000000..c68578e
--- /dev/null
+++ b/FileServer/Web/Scripts/Tools/PromisesParallel.js
@@ -0,0 +1,19 @@
+
+
+//Функция для облегчения работы с множественными запросами
+//Возможно есть более удобный вариант
+//Запросы => Ждем окончания => Вытащить json => Ждем окончания => Вернуть результат
+async function PromisesParallelAsync(promises) {
+
+ //Выполнить все запросы
+ var responses = await Promise.all(promises);
+
+ //Promise json
+ let json_promises = responses.map((e) => {
+ return e.json();
+ });
+
+ //Получить json результаты
+ return await Promise.all(json_promises);
+}
+
FileServer/Web/Views/Home/Index.cshtml 19(+12 -7)
diff --git a/FileServer/Web/Views/Home/Index.cshtml b/FileServer/Web/Views/Home/Index.cshtml
index 8bf9f77..fed28a7 100644
--- a/FileServer/Web/Views/Home/Index.cshtml
+++ b/FileServer/Web/Views/Home/Index.cshtml
@@ -39,31 +39,36 @@
@* BootstrapComponents *@
const ButtonToolbar = ReactBootstrap.ButtonToolbar;
const Button = ReactBootstrap.Button;
+ const Modal = ReactBootstrap.Modal;
+ const Form = ReactBootstrap.Form;
</script>
@* Application scripts *@
@Scripts.RenderFormat("<script type ='text/babel' src='{0}' defer></script>",
- "/Scripts/React/BootstrapControl.jsx?" + DateTime.Now,
+ "/Scripts/React/Controls/BootstrapControl.jsx?" + DateTime.Now,
"/Scripts/Services/UserServices.js?" + DateTime.Now,
- "/Scripts/React/UserControl.jsx?" + DateTime.Now,
+ "/Scripts/React/Controls/UserControl.jsx?" + DateTime.Now,
"/Scripts/Services/UploadServices.js?" + DateTime.Now,
- "/Scripts/React/UploaderControl.jsx?" + DateTime.Now,
+ "/Scripts/React/Controls/UploaderControl.jsx?" + DateTime.Now,
"/Scripts/Tools/Sort.js?" + DateTime.Now,
+ "/Scripts/Tools/PromisesParallel.js?" + DateTime.Now,
"/Scripts/Services/FileExplorerServices.js?" + DateTime.Now,
- "/Scripts/React/FileExplorerRow.jsx?" + DateTime.Now,
- "/Scripts/React/FileExplorerControl.jsx?" + DateTime.Now,
- "/Scripts/React/ExplorerActionsControl.jsx?" + DateTime.Now,
+ "/Scripts/React/Controls/FileExplorerRow.jsx?" + DateTime.Now,
+ "/Scripts/React/Controls/FileExplorerControl.jsx?" + DateTime.Now,
+ "/Scripts/React/Controls/ExplorerActionsControl.jsx?" + DateTime.Now,
+ "/Scripts/React/Controls/MenuControl.jsx?" + DateTime.Now,
- "/Scripts/React/App.jsx?" + DateTime.Now,
+ "/Scripts/React/Pages/FileExplorerPage.jsx?" + DateTime.Now,
+ "/Scripts/React/Pages/AdministratorPage.jsx?" + DateTime.Now,
"/Scripts/React/RouteSystem.jsx?" + DateTime.Now
)
}
FileServer/Web/Web.config 2(+1 -1)
diff --git a/FileServer/Web/Web.config b/FileServer/Web/Web.config
index e7310eb..ba2400b 100644
--- a/FileServer/Web/Web.config
+++ b/FileServer/Web/Web.config
@@ -20,7 +20,7 @@
<add key="webpages:Enabled" value="false" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
- <add key="WorkFolder" value="C:\Users\cccc1808\source\repos\FileServer\Console\bin\Debug\Dir1;C:\Users\cccc1808\source\repos\FileServer\Console\bin\Debug\Dir2" />
+ <add key="WorkFolder" value="D:\GIT\WebFileServer\FileServer\Console\bin\Debug\Dir1;D:\GIT\WebFileServer\FileServer\Console\bin\Debug\Dir2" />
</appSettings>
<!--
Описание изменений web.config см. по адресу http://go.microsoft.com/fwlink/?LinkId=235367.
FileServer/Web/Web.csproj 20(+14 -6)
diff --git a/FileServer/Web/Web.csproj b/FileServer/Web/Web.csproj
index 7ee4ecc..ecd8b24 100644
--- a/FileServer/Web/Web.csproj
+++ b/FileServer/Web/Web.csproj
@@ -142,6 +142,8 @@
<Compile Include="App_Start\RouteConfig.cs" />
<Compile Include="App_Start\WebApiConfig.cs" />
<Compile Include="BackgroundWorkers\GarbageUploadsWorker.cs" />
+ <Compile Include="Controllers\API\TestController.cs" />
+ <Compile Include="Controllers\Base\BaseApiController.cs" />
<Compile Include="Controllers\Base\BaseController.cs" />
<Compile Include="Controllers\API\ExplorerController.cs" />
<Compile Include="Controllers\HomeController.cs" />
@@ -198,8 +200,11 @@
<Content Include="Scripts\jquery.validate.min.js" />
<Content Include="Scripts\jquery.validate.unobtrusive.js" />
<Content Include="Scripts\jquery.validate.unobtrusive.min.js" />
- <Content Include="Scripts\React\BootstrapControl.jsx" />
- <Content Include="Scripts\React\ExplorerActionsControl.jsx" />
+ <Content Include="Scripts\React\Controls\BootstrapControl.jsx" />
+ <Content Include="Scripts\React\Controls\ExplorerActionsControl.jsx" />
+ <Content Include="Scripts\React\Controls\MenuControl.jsx" />
+ <Content Include="Scripts\React\Pages\AdministratorPage.jsx" />
+ <Content Include="Scripts\React\Pages\FileExplorerPage.jsx" />
<Content Include="Scripts\Services\FileExplorerServices.js" />
<Content Include="Scripts\Services\UserServices.js" />
<Content Include="Scripts\modernizr-2.8.3.js" />
@@ -208,12 +213,13 @@
<Content Include="Scripts\popper.js" />
<Content Include="Scripts\popper.min.js" />
<Content Include="Scripts\React\App.jsx" />
- <Content Include="Scripts\React\FileExplorerControl.jsx" />
- <Content Include="Scripts\React\FileExplorerRow.jsx" />
+ <Content Include="Scripts\React\Controls\FileExplorerControl.jsx" />
+ <Content Include="Scripts\React\Controls\FileExplorerRow.jsx" />
<Content Include="Scripts\React\RouteSystem.jsx" />
- <Content Include="Scripts\React\UploaderControl.jsx" />
- <Content Include="Scripts\React\UserControl.jsx" />
+ <Content Include="Scripts\React\Controls\UploaderControl.jsx" />
+ <Content Include="Scripts\React\Controls\UserControl.jsx" />
<Content Include="Scripts\Services\UploadServices.js" />
+ <Content Include="Scripts\Tools\PromisesParallel.js" />
<Content Include="Scripts\Tools\Sort.js" />
<Content Include="Scripts\umd\popper-utils.js" />
<Content Include="Scripts\umd\popper-utils.min.js" />
@@ -247,6 +253,8 @@
</ItemGroup>
<ItemGroup>
<Folder Include="App_Data\" />
+ <Folder Include="Views\BaseApi\" />
+ <Folder Include="Views\Test\" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
Диаграммы/Диаграмма1.graphml 650(+650 -0)
diff --git "a/\320\224\320\270\320\260\320\263\321\200\320\260\320\274\320\274\321\213/\320\224\320\270\320\260\320\263\321\200\320\260\320\274\320\274\320\2601.graphml" "b/\320\224\320\270\320\260\320\263\321\200\320\260\320\274\320\274\321\213/\320\224\320\270\320\260\320\263\321\200\320\260\320\274\320\274\320\2601.graphml"
new file mode 100644
index 0000000..90984d3
--- /dev/null
+++ "b/\320\224\320\270\320\260\320\263\321\200\320\260\320\274\320\274\321\213/\320\224\320\270\320\260\320\263\321\200\320\260\320\274\320\274\320\2601.graphml"
@@ -0,0 +1,650 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
+ <!--Created by yEd 3.16.2.1-->
+ <key attr.name="Description" attr.type="string" for="graph" id="d0"/>
+ <key for="port" id="d1" yfiles.type="portgraphics"/>
+ <key for="port" id="d2" yfiles.type="portgeometry"/>
+ <key for="port" id="d3" yfiles.type="portuserdata"/>
+ <key attr.name="url" attr.type="string" for="node" id="d4"/>
+ <key attr.name="description" attr.type="string" for="node" id="d5"/>
+ <key for="node" id="d6" yfiles.type="nodegraphics"/>
+ <key for="graphml" id="d7" yfiles.type="resources"/>
+ <key attr.name="url" attr.type="string" for="edge" id="d8"/>
+ <key attr.name="description" attr.type="string" for="edge" id="d9"/>
+ <key for="edge" id="d10" yfiles.type="edgegraphics"/>
+ <graph edgedefault="directed" id="G">
+ <data key="d0"/>
+ <node id="n0" yfiles.foldertype="group">
+ <data key="d6">
+ <y:ProxyAutoBoundsNode>
+ <y:Realizers active="0">
+ <y:GenericGroupNode configuration="DemoGroup">
+ <y:Geometry height="288.701171875" width="524.0919999999999" x="658.0" y="0.0"/>
+ <y:Fill color="#68B0E3" color2="#3C679B" transparent="false"/>
+ <y:BorderStyle color="#000000" type="line" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" borderDistance="0.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="tl" textColor="#FFFFFF" verticalTextPosition="bottom" visible="true" width="161.62890625" x="0.0" y="0.0">Структура хранения данных</y:NodeLabel>
+ <y:State autoResize="true" closed="false" closedHeight="50.0" closedWidth="50.0"/>
+ <y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
+ <y:BorderInsets bottom="0" bottomF="0.0" left="5" leftF="5.218953124999871" right="12" rightF="11.757476562499733" top="0" topF="0.0"/>
+ </y:GenericGroupNode>
+ <y:GenericGroupNode configuration="DemoGroup">
+ <y:Geometry height="50.0" width="50.0" x="658.0" y="0.0"/>
+ <y:Fill color="#68B0E3" color2="#3C679B" transparent="false"/>
+ <y:BorderStyle color="#000000" type="line" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" borderDistance="0.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="tl" textColor="#FFFFFF" verticalTextPosition="bottom" visible="true" width="10.673828125" x="0.0" y="0.0">5</y:NodeLabel>
+ <y:State autoResize="true" closed="true" closedHeight="50.0" closedWidth="50.0"/>
+ <y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
+ <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
+ </y:GenericGroupNode>
+ </y:Realizers>
+ </y:ProxyAutoBoundsNode>
+ </data>
+ <graph edgedefault="directed" id="n0:">
+ <node id="n0::n0">
+ <data key="d6">
+ <y:ShapeNode>
+ <y:Geometry height="30.0" width="30.0" x="678.2189531249999" y="173.701171875"/>
+ <y:Fill color="#FFCC00" transparent="false"/>
+ <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="27.337890625" x="1.3310546875" y="5.6494140625">Item<y:LabelModel>
+ <y:SmartNodeLabelModel distance="4.0"/>
+ </y:LabelModel>
+ <y:ModelParameter>
+ <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
+ </y:ModelParameter>
+ </y:NodeLabel>
+ <y:Shape type="ellipse"/>
+ </y:ShapeNode>
+ </data>
+ </node>
+ <node id="n0::n1">
+ <data key="d6">
+ <y:ShapeNode>
+ <y:Geometry height="30.0" width="30.0" x="760.7189531249999" y="173.701171875"/>
+ <y:Fill color="#FFCC00" transparent="false"/>
+ <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="27.337890625" x="1.3310546875" y="5.6494140625">Item<y:LabelModel>
+ <y:SmartNodeLabelModel distance="4.0"/>
+ </y:LabelModel>
+ <y:ModelParameter>
+ <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
+ </y:ModelParameter>
+ </y:NodeLabel>
+ <y:Shape type="ellipse"/>
+ </y:ShapeNode>
+ </data>
+ </node>
+ <node id="n0::n2">
+ <data key="d6">
+ <y:ShapeNode>
+ <y:Geometry height="30.0" width="30.0" x="860.5" y="173.701171875"/>
+ <y:Fill color="#FFCC00" transparent="false"/>
+ <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="27.337890625" x="1.3310546875" y="5.6494140625">Item<y:LabelModel>
+ <y:SmartNodeLabelModel distance="4.0"/>
+ </y:LabelModel>
+ <y:ModelParameter>
+ <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
+ </y:ModelParameter>
+ </y:NodeLabel>
+ <y:Shape type="ellipse"/>
+ </y:ShapeNode>
+ </data>
+ </node>
+ <node id="n0::n3">
+ <data key="d6">
+ <y:ShapeNode>
+ <y:Geometry height="30.0" width="30.0" x="1000.5" y="243.701171875"/>
+ <y:Fill color="#FFCC00" transparent="false"/>
+ <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="27.337890625" x="1.3310546875" y="5.6494140625">Item<y:LabelModel>
+ <y:SmartNodeLabelModel distance="4.0"/>
+ </y:LabelModel>
+ <y:ModelParameter>
+ <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
+ </y:ModelParameter>
+ </y:NodeLabel>
+ <y:Shape type="ellipse"/>
+ </y:ShapeNode>
+ </data>
+ </node>
+ <node id="n0::n4">
+ <data key="d6">
+ <y:ShapeNode>
+ <y:Geometry height="30.0" width="30.0" x="1000.5" y="173.701171875"/>
+ <y:Fill color="#FFCC00" transparent="false"/>
+ <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="27.337890625" x="1.3310546875" y="5.6494140625">Item<y:LabelModel>
+ <y:SmartNodeLabelModel distance="4.0"/>
+ </y:LabelModel>
+ <y:ModelParameter>
+ <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
+ </y:ModelParameter>
+ </y:NodeLabel>
+ <y:Shape type="ellipse"/>
+ </y:ShapeNode>
+ </data>
+ </node>
+ <node id="n0::n5">
+ <data key="d6">
+ <y:ShapeNode>
+ <y:Geometry height="30.0" width="30.0" x="795.7189531249999" y="243.701171875"/>
+ <y:Fill color="#FFCC00" transparent="false"/>
+ <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="27.337890625" x="1.3310546875" y="5.6494140625">Item<y:LabelModel>
+ <y:SmartNodeLabelModel distance="4.0"/>
+ </y:LabelModel>
+ <y:ModelParameter>
+ <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
+ </y:ModelParameter>
+ </y:NodeLabel>
+ <y:Shape type="ellipse"/>
+ </y:ShapeNode>
+ </data>
+ </node>
+ <node id="n0::n6">
+ <data key="d6">
+ <y:ShapeNode>
+ <y:Geometry height="30.0" width="96.59199999999987" x="1058.7425234375003" y="108.6015625"/>
+ <y:Fill color="#FFCC00" transparent="false"/>
+ <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="81.373046875" x="7.609476562500049" y="5.6494140625">Virtual folder 3<y:LabelModel>
+ <y:SmartNodeLabelModel distance="4.0"/>
+ </y:LabelModel>
+ <y:ModelParameter>
+ <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
+ </y:ModelParameter>
+ </y:NodeLabel>
+ <y:Shape type="rectangle"/>
+ </y:ShapeNode>
+ </data>
+ </node>
+ <node id="n0::n7">
+ <data key="d6">
+ <y:ShapeNode>
+ <y:Geometry height="30.0" width="30.0" x="725.7189531249999" y="243.701171875"/>
+ <y:Fill color="#FFCC00" transparent="false"/>
+ <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="27.337890625" x="1.3310546875" y="5.6494140625">Item<y:LabelModel>
+ <y:SmartNodeLabelModel distance="4.0"/>
+ </y:LabelModel>
+ <y:ModelParameter>
+ <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
+ </y:ModelParameter>
+ </y:NodeLabel>
+ <y:Shape type="ellipse"/>
+ </y:ShapeNode>
+ </data>
+ </node>
+ <node id="n0::n8">
+ <data key="d6">
+ <y:ShapeNode>
+ <y:Geometry height="30.0" width="30.0" x="1092.0385234375" y="173.701171875"/>
+ <y:Fill color="#FFCC00" transparent="false"/>
+ <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="27.337890625" x="1.3310546875" y="5.6494140625">Item<y:LabelModel>
+ <y:SmartNodeLabelModel distance="4.0"/>
+ </y:LabelModel>
+ <y:ModelParameter>
+ <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
+ </y:ModelParameter>
+ </y:NodeLabel>
+ <y:Shape type="ellipse"/>
+ </y:ShapeNode>
+ </data>
+ </node>
+ <node id="n0::n9">
+ <data key="d6">
+ <y:ShapeNode>
+ <y:Geometry height="30.0" width="96.59199999999987" x="897.2040000000001" y="103.701171875"/>
+ <y:Fill color="#FFCC00" transparent="false"/>
+ <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="81.373046875" x="7.609476562499935" y="5.6494140625">Virtual folder 2<y:LabelModel>
+ <y:SmartNodeLabelModel distance="4.0"/>
+ </y:LabelModel>
+ <y:ModelParameter>
+ <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
+ </y:ModelParameter>
+ </y:NodeLabel>
+ <y:Shape type="rectangle"/>
+ </y:ShapeNode>
+ </data>
+ </node>
+ <node id="n0::n10">
+ <data key="d6">
+ <y:ShapeNode>
+ <y:Geometry height="30.0" width="30.0" x="930.5" y="33.701171875"/>
+ <y:Fill color="#33CCCC" transparent="false"/>
+ <y:BorderStyle color="#000000" raised="false" type="dashed" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="14.669921875" x="7.6650390625" y="5.6494140625">-1<y:LabelModel>
+ <y:SmartNodeLabelModel distance="4.0"/>
+ </y:LabelModel>
+ <y:ModelParameter>
+ <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
+ </y:ModelParameter>
+ </y:NodeLabel>
+ <y:Shape type="rectangle"/>
+ </y:ShapeNode>
+ </data>
+ </node>
+ <node id="n0::n11">
+ <data key="d6">
+ <y:ShapeNode>
+ <y:Geometry height="30.0" width="30.0" x="930.5" y="173.701171875"/>
+ <y:Fill color="#FFCC00" transparent="false"/>
+ <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="27.337890625" x="1.3310546875" y="5.6494140625">Item<y:LabelModel>
+ <y:SmartNodeLabelModel distance="4.0"/>
+ </y:LabelModel>
+ <y:ModelParameter>
+ <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
+ </y:ModelParameter>
+ </y:NodeLabel>
+ <y:Shape type="ellipse"/>
+ </y:ShapeNode>
+ </data>
+ </node>
+ <node id="n0::n12">
+ <data key="d6">
+ <y:ShapeNode>
+ <y:Geometry height="30.0" width="96.59199999999987" x="719.4689531249999" y="103.701171875"/>
+ <y:Fill color="#FFCC00" transparent="false"/>
+ <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="81.373046875" x="7.609476562499935" y="5.6494140625">Virtual folder 1<y:LabelModel>
+ <y:SmartNodeLabelModel distance="4.0"/>
+ </y:LabelModel>
+ <y:ModelParameter>
+ <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
+ </y:ModelParameter>
+ </y:NodeLabel>
+ <y:Shape type="rectangle"/>
+ </y:ShapeNode>
+ </data>
+ </node>
+ </graph>
+ </node>
+ <node id="n1" yfiles.foldertype="group">
+ <data key="d6">
+ <y:ProxyAutoBoundsNode>
+ <y:Realizers active="0">
+ <y:GenericGroupNode configuration="PanelNode">
+ <y:Geometry height="290.6015625" width="379.77599999999984" x="1205.2810468750001" y="0.0"/>
+ <y:Fill color="#68B0E3" transparent="false"/>
+ <y:BorderStyle hasColor="false" type="line" width="1.0"/>
+ <y:NodeLabel alignment="right" autoSizePolicy="node_width" borderDistance="0.0" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="23.6015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#FFFFFF" verticalTextPosition="bottom" visible="true" width="379.77599999999984" x="0.0" y="0.0">Группы пользователей - права доступа</y:NodeLabel>
+ <y:StyleProperties>
+ <y:Property class="java.awt.Color" name="headerBackground" value="#68b0e3"/>
+ </y:StyleProperties>
+ <y:State autoResize="true" closed="false" closedHeight="50.0" closedWidth="50.0"/>
+ <y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
+ <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
+ </y:GenericGroupNode>
+ <y:GenericGroupNode configuration="PanelNode">
+ <y:Geometry height="53.6015625" width="50.0" x="640.5999999999999" y="-91.5"/>
+ <y:Fill color="#68B0E3" transparent="false"/>
+ <y:BorderStyle hasColor="false" type="line" width="1.0"/>
+ <y:NodeLabel alignment="right" autoSizePolicy="node_width" borderDistance="0.0" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="23.6015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#FFFFFF" verticalTextPosition="bottom" visible="true" width="50.0" x="0.0" y="0.0">4</y:NodeLabel>
+ <y:StyleProperties>
+ <y:Property class="java.awt.Color" name="headerBackground" value="#68b0e3"/>
+ </y:StyleProperties>
+ <y:State autoResize="true" closed="false" closedHeight="50.0" closedWidth="50.0"/>
+ <y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
+ <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
+ </y:GenericGroupNode>
+ </y:Realizers>
+ </y:ProxyAutoBoundsNode>
+ </data>
+ <graph edgedefault="directed" id="n1:">
+ <node id="n1::n0">
+ <data key="d6">
+ <y:ShapeNode>
+ <y:Geometry height="30.0" width="96.59199999999987" x="1473.4650468749999" y="108.6015625"/>
+ <y:Fill color="#FFCC00" transparent="false"/>
+ <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="81.373046875" x="7.609476562500049" y="5.6494140625">Virtual folder 3<y:LabelModel>
+ <y:SmartNodeLabelModel distance="4.0"/>
+ </y:LabelModel>
+ <y:ModelParameter>
+ <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
+ </y:ModelParameter>
+ </y:NodeLabel>
+ <y:Shape type="rectangle"/>
+ </y:ShapeNode>
+ </data>
+ </node>
+ <node id="n1::n1">
+ <data key="d6">
+ <y:ShapeNode>
+ <y:Geometry height="30.0" width="30.0" x="1380.1690468749998" y="38.6015625"/>
+ <y:Fill color="#33CCCC" transparent="false"/>
+ <y:BorderStyle color="#000000" raised="false" type="dashed" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="14.669921875" x="7.6650390625" y="5.6494140625">-1<y:LabelModel>
+ <y:SmartNodeLabelModel distance="4.0"/>
+ </y:LabelModel>
+ <y:ModelParameter>
+ <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
+ </y:ModelParameter>
+ </y:NodeLabel>
+ <y:Shape type="rectangle"/>
+ </y:ShapeNode>
+ </data>
+ </node>
+ <node id="n1::n2">
+ <data key="d6">
+ <y:ShapeNode>
+ <y:Geometry height="30.0" width="96.59199999999987" x="1346.873046875" y="108.6015625"/>
+ <y:Fill color="#FFCC00" transparent="false"/>
+ <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="81.373046875" x="7.609476562499822" y="5.6494140625">Virtual folder 2<y:LabelModel>
+ <y:SmartNodeLabelModel distance="4.0"/>
+ </y:LabelModel>
+ <y:ModelParameter>
+ <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
+ </y:ModelParameter>
+ </y:NodeLabel>
+ <y:Shape type="rectangle"/>
+ </y:ShapeNode>
+ </data>
+ </node>
+ <node id="n1::n3">
+ <data key="d6">
+ <y:ShapeNode>
+ <y:Geometry height="30.0" width="96.59199999999987" x="1220.2810468750001" y="108.6015625"/>
+ <y:Fill color="#FFCC00" transparent="false"/>
+ <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="81.373046875" x="7.609476562500049" y="5.6494140625">Virtual folder 1<y:LabelModel>
+ <y:SmartNodeLabelModel distance="4.0"/>
+ </y:LabelModel>
+ <y:ModelParameter>
+ <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
+ </y:ModelParameter>
+ </y:NodeLabel>
+ <y:Shape type="rectangle"/>
+ </y:ShapeNode>
+ </data>
+ </node>
+ <node id="n1::n4">
+ <data key="d6">
+ <y:ShapeNode>
+ <y:Geometry height="54.0" width="96.59199999999987" x="1473.465046875" y="156.6015625"/>
+ <y:Fill color="#FFCC00" transparent="false"/>
+ <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.349609375" x="16.62119531249982" y="17.6494140625">Permission<y:LabelModel>
+ <y:SmartNodeLabelModel distance="4.0"/>
+ </y:LabelModel>
+ <y:ModelParameter>
+ <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
+ </y:ModelParameter>
+ </y:NodeLabel>
+ <y:Shape type="star6"/>
+ </y:ShapeNode>
+ </data>
+ </node>
+ <node id="n1::n5">
+ <data key="d6">
+ <y:ShapeNode>
+ <y:Geometry height="54.0" width="96.59199999999987" x="1220.2810468750004" y="161.701171875"/>
+ <y:Fill color="#FFCC00" transparent="false"/>
+ <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.349609375" x="16.62119531249982" y="17.6494140625">Permission<y:LabelModel>
+ <y:SmartNodeLabelModel distance="4.0"/>
+ </y:LabelModel>
+ <y:ModelParameter>
+ <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
+ </y:ModelParameter>
+ </y:NodeLabel>
+ <y:Shape type="star6"/>
+ </y:ShapeNode>
+ </data>
+ </node>
+ <node id="n1::n6">
+ <data key="d6">
+ <y:ShapeNode>
+ <y:Geometry height="54.0" width="96.59199999999987" x="1346.873046875" y="161.6015625"/>
+ <y:Fill color="#FFCC00" transparent="false"/>
+ <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.349609375" x="16.62119531249982" y="17.6494140625">Permission<y:LabelModel>
+ <y:SmartNodeLabelModel distance="4.0"/>
+ </y:LabelModel>
+ <y:ModelParameter>
+ <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
+ </y:ModelParameter>
+ </y:NodeLabel>
+ <y:Shape type="star6"/>
+ </y:ShapeNode>
+ </data>
+ </node>
+ <node id="n1::n7">
+ <data key="d6">
+ <y:ShapeNode>
+ <y:Geometry height="30.0" width="55.0" x="1367.6690468749998" y="245.6015625"/>
+ <y:Fill color="#FFCC00" transparent="false"/>
+ <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
+ <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="37.3515625" x="8.82421875" y="5.6494140625">Group<y:LabelModel>
+ <y:SmartNodeLabelModel distance="4.0"/>
+ </y:LabelModel>
+ <y:ModelParameter>
+ <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
+ </y:ModelParameter>
+ </y:NodeLabel>
+ <y:Shape type="roundrectangle"/>
+ </y:ShapeNode>
+ </data>
+ </node>
+ </graph>
+ </node>
+ <edge id="n1::e0" source="n1::n7" target="n1::n5">
+ <data key="d10">
+ <y:PolyLineEdge>
+ <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+ <y:LineStyle color="#000000" type="line" width="1.0"/>
+ <y:Arrows source="none" target="standard"/>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </edge>
+ <edge id="n1::e1" source="n1::n7" target="n1::n4">
+ <data key="d10">
+ <y:PolyLineEdge>
+ <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+ <y:LineStyle color="#000000" type="line" width="1.0"/>
+ <y:Arrows source="none" target="standard"/>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </edge>
+ <edge id="n1::e2" source="n1::n1" target="n1::n3">
+ <data key="d10">
+ <y:PolyLineEdge>
+ <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+ <y:LineStyle color="#000000" type="line" width="1.0"/>
+ <y:Arrows source="none" target="standard"/>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </edge>
+ <edge id="n1::e3" source="n1::n1" target="n1::n2">
+ <data key="d10">
+ <y:PolyLineEdge>
+ <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+ <y:LineStyle color="#000000" type="line" width="1.0"/>
+ <y:Arrows source="none" target="standard"/>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </edge>
+ <edge id="n1::e4" source="n1::n1" target="n1::n0">
+ <data key="d10">
+ <y:PolyLineEdge>
+ <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+ <y:LineStyle color="#000000" type="line" width="1.0"/>
+ <y:Arrows source="none" target="standard"/>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </edge>
+ <edge id="n1::e5" source="n1::n6" target="n1::n2">
+ <data key="d10">
+ <y:PolyLineEdge>
+ <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+ <y:LineStyle color="#000000" type="line" width="1.0"/>
+ <y:Arrows source="none" target="standard"/>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </edge>
+ <edge id="n1::e6" source="n1::n4" target="n1::n0">
+ <data key="d10">
+ <y:PolyLineEdge>
+ <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+ <y:LineStyle color="#000000" type="line" width="1.0"/>
+ <y:Arrows source="none" target="standard"/>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </edge>
+ <edge id="n1::e7" source="n1::n5" target="n1::n3">
+ <data key="d10">
+ <y:PolyLineEdge>
+ <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+ <y:LineStyle color="#000000" type="line" width="1.0"/>
+ <y:Arrows source="none" target="standard"/>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </edge>
+ <edge id="n1::e8" source="n1::n7" target="n1::n6">
+ <data key="d10">
+ <y:PolyLineEdge>
+ <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+ <y:LineStyle color="#000000" type="line" width="1.0"/>
+ <y:Arrows source="none" target="standard"/>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </edge>
+ <edge id="n0::e0" source="n0::n1" target="n0::n7">
+ <data key="d10">
+ <y:PolyLineEdge>
+ <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+ <y:LineStyle color="#000000" type="line" width="1.0"/>
+ <y:Arrows source="none" target="standard"/>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </edge>
+ <edge id="n0::e1" source="n0::n1" target="n0::n5">
+ <data key="d10">
+ <y:PolyLineEdge>
+ <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+ <y:LineStyle color="#000000" type="line" width="1.0"/>
+ <y:Arrows source="none" target="standard"/>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </edge>
+ <edge id="n0::e2" source="n0::n4" target="n0::n3">
+ <data key="d10">
+ <y:PolyLineEdge>
+ <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+ <y:LineStyle color="#000000" type="line" width="1.0"/>
+ <y:Arrows source="none" target="standard"/>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </edge>
+ <edge id="n0::e3" source="n0::n6" target="n0::n8">
+ <data key="d10">
+ <y:PolyLineEdge>
+ <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+ <y:LineStyle color="#000000" type="line" width="1.0"/>
+ <y:Arrows source="none" target="standard"/>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </edge>
+ <edge id="n0::e4" source="n0::n9" target="n0::n2">
+ <data key="d10">
+ <y:PolyLineEdge>
+ <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+ <y:LineStyle color="#000000" type="line" width="1.0"/>
+ <y:Arrows source="none" target="standard"/>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </edge>
+ <edge id="n0::e5" source="n0::n9" target="n0::n11">
+ <data key="d10">
+ <y:PolyLineEdge>
+ <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+ <y:LineStyle color="#000000" type="line" width="1.0"/>
+ <y:Arrows source="none" target="standard"/>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </edge>
+ <edge id="n0::e6" source="n0::n9" target="n0::n4">
+ <data key="d10">
+ <y:PolyLineEdge>
+ <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+ <y:LineStyle color="#000000" type="line" width="1.0"/>
+ <y:Arrows source="none" target="standard"/>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </edge>
+ <edge id="n0::e7" source="n0::n10" target="n0::n12">
+ <data key="d10">
+ <y:PolyLineEdge>
+ <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+ <y:LineStyle color="#000000" type="line" width="1.0"/>
+ <y:Arrows source="none" target="standard"/>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </edge>
+ <edge id="n0::e8" source="n0::n10" target="n0::n9">
+ <data key="d10">
+ <y:PolyLineEdge>
+ <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+ <y:LineStyle color="#000000" type="line" width="1.0"/>
+ <y:Arrows source="none" target="standard"/>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </edge>
+ <edge id="n0::e9" source="n0::n10" target="n0::n6">
+ <data key="d10">
+ <y:PolyLineEdge>
+ <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+ <y:LineStyle color="#000000" type="line" width="1.0"/>
+ <y:Arrows source="none" target="standard"/>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </edge>
+ <edge id="n0::e10" source="n0::n12" target="n0::n0">
+ <data key="d10">
+ <y:PolyLineEdge>
+ <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+ <y:LineStyle color="#000000" type="line" width="1.0"/>
+ <y:Arrows source="none" target="standard"/>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </edge>
+ <edge id="n0::e11" source="n0::n12" target="n0::n1">
+ <data key="d10">
+ <y:PolyLineEdge>
+ <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+ <y:LineStyle color="#000000" type="line" width="1.0"/>
+ <y:Arrows source="none" target="standard"/>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </edge>
+ </graph>
+ <data key="d7">
+ <y:Resources/>
+ </data>
+</graphml>