diff --git a/.drone.yml b/.drone.yml
index 1e97fec..ab57284 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -85,7 +85,7 @@ steps:
- name: docker-image-console-app
image: plugins/docker
settings:
- dockerfile: MCTG/Dockerfile
+ dockerfile: MCTG/ConsoleApp/Dockerfile
context: MCTG/
registry: hub.codefirst.iut.uca.fr
repo: hub.codefirst.iut.uca.fr/alexandre.agostinho/console-mctg
diff --git a/MCTG/AppException/AppException.csproj b/MCTG/AppException/AppException.csproj
new file mode 100644
index 0000000..cfadb03
--- /dev/null
+++ b/MCTG/AppException/AppException.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
diff --git a/MCTG/AppException/RecipeExceptions/NoRecipeSelectedException.cs b/MCTG/AppException/RecipeExceptions/NoRecipeSelectedException.cs
new file mode 100644
index 0000000..7f7f294
--- /dev/null
+++ b/MCTG/AppException/RecipeExceptions/NoRecipeSelectedException.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AppException
+{
+ public class NoRecipeSelectedException : RecipeException
+ {
+ public NoRecipeSelectedException() : base("No recipe is currently selected to perform this action.") { }
+ }
+}
diff --git a/MCTG/AppException/RecipeExceptions/RecipeException.cs b/MCTG/AppException/RecipeExceptions/RecipeException.cs
new file mode 100644
index 0000000..aba9d0b
--- /dev/null
+++ b/MCTG/AppException/RecipeExceptions/RecipeException.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection.Emit;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AppException
+{
+ public class RecipeException : Exception
+ {
+ public RecipeException() : base("Something went wrong with a recipe or a collection of recipe.") { }
+ public RecipeException(string message) : base(message) { }
+ }
+}
diff --git a/MCTG/AppException/RecipeExceptions/RecipeNotFoundException.cs b/MCTG/AppException/RecipeExceptions/RecipeNotFoundException.cs
new file mode 100644
index 0000000..a6c3ee2
--- /dev/null
+++ b/MCTG/AppException/RecipeExceptions/RecipeNotFoundException.cs
@@ -0,0 +1,9 @@
+
+namespace AppException
+{
+ public class RecipeNotFoundException : RecipeException
+ {
+ public RecipeNotFoundException() : base("Recipe not found.") { }
+ public RecipeNotFoundException(int id) : base($"Recipe id: '{id}'not found.") { }
+ }
+}
diff --git a/MCTG/AppException/UserExceptions/BadMailFormatException.cs b/MCTG/AppException/UserExceptions/BadMailFormatException.cs
new file mode 100644
index 0000000..c55f0c9
--- /dev/null
+++ b/MCTG/AppException/UserExceptions/BadMailFormatException.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AppException
+{
+ public class BadMailFormatException : UserException
+ {
+ public BadMailFormatException() : base("Invalid mail format.") { }
+ public BadMailFormatException(string mail) : base($"'{mail}' is an invalid format.") { }
+ }
+}
diff --git a/MCTG/AppException/UserExceptions/NoUserConnectedException.cs b/MCTG/AppException/UserExceptions/NoUserConnectedException.cs
new file mode 100644
index 0000000..e0659fa
--- /dev/null
+++ b/MCTG/AppException/UserExceptions/NoUserConnectedException.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AppException
+{
+ public class NoUserConnectedException : UserException
+ {
+ public NoUserConnectedException() : base("No user is currently connected.") { }
+ }
+}
diff --git a/MCTG/AppException/UserExceptions/UserAlreadyConnectedException.cs b/MCTG/AppException/UserExceptions/UserAlreadyConnectedException.cs
new file mode 100644
index 0000000..15b0ad6
--- /dev/null
+++ b/MCTG/AppException/UserExceptions/UserAlreadyConnectedException.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AppException
+{
+ public class UserAlreadyConnectedException : UserException
+ {
+ public UserAlreadyConnectedException() : base("An user is already connected.") { }
+ }
+}
diff --git a/MCTG/AppException/UserExceptions/UserException.cs b/MCTG/AppException/UserExceptions/UserException.cs
new file mode 100644
index 0000000..957e97a
--- /dev/null
+++ b/MCTG/AppException/UserExceptions/UserException.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AppException
+{
+ public class UserException : Exception
+ {
+ public UserException() : base("Somthing went wrong with an User.") { }
+ public UserException(string message) : base(message) { }
+ }
+}
diff --git a/MCTG/AppException/UserExceptions/UserNotFoundException.cs b/MCTG/AppException/UserExceptions/UserNotFoundException.cs
new file mode 100644
index 0000000..c858fac
--- /dev/null
+++ b/MCTG/AppException/UserExceptions/UserNotFoundException.cs
@@ -0,0 +1,8 @@
+namespace AppException
+{
+ public class UserNotFoundException : UserException
+ {
+ public UserNotFoundException() : base("User not found.") { }
+ public UserNotFoundException(string userMail) : base($"User with mail: '{userMail}' not found.") { }
+ }
+}
diff --git a/MCTG/CI-CD.slnf b/MCTG/CI-CD.slnf
index 5728156..ca21de6 100644
--- a/MCTG/CI-CD.slnf
+++ b/MCTG/CI-CD.slnf
@@ -3,7 +3,11 @@
"path": "SAE-2.01.sln",
"projects": [
"ConsoleApp\\ConsoleApp.csproj",
+ "AppException\\AppException.csproj",
+ "Managers\\Managers.csproj",
"Model\\Model.csproj",
+ "Persistance\\DataPersistence\\DataPersistence.csproj",
+ "Persistance\\FakePersistance\\FakePersistance.csproj",
"Tests\\Model_UnitTests\\Model_UnitTests.csproj"
]
}
diff --git a/MCTG/ConsoleApp/ConsoleApp.csproj b/MCTG/ConsoleApp/ConsoleApp.csproj
index dbc5e22..4a92e3e 100644
--- a/MCTG/ConsoleApp/ConsoleApp.csproj
+++ b/MCTG/ConsoleApp/ConsoleApp.csproj
@@ -7,9 +7,12 @@
enable
-
-
-
+
+
+
+
+
+
diff --git a/MCTG/Dockerfile b/MCTG/ConsoleApp/Dockerfile
similarity index 100%
rename from MCTG/Dockerfile
rename to MCTG/ConsoleApp/Dockerfile
diff --git a/MCTG/ConsoleApp/Menu/AddRecipeMenu.cs b/MCTG/ConsoleApp/Menu/AddRecipeMenu.cs
index 5e66b49..6152dca 100644
--- a/MCTG/ConsoleApp/Menu/AddRecipeMenu.cs
+++ b/MCTG/ConsoleApp/Menu/AddRecipeMenu.cs
@@ -1,11 +1,5 @@
using ConsoleApp.Menu.Core;
using Model;
-using Model.Managers;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace ConsoleApp.Menu
{
@@ -40,14 +34,15 @@ namespace ConsoleApp.Menu
Recipe recipe = new Recipe(
title: title,
type: RecipeType.Unspecified,
+ priority: Priority.Fast,
id: null,
- authorMail: masterMgr.CurrentConnectedUser?.Mail,
+ authorMail: masterMgr.User.CurrentConnected?.Mail,
picture: null)
{
PreparationSteps = steps
};
- masterMgr.AddRecipe(recipe);
+ masterMgr.Recipe.AddRecipeToData(recipe);
return null;
}
}
diff --git a/MCTG/ConsoleApp/Menu/AddUserMenu.cs b/MCTG/ConsoleApp/Menu/AddUserMenu.cs
index 57ca2aa..49d6a57 100644
--- a/MCTG/ConsoleApp/Menu/AddUserMenu.cs
+++ b/MCTG/ConsoleApp/Menu/AddUserMenu.cs
@@ -1,11 +1,5 @@
using ConsoleApp.Menu.Core;
using Model;
-using Model.Managers;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace ConsoleApp.Menu
{
@@ -30,14 +24,9 @@ namespace ConsoleApp.Menu
string surname = _selectList[2].Item.Input;
string passwd = _selectList[3].Item.Input;
- User user = new User(
- name: name,
- surname: surname,
- mail: mail,
- password: passwd
- );
+ User user = masterMgr.User.CreateUser(mail, passwd, name, surname);
- masterMgr.Register(user);
+ masterMgr.User.AddUserToData(user);
return null;
}
}
diff --git a/MCTG/ConsoleApp/Menu/ConnectionMenu.cs b/MCTG/ConsoleApp/Menu/ConnectionMenu.cs
index 30759be..432822a 100644
--- a/MCTG/ConsoleApp/Menu/ConnectionMenu.cs
+++ b/MCTG/ConsoleApp/Menu/ConnectionMenu.cs
@@ -1,10 +1,5 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using ConsoleApp.Menu.Core;
-using Model.Managers;
+using ConsoleApp.Menu.Core;
+using Model;
namespace ConsoleApp.Menu
{
@@ -38,7 +33,7 @@ namespace ConsoleApp.Menu
string mail = _selectList[0].Item.Input;
string password = _selectList[1].Item.Input;
- if (!_masterMgr.Login(mail, password))
+ if (!_masterMgr.User.LogIn(mail, password))
{
_wrongInput = true;
return this;
diff --git a/MCTG/ConsoleApp/Menu/ExportRecipeMenu.cs b/MCTG/ConsoleApp/Menu/ExportRecipeMenu.cs
index 1518a1c..cba11d5 100644
--- a/MCTG/ConsoleApp/Menu/ExportRecipeMenu.cs
+++ b/MCTG/ConsoleApp/Menu/ExportRecipeMenu.cs
@@ -1,12 +1,5 @@
using ConsoleApp.Menu.Core;
-using Model.Managers;
using Model;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using DataPersistence;
namespace ConsoleApp.Menu
{
@@ -36,7 +29,7 @@ namespace ConsoleApp.Menu
try
{
- _masterMgr.DataMgr.Export(recipe, path);
+ _masterMgr.Data.Export(recipe, path);
}
catch (ArgumentNullException e)
{
diff --git a/MCTG/ConsoleApp/Menu/ImportRecipeMenu.cs b/MCTG/ConsoleApp/Menu/ImportRecipeMenu.cs
index d1f402c..2dac0f5 100644
--- a/MCTG/ConsoleApp/Menu/ImportRecipeMenu.cs
+++ b/MCTG/ConsoleApp/Menu/ImportRecipeMenu.cs
@@ -1,11 +1,5 @@
using ConsoleApp.Menu.Core;
using Model;
-using Model.Managers;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace ConsoleApp.Menu
{
@@ -25,7 +19,7 @@ namespace ConsoleApp.Menu
string path = _selectList[0].Item.Input;
try
{
- masterMgr.DataMgr.Import(path);
+ masterMgr.Data.Import(path);
}
catch(ArgumentNullException e)
{
diff --git a/MCTG/ConsoleApp/Menu/LogoutButton.cs b/MCTG/ConsoleApp/Menu/LogoutButton.cs
index aa10600..563332e 100644
--- a/MCTG/ConsoleApp/Menu/LogoutButton.cs
+++ b/MCTG/ConsoleApp/Menu/LogoutButton.cs
@@ -1,10 +1,5 @@
using ConsoleApp.Menu.Core;
-using Model.Managers;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using Model;
namespace ConsoleApp.Menu
{
@@ -20,7 +15,7 @@ namespace ConsoleApp.Menu
public override IMenu? Return()
{
- _masterMgr.Logout();
+ _masterMgr.User.LogOut();
return base.Return();
}
}
diff --git a/MCTG/ConsoleApp/Menu/MainMenu.cs b/MCTG/ConsoleApp/Menu/MainMenu.cs
index 856748f..94adfe6 100644
--- a/MCTG/ConsoleApp/Menu/MainMenu.cs
+++ b/MCTG/ConsoleApp/Menu/MainMenu.cs
@@ -1,12 +1,5 @@
using Model;
-using DataPersistence;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using ConsoleApp.Menu.Core;
-using Model.Managers;
namespace ConsoleApp.Menu
{
@@ -47,7 +40,7 @@ namespace ConsoleApp.Menu
{
List> selectors = base.SearchInSelection();
- if (_masterMgr.CurrentConnectedUser == null)
+ if (_masterMgr.User.CurrentConnected == null)
return selectors.Except(selectors.Where(s => s.Line == "User profile"))
.Except(selectors.Where(s => s.Line == "Logout"))
.Except(selectors.Where(s => s.Line == "Add recipe")).ToList();
diff --git a/MCTG/ConsoleApp/Menu/ProfileMenu.cs b/MCTG/ConsoleApp/Menu/ProfileMenu.cs
index da315b3..504f899 100644
--- a/MCTG/ConsoleApp/Menu/ProfileMenu.cs
+++ b/MCTG/ConsoleApp/Menu/ProfileMenu.cs
@@ -1,11 +1,5 @@
using ConsoleApp.Menu.Core;
-using Model.Managers;
using Model;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace ConsoleApp.Menu
{
diff --git a/MCTG/ConsoleApp/Menu/SearchUserRecipes.cs b/MCTG/ConsoleApp/Menu/SearchUserRecipes.cs
index abf9613..3145997 100644
--- a/MCTG/ConsoleApp/Menu/SearchUserRecipes.cs
+++ b/MCTG/ConsoleApp/Menu/SearchUserRecipes.cs
@@ -1,10 +1,4 @@
using Model;
-using Model.Managers;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace ConsoleApp.Menu
{
@@ -16,7 +10,10 @@ namespace ConsoleApp.Menu
public override void Update()
{
- _recipeCollectionOnSearch = _masterMgr.GetCurrentUserRecipes();
+ if (_masterMgr.User.CurrentConnected is null)
+ throw new ArgumentNullException();
+
+ _recipeCollectionOnSearch = _masterMgr.Recipe.GetRecipeByAuthor(_masterMgr.User.CurrentConnected.Mail);
_allSelectors = ConvertRecipeCollectionInSelectors();
_selectList = SearchInSelection();
diff --git a/MCTG/ConsoleApp/Menu/SearcherRecipe.cs b/MCTG/ConsoleApp/Menu/SearcherRecipe.cs
index 7f5065a..92e25b9 100644
--- a/MCTG/ConsoleApp/Menu/SearcherRecipe.cs
+++ b/MCTG/ConsoleApp/Menu/SearcherRecipe.cs
@@ -1,12 +1,5 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Security.Cryptography.X509Certificates;
-using System.Text;
-using System.Threading.Tasks;
-using ConsoleApp.Menu.Core;
+using ConsoleApp.Menu.Core;
using Model;
-using Model.Managers;
namespace ConsoleApp.Menu
{
@@ -36,7 +29,7 @@ namespace ConsoleApp.Menu
public override void Update()
{
- _recipeCollectionOnSearch = _masterMgr.DataMgr.GetRecipes("all recipes");
+ _recipeCollectionOnSearch = _masterMgr.Recipe.GetAllRecipes();
_allSelectors = ConvertRecipeCollectionInSelectors();
base.Update();
}
@@ -46,7 +39,7 @@ namespace ConsoleApp.Menu
if (CurrentSelected == null)
return this;
- return new PlainText(CurrentSelected.ToString());
+ return new ShowRecipeInfos(CurrentSelected);
}
#endregion
}
diff --git a/MCTG/ConsoleApp/Menu/ShowRecipeInfos.cs b/MCTG/ConsoleApp/Menu/ShowRecipeInfos.cs
new file mode 100644
index 0000000..2ac0fb9
--- /dev/null
+++ b/MCTG/ConsoleApp/Menu/ShowRecipeInfos.cs
@@ -0,0 +1,37 @@
+using ConsoleApp.Menu.Core;
+using Model;
+using System.Text;
+
+namespace ConsoleApp.Menu
+{
+ internal class ShowRecipeInfos : PlainText
+ {
+ public Recipe Recipe { get; private set; }
+
+ public ShowRecipeInfos(Recipe recipe)
+ : base("")
+ {
+ Recipe = recipe;
+ }
+
+ public override void Display()
+ {
+ StringBuilder sb = new StringBuilder($"[Recipe n°{Recipe.Id}] - {Recipe.Title}\n");
+ foreach (PreparationStep ps in Recipe.PreparationSteps)
+ {
+ sb.AppendFormat("\t* {0}\n", ps.ToString());
+ }
+ sb.AppendLine();
+ sb.AppendLine(Recipe.ConcatIngredients());
+ sb.AppendLine();
+ foreach (Review review in Recipe.Reviews)
+ {
+ sb.AppendLine(review.ToString());
+ }
+ sb.AppendLine();
+ sb.AppendLine($"Posted by: {Recipe.AuthorMail?.ToString()}");
+
+ Console.WriteLine(sb);
+ }
+ }
+}
diff --git a/MCTG/ConsoleApp/Menu/ShowUserInfos.cs b/MCTG/ConsoleApp/Menu/ShowUserInfos.cs
index 989ff5d..6715cde 100644
--- a/MCTG/ConsoleApp/Menu/ShowUserInfos.cs
+++ b/MCTG/ConsoleApp/Menu/ShowUserInfos.cs
@@ -1,11 +1,5 @@
using ConsoleApp.Menu.Core;
using Model;
-using Model.Managers;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace ConsoleApp.Menu
{
@@ -22,10 +16,10 @@ namespace ConsoleApp.Menu
public override void Display()
{
Console.WriteLine(
- $"\nUser: {_masterMgr.CurrentConnectedUser}\n\n"
- + $"\tMail: {_masterMgr.CurrentConnectedUser?.Mail}\n"
- + $"\tName: {_masterMgr.CurrentConnectedUser?.Name}\n"
- + $"\tSurname: {_masterMgr.CurrentConnectedUser?.Surname}\n");
+ $"\nUser: {_masterMgr.User.CurrentConnected}\n\n"
+ + $"\tMail: {_masterMgr.User.CurrentConnected?.Mail}\n"
+ + $"\tName: {_masterMgr.User.CurrentConnected?.Name}\n"
+ + $"\tSurname: {_masterMgr.User.CurrentConnected?.Surname}\n");
}
}
}
diff --git a/MCTG/ConsoleApp/MenuManager.cs b/MCTG/ConsoleApp/MenuManager.cs
index e215e8c..98517cb 100644
--- a/MCTG/ConsoleApp/MenuManager.cs
+++ b/MCTG/ConsoleApp/MenuManager.cs
@@ -1,13 +1,7 @@
using ConsoleApp.Menu;
using Model;
using DataPersistence;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using ConsoleApp.Menu.Core;
-using Model.Managers;
namespace ConsoleApp
{
@@ -37,7 +31,6 @@ namespace ConsoleApp
public MenuManager(MasterManager masterManager, IMenu firstMenu)
{
MasterMgr = masterManager;
-
MenuCallStack = new Stack();
MenuCallStack.Push(firstMenu);
}
diff --git a/MCTG/ConsoleApp/Program.cs b/MCTG/ConsoleApp/Program.cs
index ff09c71..3778fc2 100644
--- a/MCTG/ConsoleApp/Program.cs
+++ b/MCTG/ConsoleApp/Program.cs
@@ -1,31 +1,52 @@
using ConsoleApp;
using Model;
using DataPersistence;
-using Model.Managers;
+using FakePersistance;
+using Managers;
-Console.WriteLine("Hello, World!\n\n");
-string path = ""; // - path to the save file
-string strategy = "xml"; // - strategy is 'xml' or 'json' (/!\ this is case sensitive)
+namespace ConsoleApp;
-MasterManager masterMgr;
-IDataManager dataManager = (strategy == "xml") ?
- new DataContractXML(path)
- : new DataContractJSON(path);
-
-if (!File.Exists(Path.Combine(path, $"data.{strategy}")))
-{
- masterMgr = new MasterManager(new Stubs());
- masterMgr.DataMgr.Serializer = dataManager;
-}
-else
+public static class Program
{
- masterMgr = new MasterManager(dataManager);
-}
+ public static void Main(string[] args)
+ {
+ Console.WriteLine("Hello, World!\n\n");
+
+ string path = ""; // - path to the save file
+ string strategy = "xml"; // - strategy is 'xml' or 'json' (/!\ this is case sensitive)
+ // Initialize the data serializer
+ IDataSerializer dataSerializer = (strategy == "xml") ?
+ new DataContractXML(path)
+ : new DataContractJSON(path);
-MenuManager menuMgr = new MenuManager(masterMgr);
-menuMgr.Loop();
+ // Initialize the data manager
+ IDataManager dataManager = (!File.Exists(Path.Combine(path, $"data.{strategy}"))) ?
+ new DataDefaultManager(new Stubs())
+ : new DataDefaultManager(dataSerializer);
-// Save data.
-Console.Write("[ --SAVE-- ]:\t"); masterMgr.DataMgr.Save(); Console.WriteLine("Done.");
+ // Initialize the other managers
+ IRecipeManager recipeManager = new RecipeDefaultManager(dataManager);
+ IPasswordManager passwordManager = new PasswordSHA256Manager();
+ IUserManager userManager = new UserDefaultManager(dataManager, passwordManager);
+
+ // Initialize the master manager
+ MasterManager Master = new MasterManager(dataManager, recipeManager, userManager);
+ Master.Setup();
+
+ MenuManager menuMgr = new MenuManager(Master);
+ menuMgr.Loop();
+
+ // Change the data serializer if the one in place is 'Stubs'
+ if (Master.Data.Serializer.GetType() == typeof(Stubs))
+ {
+ var data = Master.Data.Data;
+ dataManager = new DataDefaultManager(dataSerializer, data);
+ Master = new MasterManager(dataManager, recipeManager, userManager);
+ }
+
+ // Save data.
+ Console.Write("[ --SAVE-- ]:\t"); Master.Data.SaveData(); Console.WriteLine("Done.");
+ }
+}
diff --git a/MCTG/Managers/DataDefaultManager.cs b/MCTG/Managers/DataDefaultManager.cs
new file mode 100644
index 0000000..5326602
--- /dev/null
+++ b/MCTG/Managers/DataDefaultManager.cs
@@ -0,0 +1,56 @@
+using Model;
+
+namespace Managers
+{
+ ///
+ /// Define the manager of the data. This is where all the data are put, and where we call the loading and the saving of them.
+ ///
+ public class DataDefaultManager : IDataManager
+ {
+ #region Attributes & Properties
+ public IDataSerializer Serializer { get; set; }
+
+ public Dictionary> Data { get; private set; }
+ #endregion
+
+ #region Constructors
+ ///
+ /// Constructor of the DataDefaultManager class. Take a IDataManager that will provide methods for the serialisation of the data.
+ ///
+ /// The data manager that know how to serialize a file.
+ /// The data set of the application.
+ public DataDefaultManager(IDataSerializer dataManager,
+ Dictionary>? data = null)
+ {
+ Serializer = dataManager;
+
+ if (data is null)
+ Data = new Dictionary>();
+ else
+ Data = data;
+ }
+ #endregion
+
+ #region Methods
+ public void LoadData()
+ => Data = Serializer.Load();
+
+ public void SaveData()
+ => Serializer.Save(Data);
+
+ public void Import(string pathOfTheFile)
+ where T : class
+ {
+ KeyValuePair import = Serializer.Import(pathOfTheFile);
+ Data[import.Key].Add(import.Value);
+ }
+
+ public void Export(T obj, string pathToExport)
+ where T : class
+ => Serializer.Export(obj, pathToExport);
+
+ public ICollection GetFromData() where T : class
+ => new List(Data[typeof(T).Name].Cast());
+ #endregion
+ }
+}
diff --git a/MCTG/ConsoleApp/ConsoleApp - Backup.csproj b/MCTG/Managers/Managers.csproj
similarity index 63%
rename from MCTG/ConsoleApp/ConsoleApp - Backup.csproj
rename to MCTG/Managers/Managers.csproj
index 2c5123f..bc50357 100644
--- a/MCTG/ConsoleApp/ConsoleApp - Backup.csproj
+++ b/MCTG/Managers/Managers.csproj
@@ -1,16 +1,14 @@
-
-
-
- Exe
- net7.0
- enable
- enable
- Debug;Release;CI
-
-
+
+
+
+ net7.0
+ enable
+ enable
+
+
-
+
-
-
-
+
+
+
diff --git a/MCTG/Model/User/PasswordSHA256.cs b/MCTG/Managers/PasswordSHA256Manager.cs
similarity index 94%
rename from MCTG/Model/User/PasswordSHA256.cs
rename to MCTG/Managers/PasswordSHA256Manager.cs
index e418de1..ee9ec27 100644
--- a/MCTG/Model/User/PasswordSHA256.cs
+++ b/MCTG/Managers/PasswordSHA256Manager.cs
@@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace Model
{
[DataContract(Name = "passmgr")]
- public class PasswordSHA256 : IPasswordManager
+ public class PasswordSHA256Manager : IPasswordManager
{
public string HashPassword(string password)
{
diff --git a/MCTG/Managers/RecipeDefaultManager.cs b/MCTG/Managers/RecipeDefaultManager.cs
new file mode 100644
index 0000000..feb69a7
--- /dev/null
+++ b/MCTG/Managers/RecipeDefaultManager.cs
@@ -0,0 +1,112 @@
+using AppException;
+using Model;
+using System.Diagnostics;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+namespace Managers
+{
+ public class RecipeDefaultManager : IRecipeManager
+ {
+ private IDataManager _dataManager;
+
+ public Recipe? CurrentSelected { get; set; } = null;
+
+
+ public RecipeDefaultManager(IDataManager dataManager)
+ {
+ _dataManager = dataManager;
+ }
+
+
+ public bool AddRecipeToData(Recipe recipe)
+ {
+ var recipeList = _dataManager.Data[nameof(Recipe)];
+
+ if (recipeList.Exists(r => r.Equals(recipe)))
+ return false;
+
+ _dataManager.Data[nameof(Recipe)].Add(recipe);
+ return true;
+ }
+
+ public RecipeCollection GetAllRecipes()
+ {
+ return new RecipeCollection(
+ "All recipes",
+ _dataManager.GetFromData().ToArray());
+ }
+
+ public RecipeCollection GetRecipeByAuthor(string authorMail)
+ {
+ User? author = _dataManager.GetFromData()
+ .ToList()
+ .Find(u => u.Mail == authorMail);
+ if (author is null)
+ throw new UserNotFoundException(authorMail);
+
+ IEnumerable recipes = from Recipe r in _dataManager.GetFromData()
+ where r.AuthorMail == author.Mail
+ select r;
+
+ return new RecipeCollection(
+ $"{author.Name} {author.Surname}'s recipes", recipes.ToArray());
+ }
+
+ public RecipeCollection SearchRecipeByTitle(string title)
+ {
+ IEnumerable recipes = from Recipe recipe in _dataManager.GetFromData()
+ where recipe.Title.Contains(title)
+ select recipe;
+ return new RecipeCollection(
+ $"Search for '{title}'", recipes.ToArray());
+ }
+
+ public Recipe GetRecipeFromId(int id)
+ {
+ Recipe? recipe = _dataManager.GetFromData()
+ .ToList()
+ .Find(r => r.Id == id);
+ if (recipe is null)
+ throw new RecipeNotFoundException();
+
+ return recipe;
+ }
+
+ public RecipeCollection GetRecipesByPriorityOrder(IEnumerable priorities)
+ {
+ List recipes = new List();
+ IEnumerable recipesWithCurrentPriority;
+ foreach (Priority priority in priorities)
+ {
+ recipesWithCurrentPriority = from Recipe recipe in _dataManager.GetFromData()
+ where recipe.Priority == priority
+ select recipe;
+ recipes.AddRange(recipesWithCurrentPriority);
+ }
+
+ return new RecipeCollection(
+ $"Suggestions", recipes.ToArray());
+ }
+
+ public bool ModifyCurrentSelected(Recipe newRecipe)
+ {
+ if (CurrentSelected is null)
+ throw new NoRecipeSelectedException();
+
+ try
+ {
+ var index = _dataManager.GetFromData().ToList()
+ .FindIndex(u => u.Equals(CurrentSelected));
+ _dataManager.Data[nameof(Recipe)][index] = newRecipe;
+ }
+ catch (ArgumentNullException e)
+ {
+ Debug.WriteLine("Recipe to modify not found.");
+ return false;
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/MCTG/Managers/UserDefaultManager.cs b/MCTG/Managers/UserDefaultManager.cs
new file mode 100644
index 0000000..d4731a8
--- /dev/null
+++ b/MCTG/Managers/UserDefaultManager.cs
@@ -0,0 +1,149 @@
+using AppException;
+using Model;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Security.Cryptography;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+
+namespace Managers
+{
+ public class UserDefaultManager : IUserManager, INotifyPropertyChanging
+ {
+ private IDataManager _dataManager;
+ private IPasswordManager _passwordManager;
+ private User? _currentConnected = null;
+
+
+ public UserDefaultManager(IDataManager dataManager, IPasswordManager passwordManager)
+ {
+ _dataManager = dataManager;
+ _passwordManager = passwordManager;
+ }
+
+
+ public User? CurrentConnected
+ {
+ get => _currentConnected;
+ private set
+ {
+ _currentConnected = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public IPasswordManager PasswordManager => _passwordManager;
+
+
+ public event PropertyChangingEventHandler? PropertyChanging;
+
+ public void OnPropertyChanged([CallerMemberName] string pname = "")
+ => PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(pname));
+
+
+ public bool AddUserToData(User user)
+ {
+ var userList = _dataManager.Data[nameof(User)];
+
+ if (userList.Exists(r => r.Equals(user)))
+ return false;
+
+ _dataManager.Data[nameof(User)].Add(user);
+ return true;
+ }
+
+ public User CreateUser(string mail, string password,
+ string? name = null, string? surname = null, string? profilePict = null)
+ {
+ if (name is null) name = $"user{GetRandomInt()}";
+ if (surname is null) surname = "";
+ if (profilePict is null) profilePict = "default_user_picture.png";
+
+ const string mailRegex = @"^([\w\.-]+)@([\w\.-]+\.\w+)$";
+ if (!Regex.Match(mail, mailRegex).Success)
+ throw new BadMailFormatException();
+
+ string hashedPassword = PasswordManager.HashPassword(password);
+ return new User(name, surname, mail, hashedPassword, profilePict);
+ }
+
+ public ICollection GetAllUsers()
+ {
+ return _dataManager.GetFromData();
+ }
+
+ public User GetUserFromMail(string mail)
+ {
+ User? user = _dataManager.GetFromData().ToList()
+ .Find(u => u.Mail == mail);
+ if (user is null)
+ throw new UserNotFoundException();
+
+ return user;
+ }
+
+ public bool LogIn(string mail, string password)
+ {
+ if (CurrentConnected is not null)
+ throw new UserAlreadyConnectedException();
+
+#if DEBUG
+ if (mail == "admin")
+ {
+ CurrentConnected = _dataManager.GetFromData()
+ .FirstOrDefault(u => u.Mail == "admin@mctg.fr");
+ return true;
+ }
+#endif
+
+ User? user = _dataManager.GetFromData().ToList()
+ .Find(u => u.Mail == mail);
+ if (user is null)
+ return false;
+
+ if (!_passwordManager.VerifyPassword(user.Password, password))
+ return false;
+
+ CurrentConnected = user;
+ return true;
+ }
+
+ public void LogOut()
+ {
+ if (CurrentConnected is null)
+ throw new NoUserConnectedException();
+
+ CurrentConnected = null;
+ }
+
+ public bool ModifyCurrentConnected(User newUser)
+ {
+ try
+ {
+ var index = _dataManager.GetFromData().ToList()
+ .FindIndex(u => u.Equals(_currentConnected));
+ _dataManager.Data[nameof(User)][index] = newUser;
+ }
+ catch (ArgumentNullException e)
+ {
+ Debug.WriteLine("User to modify not found.");
+ return false;
+ }
+
+ return true;
+ }
+
+ private int GetRandomInt()
+ {
+ var randomGenerator = RandomNumberGenerator.Create();
+ byte[] data = new byte[16];
+ randomGenerator.GetBytes(data);
+ return Math.Abs(BitConverter.ToInt16(data));
+ }
+ }
+}
diff --git a/MCTG/Model/Managers/DataManager.cs b/MCTG/Model/Managers/DataManager.cs
deleted file mode 100644
index 8ff3ff6..0000000
--- a/MCTG/Model/Managers/DataManager.cs
+++ /dev/null
@@ -1,105 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Model
-{
- ///
- /// Define the manager of the data. This is where all the data are put, and where we call the loading and the saving of them.
- ///
- public class DataManager
- {
- #region Attributes & Properties
- ///
- /// The data manager injected that know how to serialize the data.
- /// The setter is actually public for testing purpose. It will be private after.
- /// See:
- ///
- public IDataManager Serializer { get; set; }
-
- ///
- /// The collection of all data. Each line of this dictionary has the type of the data as it key and the data for values.
- ///
- public Dictionary> Data { get; private set; }
- #endregion
-
- #region Constructors
- ///
- /// Constructor of the DataManager class. Take a IDataManager that will provide methods for the serialisation of the data.
- ///
- /// The data manager that know how to serialize a file.
- public DataManager(IDataManager dataMgr)
- {
- Serializer = dataMgr;
- Data = Serializer.Load();
- }
- #endregion
-
- #region Methods
- ///
- /// Reload the data. Useful to update new data written in the save file.
- /// See:
- ///
- public void Reload()
- => Data = Serializer.Load();
-
- ///
- /// Save the data. Call the Save method of the serializer.
- /// See:
- ///
- public void Save()
- => Serializer.Save(Data);
-
- ///
- /// Import data from a file.
- /// See:
- ///
- /// The type of data to import.
- /// The path containing the name of the file created.
- public void Import(string pathOfTheFile)
- where T : class
- {
- KeyValuePair import = Serializer.Import(pathOfTheFile);
- Data[import.Key].Add(import.Value);
- }
-
- ///
- /// Export the data from the collection of data.
- /// See:
- ///
- /// The type of data to export
- /// The object to export
- /// The path containing the name of the file created.
- public void Export(T obj, string pathToExport)
- where T : class
- => Serializer.Export(obj, pathToExport);
-
- ///
- /// Get all the recipe from the data.
- ///
- /// The title to give for the Recipe Collection
- /// A RecipeCollection that contain all the recipe in the data.
- public RecipeCollection GetRecipes(string rcTitle = "default")
- => new RecipeCollection(rcTitle, Data[nameof(Recipe)].Cast().ToArray());
-
- ///
- /// Get all the Users from the data.
- ///
- /// A list of all Users.
- public List GetUsers()
- => new List(Data[nameof(User)].Cast());
-
- ///
- /// Get a list of an item in the data.
- ///
- /// The type of the item
- /// The list of all the item found in the data.
- public ICollection GetFromData() where T : class
- => new List(Data[typeof(T).Name].Cast());
-
- #endregion
- }
-}
diff --git a/MCTG/Model/Managers/IDataManager.cs b/MCTG/Model/Managers/IDataManager.cs
index c8a4ca6..ccd40c7 100644
--- a/MCTG/Model/Managers/IDataManager.cs
+++ b/MCTG/Model/Managers/IDataManager.cs
@@ -1,44 +1,64 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Model
-{
- ///
- /// Interface that define the methods of a data serializer.
- ///
- public interface IDataManager
- {
- ///
- /// Save all the data in a file.
- ///
- /// The data to save.
- void Save(Dictionary> elements);
-
- ///
- /// Load all the data from a file.
- ///
- /// The data loaded.
- Dictionary> Load();
-
- ///
- /// Import an element to the collection of data.
- ///
- /// The type of the element to impoert
- /// The path containing the name of the file.
- /// A pair where the key is the entry in the data and the value is the value to add on this entry.
- public KeyValuePair Import(string pathToImport)
- where T : class;
-
- ///
- /// Export an element from the collection of data.
- ///
- /// The type of the exported object.
- /// The object to export.
- /// The path containing the name of the file created.
- public void Export(T obj, string pathToExport)
- where T : class;
- }
-}
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Model
+{
+ ///
+ /// Define how to manage data.
+ ///
+ public interface IDataManager
+ {
+ ///
+ /// The data manager injected that know how to serialize the data.
+ /// The setter is actually public for testing purpose. It will be private after.
+ /// See:
+ ///
+ IDataSerializer Serializer { get; }
+
+ ///
+ /// The collection of all data. Each line of this dictionary has the type of the data as it key and the data for values.
+ ///
+ Dictionary> Data { get; }
+
+
+ ///
+ /// Load the data. Used to update data written in the save file.
+ /// See:
+ ///
+ void LoadData();
+
+ ///
+ /// Save the data. Call the Save method of the serializer.
+ /// See:
+ ///
+ void SaveData();
+
+ ///
+ /// Import data from a file.
+ /// See:
+ ///
+ /// The type of data to import.
+ /// The path containing the name of the file created.
+ void Import(string pathOfTheFile) where T : class;
+
+ ///
+ /// Export the data from the collection of data.
+ /// See:
+ ///
+ /// The type of data to export
+ /// The object to export
+ /// The path containing the name of the file created.
+ void Export(T obj, string pathToExport) where T : class;
+
+ ///
+ /// Get a list of an item in the data.
+ ///
+ /// The type of the item
+ /// The list of all the item found in the data.
+ ICollection GetFromData() where T : class;
+ }
+}
diff --git a/MCTG/Model/Managers/IDataSerializer.cs b/MCTG/Model/Managers/IDataSerializer.cs
new file mode 100644
index 0000000..eba0cb6
--- /dev/null
+++ b/MCTG/Model/Managers/IDataSerializer.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Model
+{
+ ///
+ /// Interface that define the methods of a data serializer.
+ ///
+ public interface IDataSerializer
+ {
+ ///
+ /// Save all the data in a file.
+ ///
+ /// The data to save.
+ void Save(Dictionary> elements);
+
+ ///
+ /// Load all the data from a file.
+ ///
+ /// The data loaded.
+ Dictionary> Load();
+
+ ///
+ /// Import an element to the collection of data.
+ ///
+ /// The type of the element to impoert
+ /// The path containing the name of the file.
+ /// A pair where the key is the entry in the data and the value is the value to add on this entry.
+ public KeyValuePair Import(string pathToImport)
+ where T : class;
+
+ ///
+ /// Export an element from the collection of data.
+ ///
+ /// The type of the exported object.
+ /// The object to export.
+ /// The path containing the name of the file created.
+ public void Export(T obj, string pathToExport)
+ where T : class;
+ }
+}
diff --git a/MCTG/Model/Managers/IPasswordManager.cs b/MCTG/Model/Managers/IPasswordManager.cs
new file mode 100644
index 0000000..59d85d7
--- /dev/null
+++ b/MCTG/Model/Managers/IPasswordManager.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Model
+{
+ ///
+ /// Define how to manage passwords.
+ ///
+ public interface IPasswordManager
+ {
+ ///
+ /// Hash a plain text password.
+ ///
+ /// The plain password to hash.
+ /// The hashed password.
+ public string HashPassword(string password);
+
+ ///
+ /// Verify a plain text password with a hashed one.
+ ///
+ /// The hashed password.
+ /// The plain text password.
+ /// True is the password is correct, false otherwise.
+ public bool VerifyPassword(string hashedPassword,string password);
+ }
+}
diff --git a/MCTG/Model/Managers/IRecipeManager.cs b/MCTG/Model/Managers/IRecipeManager.cs
new file mode 100644
index 0000000..5a7fe22
--- /dev/null
+++ b/MCTG/Model/Managers/IRecipeManager.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Model
+{
+ ///
+ /// Define how to manage recipes.
+ ///
+ public interface IRecipeManager
+ {
+ ///
+ /// Get or set the currently selected recipe.
+ ///
+ Recipe? CurrentSelected { get; set; }
+
+ ///
+ /// Get all the recipe in the data.
+ ///
+ /// The RecipeCollection containing the recipes stored in the data.
+ RecipeCollection GetAllRecipes();
+
+ ///
+ /// Get the recipe corresponding to the id.
+ ///
+ /// The id of the recipe we want.
+ /// The recipe corresponding to the id.
+ Recipe GetRecipeFromId(int id);
+
+ ///
+ /// Search in data the recipes containing this part of title.
+ ///
+ /// Search string.
+ /// The RecipeCollection of recipes corresponding to the search.
+ RecipeCollection SearchRecipeByTitle(string title);
+
+ ///
+ /// Get all the recipes created by the author.
+ ///
+ /// The author's mail.
+ /// The RecipeCollection of the recipe created by the author.
+ RecipeCollection GetRecipeByAuthor(string authorMail);
+
+ ///
+ /// Get an ordored list of the recipes sorted by the priority list.
+ ///
+ /// The priority list.
+ /// The RecipeCollection ordored by the priority list.
+ RecipeCollection GetRecipesByPriorityOrder(IEnumerable priority);
+
+ ///
+ /// Add a recipe to the data.
+ ///
+ /// The recipe to add.
+ /// Weither the adding succed or not.
+ bool AddRecipeToData(Recipe recipe);
+
+ ///
+ /// Modify the recipe with a new one in the data.
+ ///
+ /// The new recipe
+ /// Weither the modification succed or not.
+ bool ModifyCurrentSelected(Recipe newRecipe);
+ }
+}
diff --git a/MCTG/Model/Managers/IUserManager.cs b/MCTG/Model/Managers/IUserManager.cs
new file mode 100644
index 0000000..9713ed5
--- /dev/null
+++ b/MCTG/Model/Managers/IUserManager.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Model
+{
+ ///
+ /// Define how to manage an user.
+ ///
+ public interface IUserManager
+ {
+ ///
+ /// Get the current connected user. Null if no user is connected.
+ ///
+ User? CurrentConnected { get; }
+
+ ///
+ /// Get the password manager used to hash passords.
+ ///
+ IPasswordManager PasswordManager { get; }
+
+ ///
+ /// Get a collection of all users in the data.
+ ///
+ /// A collection of all user.
+ ICollection GetAllUsers();
+
+ ///
+ /// Get an user by his email.
+ ///
+ /// The user's mail.
+ /// The user corresponding to the mail.
+ User GetUserFromMail(string mail);
+
+ ///
+ /// Create a new user. The mail and the password are required. Other can be null.
+ /// This function use the password manager to hash the plain text password.
+ ///
+ /// The user's mail address.
+ /// The user's plain text password.
+ /// The user's name.
+ /// The user's surname.
+ /// The user's profile picture.
+ /// A new user.
+ User CreateUser(string mail, string password,
+ string? name = null, string? surname = null, string? profilePict = null);
+
+ ///
+ /// Add an user in the data.
+ ///
+ /// The user to add.
+ /// True is the user was correctly added to the data. False otherwise.
+ bool AddUserToData(User user);
+
+ ///
+ /// Modify the currently connected user.
+ ///
+ /// An user containing new user's properties to changes.
+ ///
+ bool ModifyCurrentConnected(User newUser);
+
+ ///
+ /// Log in an user. If the connection succed, pass the connected user to .
+ ///
+ /// The User's mail.
+ /// The User's (plain text) password.
+ /// True if the connection succed, false otherwise.
+ bool LogIn(string mail, string password);
+
+ ///
+ /// Log out the current connected user.
+ ///
+ void LogOut();
+ }
+}
diff --git a/MCTG/Model/Managers/MasterManager.cs b/MCTG/Model/Managers/MasterManager.cs
index aed3c0b..551a6a1 100644
--- a/MCTG/Model/Managers/MasterManager.cs
+++ b/MCTG/Model/Managers/MasterManager.cs
@@ -1,161 +1,57 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading.Tasks;
-namespace Model.Managers
+
+namespace Model
{
///
/// The Main manager of the model.
///
- public class MasterManager : INotifyPropertyChanged
+ public class MasterManager
{
#region Attributes & Properties
- public event PropertyChangedEventHandler? PropertyChanged;
-
- private RecipeCollection _recipesInSearch = new RecipeCollection("");
-
- ///
- /// The currently connected user. 'null' if no user is connected.
- ///
- public User? CurrentConnectedUser { get; private set; }
-
- public RecipeCollection RecipesInSearch
- {
- get => _recipesInSearch;
- set
- {
- _recipesInSearch = value;
- OnPropertyChange();
- }
- }
-
///
- /// The collection of all recipes loaded.
+ /// Manage the data of the application.
///
- public RecipeCollection Recipes { get; private set; }
+ public IDataManager Data { get; private set; }
///
- /// The collection of all users loaded.
+ /// Manage the recipes of the application.
///
- public List Users { get; private set; }
+ public IRecipeManager Recipe { get; private set; }
///
- /// The data manager for load, save, export and import data.
+ /// Manage the users of the application.
///
- public DataManager DataMgr { get; private set; }
+ public IUserManager User { get; private set; }
#endregion
#region Constructors
///
/// Constructor of the MasterManager.
///
- /// The serializer for the data.
- public MasterManager(IDataManager dataManager)
+ /// The data manager.
+ /// The recipes manager.
+ /// The users manager.
+ public MasterManager(IDataManager dataManager,
+ IRecipeManager recipeManager,
+ IUserManager userManager)
{
- DataMgr = new DataManager(dataManager);
- CurrentConnectedUser = null;
- Recipes = DataMgr.GetRecipes("all recipes");
- RecipesInSearch = DataMgr.GetRecipes("search on");
- Users = DataMgr.GetUsers();
+ Data = dataManager;
+ Recipe = recipeManager;
+ User = userManager;
}
#endregion
#region Methods
///
- /// Log in an user. Test if the log in information are correct then connect the user.
+ /// Setup all the necessary parameters before start.
///
- /// The user's mail
- /// The user's password.
- /// True if the user was correctly connected, false if there is something wrong.
- ///
- public bool Login(string mail, string password)
+ public void Setup()
{
- if (Users is null || Users.Count == 0)
- throw new ArgumentNullException("There is no users registred.");
-
-#if DEBUG
- if (mail == "admin")
- {
- CurrentConnectedUser = Users.FirstOrDefault(u => u.Mail == "admin@mctg.fr");
- return true;
- }
-#endif
-
- User? user = Users.Find(u => u.Mail == mail);
-
- if (user is null || !user.psswMgr.VerifyPassword(user.Password, password))
- return false;
-
- CurrentConnectedUser = user;
- return true;
- }
-
- ///
- /// Log out an user.
- ///
- /// True if the user is correctly diconnected, false otherwise.
- public bool Logout()
- {
- if (CurrentConnectedUser is null)
- return false;
-
- CurrentConnectedUser = null;
- return true;
+ Data.LoadData();
}
-
- ///
- /// Register an user.
- ///
- /// The new user to add in the database.
- /// False if there is a problem with the registerement, true otherwise.
- public bool Register(User newuser)
- {
- try
- {
- User user = newuser;
- DataMgr.Data[nameof(User)].Add(user);
- Users = DataMgr.GetUsers();
- }
- catch (ArgumentException e)
- {
- Debug.WriteLine(e.Message);
- return false;
- }
-
- return true;
- }
-
- ///
- /// Add a recipe to the database.
- ///
- /// The recipe to add.
- public void AddRecipe(Recipe recipe)
- {
- DataMgr.Data[nameof(Recipe)].Add(recipe);
- Recipes = DataMgr.GetRecipes();
- }
-
- ///
- /// Get the current connected user's personal recipes.
- ///
- /// The current connected user's personal recipes.
- public RecipeCollection GetCurrentUserRecipes()
- => new RecipeCollection("User recipes",
- DataMgr.GetRecipes().ToList().FindAll(r => r.AuthorMail == CurrentConnectedUser?.Mail).ToArray());
-
- ///
- /// Notify property change handler.
- ///
- /// the name of the property that change.
- public void OnPropertyChange([CallerMemberName] string propertyName = "")
- => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ #endregion
}
- #endregion
}
diff --git a/MCTG/Model/Model.csproj b/MCTG/Model/Model.csproj
index 97854fe..097e9d6 100644
--- a/MCTG/Model/Model.csproj
+++ b/MCTG/Model/Model.csproj
@@ -1,10 +1,15 @@
-
-
-
- net7.0
- enable
- enable
- Debug;Release;CI
-
-
-
+
+
+
+ net7.0
+ enable
+ enable
+ Debug;Release;CI
+
+
+
+
+
+
+
+
diff --git a/MCTG/Model/Recipes/ReadOnlyObservableRecipeCollection.cs b/MCTG/Model/Recipes/ReadOnlyObservableRecipeCollection.cs
new file mode 100644
index 0000000..8def54c
--- /dev/null
+++ b/MCTG/Model/Recipes/ReadOnlyObservableRecipeCollection.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Model
+{
+ public class ReadOnlyObservableRecipeCollection : ReadOnlyObservableCollection
+ {
+ private RecipeCollection _recipeObserved;
+
+ public string Description => _recipeObserved.Description;
+
+ public ReadOnlyObservableRecipeCollection(RecipeCollection recipeCollection)
+ : base(recipeCollection)
+ {
+ _recipeObserved = recipeCollection;
+ }
+ }
+}
diff --git a/MCTG/Model/Recipes/Recipe.cs b/MCTG/Model/Recipes/Recipe.cs
index 7ba928a..9c48274 100644
--- a/MCTG/Model/Recipes/Recipe.cs
+++ b/MCTG/Model/Recipes/Recipe.cs
@@ -52,6 +52,12 @@ namespace Model
}
}
+ ///
+ /// Priority of this recipe.
+ ///
+ [DataMember(Name = "priority")]
+ public Priority Priority { get; private set; }
+
///
/// The Title of the recipe.
/// Set to "No title." when the value passed is null, empty or contain white spaces.
@@ -110,13 +116,15 @@ namespace Model
///
/// The title of the recipe
/// The type of the recipe.
+ /// The priority of this recipe.
/// The id of the recipe. If not given, get a new id.
/// The name of the user that create this recipe.
/// The image that represent the recipe
- public Recipe(string title, RecipeType type, int? id, string? authorMail, string? picture)
+ public Recipe(string title, RecipeType type, Priority priority, int? id, string? authorMail, string? picture)
{
Title = title;
Type = type;
+ Priority = priority;
Image = picture;
AuthorMail = authorMail;
@@ -134,18 +142,21 @@ namespace Model
else Id = (int)id;
}
- public Recipe(string title, RecipeType type, int? id, string? authorMail)
- : this(title, type, id, authorMail, null)
+ ///
+ public Recipe(string title, RecipeType type, Priority priority, int? id, string? authorMail)
+ : this(title, type, priority, id, authorMail, null)
{
}
- public Recipe(string title, RecipeType type)
- : this(title, type, null, null)
+ ///
+ public Recipe(string title, RecipeType type, Priority priority)
+ : this(title, type, priority, null, null)
{
}
+ ///
public Recipe(string title)
- : this(title, RecipeType.Unspecified)
+ : this(title, RecipeType.Unspecified, Priority.Fast)
{
}
#endregion
@@ -176,7 +187,7 @@ namespace Model
/// Concatenate the list of ingredients in a single string
///
/// The list of ingredients in string format
- private string ConcatIngredients()
+ public string ConcatIngredients()
{
StringBuilder sb = new StringBuilder();
foreach (Ingredient ingredient in Ingredients)
@@ -197,9 +208,11 @@ namespace Model
public override bool Equals(object? obj)
{
- var item = obj as Recipe;
- if (item == null) return false;
- return Equals(obj);
+ if (ReferenceEquals(obj, null)) return false;
+ if (ReferenceEquals(obj, this)) return true;
+ if (GetType() != obj.GetType()) return false;
+
+ return Equals(obj as Recipe);
}
public override int GetHashCode()
@@ -209,21 +222,7 @@ namespace Model
public override string ToString()
{
- StringBuilder sb = new StringBuilder($"[Recipe n°{Id}] - {Title}\n");
- foreach (PreparationStep ps in PreparationSteps)
- {
- sb.AppendFormat("\t* {0}\n", ps.ToString());
- }
- sb.AppendLine();
- sb.AppendLine(ConcatIngredients());
- sb.AppendLine();
- foreach (Review review in Reviews)
- {
- sb.AppendLine(review.ToString());
- }
- sb.AppendLine();
- sb.AppendLine($"Posted by: {AuthorMail?.ToString()}");
- return sb.ToString();
+ return $"'{Title}' [{Id}] - {Type}, {Priority} | {AuthorMail} | {Image}";
}
#endregion
}
diff --git a/MCTG/Model/Recipes/RecipeCollection.cs b/MCTG/Model/Recipes/RecipeCollection.cs
index 7579d5f..c58bda2 100644
--- a/MCTG/Model/Recipes/RecipeCollection.cs
+++ b/MCTG/Model/Recipes/RecipeCollection.cs
@@ -2,6 +2,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.ComponentModel;
using System.Text;
namespace Model
@@ -31,53 +32,32 @@ namespace Model
_description = "No description.";
else
_description = value;
+
+ OnPropertyChanged(new PropertyChangedEventArgs("Description"));
}
}
#endregion
#region Constructors
///
- /// Construct a new collection of _recipes.
+ /// Construct a new collection of recipes.
///
- /// A short description of what this list will contain
- /// Recipes to add in this new collection
- public RecipeCollection(string description, params Recipe[] recipes)
+ /// A short description of what this list will contain.
+ /// Recipes to add in this new collection.
+ public RecipeCollection(string description, ICollection recipes)
: base(recipes)
{
Description = description;
}
- #endregion
- #region Methods
- ///
- /// Find a recipe in this list by giving the id.
- ///
- /// The id of the list we are looking for
- /// The recipe of the id given
- ///
- public Recipe? GetRecipeById(int id)
+ ///
+ public RecipeCollection(string description)
+ : base()
{
- Recipe? recipe = this.ToList().Find(r => r.Id == id);
-
- if (recipe == null) throw new ArgumentException("No _recipes match the given id.");
- return recipe;
- }
-
- ///
- /// Utility to find a recipe by his _name.
- ///
- /// The string for the search
- /// A collection of Recipe where their Title contain the string.
- public RecipeCollection ResearchByName(string str)
- {
- if (string.IsNullOrEmpty(str))
- return this;
-
- return new RecipeCollection(
- description: $"Results of the research: {str}",
- recipes: this.ToList().FindAll(x => x.Title.ToLower().Contains(str.ToLower())).ToArray());
}
+ #endregion
+ #region Methods
public virtual bool Equals(RecipeCollection? other)
{
if (other == null) return false;
diff --git a/MCTG/Model/Recipes/Review.cs b/MCTG/Model/Recipes/Review.cs
index f55d6e7..2640760 100644
--- a/MCTG/Model/Recipes/Review.cs
+++ b/MCTG/Model/Recipes/Review.cs
@@ -1,4 +1,4 @@
-using Model.Managers;
+using Model;
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/MCTG/Model/User/IPasswordManager.cs b/MCTG/Model/User/IPasswordManager.cs
deleted file mode 100644
index 5ca0732..0000000
--- a/MCTG/Model/User/IPasswordManager.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Model
-{
- public interface IPasswordManager
- {
- public string HashPassword(string password);
- public bool VerifyPassword(string hashedPassword,string password);
-
- }
-}
diff --git a/MCTG/Model/User/Priority.cs b/MCTG/Model/Users/Priority.cs
similarity index 100%
rename from MCTG/Model/User/Priority.cs
rename to MCTG/Model/Users/Priority.cs
diff --git a/MCTG/Model/User/User.cs b/MCTG/Model/Users/User.cs
similarity index 60%
rename from MCTG/Model/User/User.cs
rename to MCTG/Model/Users/User.cs
index 1085d0e..86dd823 100644
--- a/MCTG/Model/User/User.cs
+++ b/MCTG/Model/Users/User.cs
@@ -1,205 +1,184 @@
-
-using System.Runtime.CompilerServices;
-using System.Runtime.Serialization;
-using System.ComponentModel;
-
-namespace Model
-{
- ///
- /// A user is an entity with a _name, a surname, mail, profilePict and a list of priority.
- /// This user can login with an Id and a password
- ///
- [DataContract(Name = "user")]
- public class User : IEquatable , INotifyPropertyChanged
- {
- #region Private Attributes
-
- [DataMember] private string name="";
- [DataMember] private string surname="";
- [DataMember] private string mail = "";
- [DataMember] private string picture = "";
- [DataMember] private string password = "";
- [DataMember] private List priorities;
-
- public event PropertyChangedEventHandler? PropertyChanged;
- #endregion
-
- #region Properties
-
- ///
- /// Property to get Name of users and a setter
- ///
- /// Setter have Exception which is trigger when Name is null
- public string Name
- {
- get { return name; }
- set
- {
-
- name = value;
- OnPropertyChanged();
- }
- }
-
- ///
- /// Property to get Surname of users and a setter
- ///
- /// Setter have Exception which is trigger when Surname is null
- public string Surname
- {
- get { return surname; }
- set
- {
-
- surname = value;
- OnPropertyChanged();
- }
- }
-
- ///
- /// Property to get mail of users and a setter
- ///
- /// User's mail will serve to log the user. So there's no setter, just an init. User will enter one time his email at his
- /// account creation.
- public string Mail
- {
- get { return mail; }
- private init
- {
- if (string.IsNullOrWhiteSpace(value))
- {
- throw new ArgumentException("Impossible d'avoir un champ Email vide!");
- }
- mail = value;
- }
- }
-
- public string Password
- {
- get => password;
- set => password = value;
-
- }
-
-
- ///
- /// For now, we define the ProfilPict as a string which is "PhotoParDefaut"
- /// when the value is null.
- ///
- public string ProfilPict
- {
- get => picture;
- set => picture = value;
-
- }
-
- ///
- /// This is the list of priorities specific tu the user. This list is initiate
- /// by default. User could change it at will.
- ///
-
- public List Priorities
- {
- get => priorities;
- set=> priorities = value;
- }
-
- public override bool Equals(object? other)
- {
- if (other == null) return false;
- if (other == this) return true;
- return Equals(other);
- }
-
- public bool Equals(User? other)
- {
- if (other == null) return false;
- return Name.Equals(other.Name) && Surname.Equals(other.Surname) && Mail.Equals(other.Mail);
- }
-
- public override int GetHashCode()
- {
- throw new NotImplementedException();
- }
-
-
- protected void OnPropertyChanged ([CallerMemberName] string? propertyName = null)
- {
- if (PropertyChanged != null)
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
- }
-
- public override string ToString()
- {
- return $"{Name} {Surname}";
- }
-
- [DataMember(Name = "passmgr")]
- public IPasswordManager psswMgr { get; private set; }
-
-
- #endregion
-
- #region Constructors
-
- ///
- /// Construtors of user.
- ///
- /// The name of the user
- /// The surname of the user
- /// The user needs an email to login.
- /// The password of the new user.
- /// The password manager to manage the user password.
- public User(string name, string surname, string mail, string password, IPasswordManager passwordManager)
- {
- Name = name;
- Surname = surname;
- Mail = mail;
- psswMgr = passwordManager;
- Password = psswMgr.HashPassword(password);
- priorities = new List {
- Priority.Gourmet,
- Priority.Economic,
- Priority.Fast,
- Priority.Light,
- Priority.Easy};
- ProfilPict = picture;
-
- }
-
- ///
- ///
- ///
- public User(string name, string surname, string mail, string password)
- : this(name, surname,mail, password, new PasswordSHA256())
- {
-
- }
-
- ///
- ///
- ///
- public User()
- : this("John", "Doe", "truc@gmail.com", "mdp")
- {
-
- }
-
- ///
- ///
- ///
- public User (User user)
- {
- Name = user.Name;
- Surname = user.Surname;
- Mail = user.Mail;
- psswMgr = user.psswMgr;
- Password = user.Password;
- priorities = user.Priorities;
- ProfilPict = user.ProfilPict;
- }
-
-
- #endregion
- }
-}
+
+using System.Runtime.CompilerServices;
+using System.Runtime.Serialization;
+using System.ComponentModel;
+using System.Collections.ObjectModel;
+
+namespace Model
+{
+ ///
+ /// A user is an entity with a name, a surname, mail, profilePict and a list of priority.
+ /// This user can login with an Id and a password
+ ///
+ [DataContract(Name = "user")]
+ public class User : IEquatable , INotifyPropertyChanged
+ {
+ #region Private Attributes
+ [DataMember] private string _name = "";
+ [DataMember] private string _surname = "";
+ [DataMember] private string _mail = "";
+
+ [DataMember(Name = "priorities")]
+ public ObservableCollection _priorities { get; private set; } = new ObservableCollection
+ {
+ Priority.Gourmet,
+ Priority.Economic,
+ Priority.Fast,
+ Priority.Light,
+ Priority.Easy
+ };
+
+ public event PropertyChangedEventHandler? PropertyChanged;
+ #endregion
+
+ #region Properties
+ ///
+ /// Property to get Name of users and a setter
+ ///
+ /// Setter have Exception which is trigger when Name is null
+ public string Name
+ {
+ get { return _name; }
+ set
+ {
+
+ _name = value;
+ OnPropertyChanged();
+ }
+ }
+
+ ///
+ /// Property to get Surname of users and a setter
+ ///
+ /// Setter have Exception which is trigger when Surname is null
+ public string Surname
+ {
+ get { return _surname; }
+ set
+ {
+
+ _surname = value;
+ OnPropertyChanged();
+ }
+ }
+
+ ///
+ /// Property to get mail of users and a setter
+ ///
+ /// User's mail will serve to log the user. So there's no setter, just an init. User will enter one time his email at his
+ /// account creation.
+ public string Mail
+ {
+ get { return _mail; }
+ private init
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ throw new ArgumentException("Impossible d'avoir un champ Email vide!");
+ }
+ _mail = value;
+ }
+ }
+
+ ///
+ /// The user's hashed password. The hashed method is defined with the PasswordManager.
+ /// See: .
+ ///
+ [DataMember(Name = "hashedpass")]
+ public string Password { get; private set; } = "";
+
+ ///
+ /// For now, we define the ProfilePict as a string which is "PhotoParDefaut"
+ /// when the value is null.
+ ///
+ [DataMember(Name = "profilepic")]
+ public string ProfilePict { get; private set; } = "default_picture.png";
+
+ ///
+ /// This is the list of priorities specific tu the user. This list is initiate
+ /// by default. User could change it at will.
+ ///
+ public ReadOnlyObservableCollection Priorities { get; private set; }
+ #endregion
+
+ #region Methods
+ public override bool Equals(object? other)
+ {
+ if (ReferenceEquals(other, null)) return false;
+ if (ReferenceEquals(other, this)) return true;
+ if (GetType() != other.GetType()) return false;
+
+ return Equals(other as User);
+ }
+
+ public bool Equals(User? other)
+ {
+ if (other == null) return false;
+ if (other == this) return true;
+
+ return Name.Equals(other.Name) && Surname.Equals(other.Surname) && Mail.Equals(other.Mail);
+ }
+
+ public override int GetHashCode()
+ {
+ throw new NotImplementedException();
+ }
+
+ protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
+ {
+ if (PropertyChanged != null)
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ public override string ToString()
+ {
+ return $"{Name} {Surname}";
+ }
+ #endregion
+
+ #region Constructors
+ ///
+ /// Construtors of user.
+ ///
+ /// The name of the user
+ /// The surname of the user
+ /// The user needs an email to login.
+ /// The password of the new user.
+ public User(string name, string surname, string mail, string hashedPassword)
+ {
+ Name = name;
+ Surname = surname;
+ Mail = mail;
+ Password = hashedPassword;
+ Priorities = new ReadOnlyObservableCollection(_priorities);
+ }
+
+ ///
+ /// Profile picture of the new user.
+ public User(string name, string surname, string mail, string hashedPassword, string profilePict)
+ : this(name, surname, mail, hashedPassword)
+ {
+ ProfilePict = profilePict;
+ }
+
+ ///
+ public User()
+ : this("John", "Doe", "truc@gmail.com", "mdp")
+ {
+ }
+
+ ///
+ /// The user to copy.
+ public User(User user)
+ {
+ Name = user.Name;
+ Surname = user.Surname;
+ Mail = user.Mail;
+ Password = user.Password;
+ _priorities = user._priorities;
+ Priorities = new ReadOnlyObservableCollection(_priorities);
+ ProfilePict = user.ProfilePict;
+ }
+ #endregion
+ }
+}
diff --git a/MCTG/DataPersistence/DataContractJSON.cs b/MCTG/Persistance/DataPersistence/DataContractJSON.cs
similarity index 92%
rename from MCTG/DataPersistence/DataContractJSON.cs
rename to MCTG/Persistance/DataPersistence/DataContractJSON.cs
index b32cbd2..bdb25d4 100644
--- a/MCTG/DataPersistence/DataContractJSON.cs
+++ b/MCTG/Persistance/DataPersistence/DataContractJSON.cs
@@ -1,20 +1,12 @@
using Model;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
-using System.Text;
-using System.Threading.Tasks;
-using System.Xml;
-using System.Xml.Linq;
namespace DataPersistence
{
///
/// Define a serializer to manage JSON files.
///
- public class DataContractJSON : IDataManager
+ public class DataContractJSON : IDataSerializer
{
#region Attributes
private string _jsonFolderPath;
@@ -38,6 +30,7 @@ namespace DataPersistence
{
typeof(Recipe),
typeof(RecipeType),
+ typeof(Priority),
typeof(Review),
typeof(User),
typeof(Ingredient),
diff --git a/MCTG/DataPersistence/DataContractXML.cs b/MCTG/Persistance/DataPersistence/DataContractXML.cs
similarity index 92%
rename from MCTG/DataPersistence/DataContractXML.cs
rename to MCTG/Persistance/DataPersistence/DataContractXML.cs
index b3d1d50..e8443c4 100644
--- a/MCTG/DataPersistence/DataContractXML.cs
+++ b/MCTG/Persistance/DataPersistence/DataContractXML.cs
@@ -1,19 +1,13 @@
using Model;
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Runtime.Serialization;
-using System.Text;
-using System.Threading.Tasks;
using System.Xml;
-using System.Xml.Linq;
namespace DataPersistence
{
///
/// Define a serializer to manage XML files.
///
- public class DataContractXML : IDataManager
+ public class DataContractXML : IDataSerializer
{
#region Attributes
private string _xmlFolderPath;
@@ -46,11 +40,11 @@ namespace DataPersistence
{
typeof(Recipe),
typeof(RecipeType),
+ typeof(Priority),
typeof(Review),
typeof(User),
typeof(Ingredient),
- typeof(Quantity),
- typeof(PasswordSHA256)
+ typeof(Quantity)
},
PreserveObjectReferences = true
};
diff --git a/MCTG/DataPersistence/DataPersistence.csproj b/MCTG/Persistance/DataPersistence/DataPersistence.csproj
similarity index 79%
rename from MCTG/DataPersistence/DataPersistence.csproj
rename to MCTG/Persistance/DataPersistence/DataPersistence.csproj
index 4b28c7b..71cce12 100644
--- a/MCTG/DataPersistence/DataPersistence.csproj
+++ b/MCTG/Persistance/DataPersistence/DataPersistence.csproj
@@ -6,8 +6,8 @@
enable
-
-
+
+
diff --git a/MCTG/Persistance/FakePersistance/FakePersistance.csproj b/MCTG/Persistance/FakePersistance/FakePersistance.csproj
new file mode 100644
index 0000000..2df6af7
--- /dev/null
+++ b/MCTG/Persistance/FakePersistance/FakePersistance.csproj
@@ -0,0 +1,14 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+
diff --git a/MCTG/DataPersistence/Stubs.cs b/MCTG/Persistance/FakePersistance/Stubs.cs
similarity index 89%
rename from MCTG/DataPersistence/Stubs.cs
rename to MCTG/Persistance/FakePersistance/Stubs.cs
index 925dbae..cfad2b9 100644
--- a/MCTG/DataPersistence/Stubs.cs
+++ b/MCTG/Persistance/FakePersistance/Stubs.cs
@@ -1,18 +1,18 @@
-using Model;
+using Managers;
+using Model;
using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Threading.Tasks;
-namespace DataPersistence
+
+namespace FakePersistance
{
///
/// The subs class is a group of prefabricated object that can only be loaded. It only use is for testing.
///
- public class Stubs : IDataManager
+ public class Stubs : IDataSerializer
{
+ private IPasswordManager psswdMgr = new PasswordSHA256Manager();
+
public Dictionary> Load()
{
Dictionary> data = new Dictionary>
@@ -22,7 +22,7 @@ namespace DataPersistence
nameof(Recipe),
new List
-
+
+
+
+
-
+
+
-
+ MSBuild:Compile
-
+ MSBuild:Compile
-
+ MSBuild:Compile
-
+ MSBuild:Compile
-
+ MSBuild:Compile
-
+ MSBuild:Compile
-
+ MSBuild:Compile
-
+ MSBuild:Compile
-
+ MSBuild:Compile
-
+ MSBuild:Compile
-
+ MSBuild:Compile
-
+ MSBuild:Compile
-
+ MSBuild:Compile