fixed merge
continuous-integration/drone/push Build was killed Details

pull/65/head
Roxane ROSSETTO 2 years ago
commit 907eb84ba0

@ -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

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

@ -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.") { }
}
}

@ -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) { }
}
}

@ -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.") { }
}
}

@ -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.") { }
}
}

@ -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.") { }
}
}

@ -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.") { }
}
}

@ -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) { }
}
}

@ -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.") { }
}
}

@ -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"
]
}

@ -7,9 +7,12 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\DataPersistence\DataPersistence.csproj" />
<ProjectReference Include="..\Model\Model.csproj" />
<ItemGroup>
<ProjectReference Include="..\Managers\Managers.csproj" />
<ProjectReference Include="..\Model\Model.csproj" />
<ProjectReference Include="..\Persistance\DataPersistence\DataPersistence.csproj" />
<ProjectReference Include="..\Persistance\FakePersistance\FakePersistance.csproj" />
</ItemGroup>
</Project>

@ -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;
}
}

@ -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;
}
}

@ -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;

@ -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)
{

@ -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<Recipe>(path);
masterMgr.Data.Import<Recipe>(path);
}
catch(ArgumentNullException e)
{

@ -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();
}
}

@ -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<Selector<IMenu>> 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();

@ -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
{

@ -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();

@ -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
}

@ -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);
}
}
}

@ -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");
}
}
}

@ -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<IMenu>();
MenuCallStack.Push(firstMenu);
}

@ -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.");
}
}

@ -0,0 +1,56 @@
using Model;
namespace Managers
{
/// <summary>
/// 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.
/// </summary>
public class DataDefaultManager : IDataManager
{
#region Attributes & Properties
public IDataSerializer Serializer { get; set; }
public Dictionary<string, List<object>> Data { get; private set; }
#endregion
#region Constructors
/// <summary>
/// Constructor of the DataDefaultManager class. Take a IDataManager that will provide methods for the serialisation of the data.
/// </summary>
/// <param name="dataManager">The data manager that know how to serialize a file.</param>
/// <param name="data">The data set of the application.</param>
public DataDefaultManager(IDataSerializer dataManager,
Dictionary<string, List<object>>? data = null)
{
Serializer = dataManager;
if (data is null)
Data = new Dictionary<string, List<object>>();
else
Data = data;
}
#endregion
#region Methods
public void LoadData()
=> Data = Serializer.Load();
public void SaveData()
=> Serializer.Save(Data);
public void Import<T>(string pathOfTheFile)
where T : class
{
KeyValuePair<string, T> import = Serializer.Import<T>(pathOfTheFile);
Data[import.Key].Add(import.Value);
}
public void Export<T>(T obj, string pathToExport)
where T : class
=> Serializer.Export<T>(obj, pathToExport);
public ICollection<T> GetFromData<T>() where T : class
=> new List<T>(Data[typeof(T).Name].Cast<T>());
#endregion
}
}

@ -1,16 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Configurations>Debug;Release;CI</Configurations>
</PropertyGroup>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\DataPersistence\DataPersistence.csproj" />
<ProjectReference Include="..\AppException\AppException.csproj" />
<ProjectReference Include="..\Model\Model.csproj" />
</ItemGroup>
</Project>
</ItemGroup>
</Project>

@ -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)
{

@ -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<Recipe>().ToArray());
}
public RecipeCollection GetRecipeByAuthor(string authorMail)
{
User? author = _dataManager.GetFromData<User>()
.ToList()
.Find(u => u.Mail == authorMail);
if (author is null)
throw new UserNotFoundException(authorMail);
IEnumerable<Recipe> recipes = from Recipe r in _dataManager.GetFromData<Recipe>()
where r.AuthorMail == author.Mail
select r;
return new RecipeCollection(
$"{author.Name} {author.Surname}'s recipes", recipes.ToArray());
}
public RecipeCollection SearchRecipeByTitle(string title)
{
IEnumerable<Recipe> recipes = from Recipe recipe in _dataManager.GetFromData<Recipe>()
where recipe.Title.Contains(title)
select recipe;
return new RecipeCollection(
$"Search for '{title}'", recipes.ToArray());
}
public Recipe GetRecipeFromId(int id)
{
Recipe? recipe = _dataManager.GetFromData<Recipe>()
.ToList()
.Find(r => r.Id == id);
if (recipe is null)
throw new RecipeNotFoundException();
return recipe;
}
public RecipeCollection GetRecipesByPriorityOrder(IEnumerable<Priority> priorities)
{
List<Recipe> recipes = new List<Recipe>();
IEnumerable<Recipe> recipesWithCurrentPriority;
foreach (Priority priority in priorities)
{
recipesWithCurrentPriority = from Recipe recipe in _dataManager.GetFromData<Recipe>()
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<Recipe>().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;
}
}
}

@ -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<User> GetAllUsers()
{
return _dataManager.GetFromData<User>();
}
public User GetUserFromMail(string mail)
{
User? user = _dataManager.GetFromData<User>().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<User>()
.FirstOrDefault(u => u.Mail == "admin@mctg.fr");
return true;
}
#endif
User? user = _dataManager.GetFromData<User>().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<User>().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));
}
}
}

@ -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
{
/// <summary>
/// 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.
/// </summary>
public class DataManager
{
#region Attributes & Properties
/// <summary>
/// The data manager injected that know how to serialize the data.
/// <br/><remarks><i>The setter is actually public for testing purpose. It will be private after.</i></remarks>
/// <br/>See: <see cref="IDataManager"/>
/// </summary>
public IDataManager Serializer { get; set; }
/// <summary>
/// The collection of all data. Each line of this dictionary has the type of the data as it key and the data for values.
/// </summary>
public Dictionary<string, List<object>> Data { get; private set; }
#endregion
#region Constructors
/// <summary>
/// Constructor of the DataManager class. Take a IDataManager that will provide methods for the serialisation of the data.
/// </summary>
/// <param name="dataMgr">The data manager that know how to serialize a file.</param>
public DataManager(IDataManager dataMgr)
{
Serializer = dataMgr;
Data = Serializer.Load();
}
#endregion
#region Methods
/// <summary>
/// Reload the data. Useful to update new data written in the save file.
/// <br/>See: <see cref="IDataManager.Load"/>
/// </summary>
public void Reload()
=> Data = Serializer.Load();
/// <summary>
/// Save the data. Call the Save method of the serializer.
/// <br/>See: <see cref="IDataManager.Save(Dictionary{string, List{object}})"/>
/// </summary>
public void Save()
=> Serializer.Save(Data);
/// <summary>
/// Import data from a file.
/// <br/>See: <see cref="IDataManager.Import{T}(string)"/>
/// </summary>
/// <typeparam name="T">The type of data to import.</typeparam>
/// <param name="pathOfTheFile">The path containing the name of the file created.</param>
public void Import<T>(string pathOfTheFile)
where T : class
{
KeyValuePair<string, T> import = Serializer.Import<T>(pathOfTheFile);
Data[import.Key].Add(import.Value);
}
/// <summary>
/// Export the data from the collection of data.
/// <br/>See: <see cref="IDataManager.Export{T}(T, string)"/>
/// </summary>
/// <typeparam name="T">The type of data to export</typeparam>
/// <param name="obj">The object to export</param>
/// <param name="pathToExport">The path containing the name of the file created.</param>
public void Export<T>(T obj, string pathToExport)
where T : class
=> Serializer.Export<T>(obj, pathToExport);
/// <summary>
/// Get all the recipe from the data.
/// </summary>
/// <param name="rcTitle">The title to give for the Recipe Collection</param>
/// <returns>A RecipeCollection that contain all the recipe in the data.</returns>
public RecipeCollection GetRecipes(string rcTitle = "default")
=> new RecipeCollection(rcTitle, Data[nameof(Recipe)].Cast<Recipe>().ToArray());
/// <summary>
/// Get all the Users from the data.
/// </summary>
/// <returns>A list of all Users.</returns>
public List<User> GetUsers()
=> new List<User>(Data[nameof(User)].Cast<User>());
/// <summary>
/// Get a list of an item in the data.
/// </summary>
/// <typeparam name="T">The type of the item</typeparam>
/// <returns>The list of all the item found in the data.</returns>
public ICollection<T> GetFromData<T>() where T : class
=> new List<T>(Data[typeof(T).Name].Cast<T>());
#endregion
}
}

@ -1,44 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model
{
/// <summary>
/// Interface that define the methods of a data serializer.
/// </summary>
public interface IDataManager
{
/// <summary>
/// Save all the data in a file.
/// </summary>
/// <param name="elements">The data to save.</param>
void Save(Dictionary<string, List<object>> elements);
/// <summary>
/// Load all the data from a file.
/// </summary>
/// <returns>The data loaded.</returns>
Dictionary<string, List<object>> Load();
/// <summary>
/// Import an element to the collection of data.
/// </summary>
/// <typeparam name="T">The type of the element to impoert</typeparam>
/// <param name="pathToImport">The path containing the name of the file.</param>
/// <returns>A pair where the key is the entry in the data and the value is the value to add on this entry.</returns>
public KeyValuePair<string, T> Import<T>(string pathToImport)
where T : class;
/// <summary>
/// Export an element from the collection of data.
/// </summary>
/// <typeparam name="T">The type of the exported object.</typeparam>
/// <param name="obj">The object to export.</param>
/// <param name="pathToExport">The path containing the name of the file created.</param>
public void Export<T>(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
{
/// <summary>
/// Define how to manage data.
/// </summary>
public interface IDataManager
{
/// <summary>
/// The data manager injected that know how to serialize the data.
/// <br/><remarks><i>The setter is actually public for testing purpose. It will be private after.</i></remarks>
/// <br/>See: <see cref="IDataSerializer"/>
/// </summary>
IDataSerializer Serializer { get; }
/// <summary>
/// The collection of all data. Each line of this dictionary has the type of the data as it key and the data for values.
/// </summary>
Dictionary<string, List<object>> Data { get; }
/// <summary>
/// Load the data. Used to update data written in the save file.
/// <br/>See: <see cref="IDataSerializer.Load"/>
/// </summary>
void LoadData();
/// <summary>
/// Save the data. Call the Save method of the serializer.
/// <br/>See: <see cref="IDataSerializer.Save(Dictionary{string, List{object}})"/>
/// </summary>
void SaveData();
/// <summary>
/// Import data from a file.
/// <br/>See: <see cref="IDataSerializer.Import{T}(string)"/>
/// </summary>
/// <typeparam name="T">The type of data to import.</typeparam>
/// <param name="pathOfTheFile">The path containing the name of the file created.</param>
void Import<T>(string pathOfTheFile) where T : class;
/// <summary>
/// Export the data from the collection of data.
/// <br/>See: <see cref="IDataSerializer.Export{T}(T, string)"/>
/// </summary>
/// <typeparam name="T">The type of data to export</typeparam>
/// <param name="obj">The object to export</param>
/// <param name="pathToExport">The path containing the name of the file created.</param>
void Export<T>(T obj, string pathToExport) where T : class;
/// <summary>
/// Get a list of an item in the data.
/// </summary>
/// <typeparam name="T">The type of the item</typeparam>
/// <returns>The list of all the item found in the data.</returns>
ICollection<T> GetFromData<T>() where T : class;
}
}

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model
{
/// <summary>
/// Interface that define the methods of a data serializer.
/// </summary>
public interface IDataSerializer
{
/// <summary>
/// Save all the data in a file.
/// </summary>
/// <param name="elements">The data to save.</param>
void Save(Dictionary<string, List<object>> elements);
/// <summary>
/// Load all the data from a file.
/// </summary>
/// <returns>The data loaded.</returns>
Dictionary<string, List<object>> Load();
/// <summary>
/// Import an element to the collection of data.
/// </summary>
/// <typeparam name="T">The type of the element to impoert</typeparam>
/// <param name="pathToImport">The path containing the name of the file.</param>
/// <returns>A pair where the key is the entry in the data and the value is the value to add on this entry.</returns>
public KeyValuePair<string, T> Import<T>(string pathToImport)
where T : class;
/// <summary>
/// Export an element from the collection of data.
/// </summary>
/// <typeparam name="T">The type of the exported object.</typeparam>
/// <param name="obj">The object to export.</param>
/// <param name="pathToExport">The path containing the name of the file created.</param>
public void Export<T>(T obj, string pathToExport)
where T : class;
}
}

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model
{
/// <summary>
/// Define how to manage passwords.
/// </summary>
public interface IPasswordManager
{
/// <summary>
/// Hash a plain text password.
/// </summary>
/// <param name="password">The plain password to hash.</param>
/// <returns>The hashed password.</returns>
public string HashPassword(string password);
/// <summary>
/// Verify a plain text password with a hashed one.
/// </summary>
/// <param name="hashedPassword">The hashed password.</param>
/// <param name="password">The plain text password.</param>
/// <returns>True is the password is correct, false otherwise.</returns>
public bool VerifyPassword(string hashedPassword,string password);
}
}

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model
{
/// <summary>
/// Define how to manage recipes.
/// </summary>
public interface IRecipeManager
{
/// <summary>
/// Get or set the currently selected recipe.
/// </summary>
Recipe? CurrentSelected { get; set; }
/// <summary>
/// Get all the recipe in the data.
/// </summary>
/// <returns>The RecipeCollection containing the recipes stored in the data.</returns>
RecipeCollection GetAllRecipes();
/// <summary>
/// Get the recipe corresponding to the id.
/// </summary>
/// <param name="id">The id of the recipe we want.</param>
/// <returns>The recipe corresponding to the id.</returns>
Recipe GetRecipeFromId(int id);
/// <summary>
/// Search in data the recipes containing this part of title.
/// </summary>
/// <param name="title">Search string.</param>
/// <returns>The RecipeCollection of recipes corresponding to the search.</returns>
RecipeCollection SearchRecipeByTitle(string title);
/// <summary>
/// Get all the recipes created by the author.
/// </summary>
/// <param name="authorMail">The author's mail.</param>
/// <returns>The RecipeCollection of the recipe created by the author.</returns>
RecipeCollection GetRecipeByAuthor(string authorMail);
/// <summary>
/// Get an ordored list of the recipes sorted by the priority list.
/// </summary>
/// <param name="priority">The priority list.</param>
/// <returns>The RecipeCollection ordored by the priority list.</returns>
RecipeCollection GetRecipesByPriorityOrder(IEnumerable<Priority> priority);
/// <summary>
/// Add a recipe to the data.
/// </summary>
/// <param name="recipe">The recipe to add.</param>
/// <returns>Weither the adding succed or not.</returns>
bool AddRecipeToData(Recipe recipe);
/// <summary>
/// Modify the <see cref="CurrentSelected"/> recipe with a new one in the data.
/// </summary>
/// <param name="newRecipe">The new recipe</param>
/// <returns>Weither the modification succed or not.</returns>
bool ModifyCurrentSelected(Recipe newRecipe);
}
}

@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model
{
/// <summary>
/// Define how to manage an user.
/// </summary>
public interface IUserManager
{
/// <summary>
/// Get the current connected user. Null if no user is connected.
/// </summary>
User? CurrentConnected { get; }
/// <summary>
/// Get the password manager used to hash passords.
/// </summary>
IPasswordManager PasswordManager { get; }
/// <summary>
/// Get a collection of all users in the data.
/// </summary>
/// <returns>A collection of all user.</returns>
ICollection<User> GetAllUsers();
/// <summary>
/// Get an user by his email.
/// </summary>
/// <param name="mail">The user's mail.</param>
/// <returns>The user corresponding to the mail.</returns>
User GetUserFromMail(string mail);
/// <summary>
/// Create a new user. The mail and the password are required. Other can be null.
/// <br/>This function use the password manager to hash the plain text password.
/// </summary>
/// <param name="mail">The user's mail address.</param>
/// <param name="password">The user's plain text password.</param>
/// <param name="name">The user's name.</param>
/// <param name="surname">The user's surname.</param>
/// <param name="profilePict">The user's profile picture.</param>
/// <returns>A new user.</returns>
User CreateUser(string mail, string password,
string? name = null, string? surname = null, string? profilePict = null);
/// <summary>
/// Add an user in the data.
/// </summary>
/// <param name="user">The user to add.</param>
/// <returns>True is the user was correctly added to the data. False otherwise.</returns>
bool AddUserToData(User user);
/// <summary>
/// Modify the currently connected user.
/// </summary>
/// <param name="newUser">An user containing new user's properties to changes.</param>
/// <returns></returns>
bool ModifyCurrentConnected(User newUser);
/// <summary>
/// Log in an user. If the connection succed, pass the connected user to <see cref="CurrentConnected"/>.
/// </summary>
/// <param name="mail">The User's mail.</param>
/// <param name="password">The User's (plain text) password.</param>
/// <returns>True if the connection succed, false otherwise.</returns>
bool LogIn(string mail, string password);
/// <summary>
/// Log out the current connected user.
/// </summary>
void LogOut();
}
}

@ -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
{
/// <summary>
/// The Main manager of the model.
/// </summary>
public class MasterManager : INotifyPropertyChanged
public class MasterManager
{
#region Attributes & Properties
public event PropertyChangedEventHandler? PropertyChanged;
private RecipeCollection _recipesInSearch = new RecipeCollection("");
/// <summary>
/// The currently connected user. 'null' if no user is connected.
/// </summary>
public User? CurrentConnectedUser { get; private set; }
public RecipeCollection RecipesInSearch
{
get => _recipesInSearch;
set
{
_recipesInSearch = value;
OnPropertyChange();
}
}
/// <summary>
/// The collection of all recipes loaded.
/// Manage the data of the application.
/// </summary>
public RecipeCollection Recipes { get; private set; }
public IDataManager Data { get; private set; }
/// <summary>
/// The collection of all users loaded.
/// Manage the recipes of the application.
/// </summary>
public List<User> Users { get; private set; }
public IRecipeManager Recipe { get; private set; }
/// <summary>
/// The data manager for load, save, export and import data.
/// Manage the users of the application.
/// </summary>
public DataManager DataMgr { get; private set; }
public IUserManager User { get; private set; }
#endregion
#region Constructors
/// <summary>
/// Constructor of the MasterManager.
/// </summary>
/// <param name="dataManager">The serializer for the data.</param>
public MasterManager(IDataManager dataManager)
/// <param name="dataManager">The data manager.</param>
/// <param name="recipeManager">The recipes manager.</param>
/// <param name="userManager">The users manager.</param>
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
/// <summary>
/// Log in an user. Test if the log in information are correct then connect the user.
/// Setup all the necessary parameters before start.
/// </summary>
/// <param name="mail">The user's mail</param>
/// <param name="password">The user's password.</param>
/// <returns>True if the user was correctly connected, false if there is something wrong.</returns>
/// <exception cref="ArgumentNullException"></exception>
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;
}
/// <summary>
/// Log out an user.
/// </summary>
/// <returns>True if the user is correctly diconnected, false otherwise.</returns>
public bool Logout()
{
if (CurrentConnectedUser is null)
return false;
CurrentConnectedUser = null;
return true;
Data.LoadData();
}
/// <summary>
/// Register an user.
/// </summary>
/// <param name="newuser">The new user to add in the database.</param>
/// <returns>False if there is a problem with the registerement, true otherwise.</returns>
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;
}
/// <summary>
/// Add a recipe to the database.
/// </summary>
/// <param name="recipe">The recipe to add.</param>
public void AddRecipe(Recipe recipe)
{
DataMgr.Data[nameof(Recipe)].Add(recipe);
Recipes = DataMgr.GetRecipes();
}
/// <summary>
/// Get the current connected user's personal recipes.
/// </summary>
/// <returns>The current connected user's personal recipes.</returns>
public RecipeCollection GetCurrentUserRecipes()
=> new RecipeCollection("User recipes",
DataMgr.GetRecipes().ToList().FindAll(r => r.AuthorMail == CurrentConnectedUser?.Mail).ToArray());
/// <summary>
/// Notify property change handler.
/// </summary>
/// <param name="propertyName">the name of the property that change.</param>
public void OnPropertyChange([CallerMemberName] string propertyName = "")
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
#endregion
}
#endregion
}

@ -1,10 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Configurations>Debug;Release;CI</Configurations>
</PropertyGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Configurations>Debug;Release;CI</Configurations>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\AppException\AppException.csproj" />
</ItemGroup>
</Project>

@ -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<Recipe>
{
private RecipeCollection _recipeObserved;
public string Description => _recipeObserved.Description;
public ReadOnlyObservableRecipeCollection(RecipeCollection recipeCollection)
: base(recipeCollection)
{
_recipeObserved = recipeCollection;
}
}
}

@ -52,6 +52,12 @@ namespace Model
}
}
/// <summary>
/// Priority of this recipe.
/// </summary>
[DataMember(Name = "priority")]
public Priority Priority { get; private set; }
/// <summary>
/// The Title of the recipe. <br/>
/// Set to "No title." when the value passed is null, empty or contain white spaces.
@ -110,13 +116,15 @@ namespace Model
/// </summary>
/// <param name="title">The title of the recipe</param>
/// <param name="type">The type of the recipe.</param>
/// <param name="priority">The priority of this recipe.</param>
/// <param name="id">The id of the recipe. If not given, get a new id.</param>
/// <param name="authorMail">The name of the user that create this recipe.</param>
/// <param name="picture"> The image that represent the recipe</param>
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)
/// <inheritdoc cref="Recipe.Recipe(string, RecipeType, Priority, int?, string?, string?)"/>
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)
/// <inheritdoc cref="Recipe.Recipe(string, RecipeType, Priority, int?, string?, string?)"/>
public Recipe(string title, RecipeType type, Priority priority)
: this(title, type, priority, null, null)
{
}
/// <inheritdoc cref="Recipe.Recipe(string, RecipeType, Priority, int?, string?, string?)"/>
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
/// </summary>
/// <returns>The list of ingredients in string format</returns>
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
}

@ -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
/// <summary>
/// Construct a new collection of _recipes.
/// Construct a new collection of recipes.
/// </summary>
/// <param _name="description">A short description of what this list will contain</param>
/// <param _name="recipes">Recipes to add in this new collection</param>
public RecipeCollection(string description, params Recipe[] recipes)
/// <param name="description">A short description of what this list will contain.</param>
/// <param name="recipes">Recipes to add in this new collection.</param>
public RecipeCollection(string description, ICollection<Recipe> recipes)
: base(recipes)
{
Description = description;
}
#endregion
#region Methods
/// <summary>
/// Find a recipe in this list by giving the id.
/// </summary>
/// <param _name="id">The id of the list we are looking for</param>
/// <returns>The recipe of the id given</returns>
/// <exception cref="ArgumentException"/>
public Recipe? GetRecipeById(int id)
/// <inheritdoc cref="RecipeCollection.RecipeCollection(string, ICollection{Recipe})"/>
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;
}
/// <summary>
/// Utility to find a recipe by his _name.
/// </summary>
/// <param _name="str">The string for the search</param>
/// <returns>A collection of Recipe where their Title contain the string.</returns>
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;

@ -1,4 +1,4 @@
using Model.Managers;
using Model;
using System;
using System.Collections.Generic;
using System.Linq;

@ -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);
}
}

@ -1,205 +1,184 @@
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.ComponentModel;
namespace Model
{
/// <summary>
/// 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
/// </summary>
[DataContract(Name = "user")]
public class User : IEquatable<User> , 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<Priority> priorities;
public event PropertyChangedEventHandler? PropertyChanged;
#endregion
#region Properties
/// <summary>
/// Property to get Name of users and a setter
/// </summary>
/// <exception cref="ArgumentException" >Setter have Exception which is trigger when Name is null</exception>
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged();
}
}
/// <summary>
/// Property to get Surname of users and a setter
/// </summary>
/// <exception cref="ArgumentException" >Setter have Exception which is trigger when Surname is null</exception>
public string Surname
{
get { return surname; }
set
{
surname = value;
OnPropertyChanged();
}
}
/// <summary>
/// Property to get mail of users and a setter
/// </summary>
/// <exception cref="ArgumentException" >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.</exception>
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;
}
/// <summary>
/// For now, we define the ProfilPict as a string which is "PhotoParDefaut"
/// when the value is null.
/// </summary>
public string ProfilPict
{
get => picture;
set => picture = value;
}
/// <summary>
/// This is the list of priorities specific tu the user. This list is initiate
/// by default. User could change it at will.
/// </summary>
public List<Priority> 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
/// <summary>
/// Construtors of user.
/// </summary>
/// <param name="name">The name of the user</param>
/// <param name="surname">The surname of the user</param>
/// <param name="mail">The user needs an email to login.</param>
/// <param name="password">The password of the new user.</param>
/// <param name="passwordManager">The password manager to manage the user password.</param>
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> {
Priority.Gourmet,
Priority.Economic,
Priority.Fast,
Priority.Light,
Priority.Easy};
ProfilPict = picture;
}
/// <summary>
/// <inheritdoc cref="User.User"/>
/// </summary>
public User(string name, string surname, string mail, string password)
: this(name, surname,mail, password, new PasswordSHA256())
{
}
/// <summary>
/// <inheritdoc cref="User.User"/>
/// </summary>
public User()
: this("John", "Doe", "truc@gmail.com", "mdp")
{
}
/// <summary>
/// <inheritdoc cref="User.User"/>
/// </summary>
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
{
/// <summary>
/// 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
/// </summary>
[DataContract(Name = "user")]
public class User : IEquatable<User> , INotifyPropertyChanged
{
#region Private Attributes
[DataMember] private string _name = "";
[DataMember] private string _surname = "";
[DataMember] private string _mail = "";
[DataMember(Name = "priorities")]
public ObservableCollection<Priority> _priorities { get; private set; } = new ObservableCollection<Priority>
{
Priority.Gourmet,
Priority.Economic,
Priority.Fast,
Priority.Light,
Priority.Easy
};
public event PropertyChangedEventHandler? PropertyChanged;
#endregion
#region Properties
/// <summary>
/// Property to get Name of users and a setter
/// </summary>
/// <exception cref="ArgumentException" >Setter have Exception which is trigger when Name is null</exception>
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged();
}
}
/// <summary>
/// Property to get Surname of users and a setter
/// </summary>
/// <exception cref="ArgumentException" >Setter have Exception which is trigger when Surname is null</exception>
public string Surname
{
get { return _surname; }
set
{
_surname = value;
OnPropertyChanged();
}
}
/// <summary>
/// Property to get mail of users and a setter
/// </summary>
/// <exception cref="ArgumentException" >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.</exception>
public string Mail
{
get { return _mail; }
private init
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException("Impossible d'avoir un champ Email vide!");
}
_mail = value;
}
}
/// <summary>
/// The user's hashed password. The hashed method is defined with the PasswordManager.
/// <br/>See: <see cref="IPasswordManager"/>.
/// </summary>
[DataMember(Name = "hashedpass")]
public string Password { get; private set; } = "";
/// <summary>
/// For now, we define the ProfilePict as a string which is "PhotoParDefaut"
/// when the value is null.
/// </summary>
[DataMember(Name = "profilepic")]
public string ProfilePict { get; private set; } = "default_picture.png";
/// <summary>
/// This is the list of priorities specific tu the user. This list is initiate
/// by default. User could change it at will.
/// </summary>
public ReadOnlyObservableCollection<Priority> 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
/// <summary>
/// Construtors of user.
/// </summary>
/// <param name="name">The name of the user</param>
/// <param name="surname">The surname of the user</param>
/// <param name="mail">The user needs an email to login.</param>
/// <param name="hashedPassword">The password of the new user.</param>
public User(string name, string surname, string mail, string hashedPassword)
{
Name = name;
Surname = surname;
Mail = mail;
Password = hashedPassword;
Priorities = new ReadOnlyObservableCollection<Priority>(_priorities);
}
/// <inheritdoc cref="User.User"/>
/// <param name="profilePict">Profile picture of the new user.</param>
public User(string name, string surname, string mail, string hashedPassword, string profilePict)
: this(name, surname, mail, hashedPassword)
{
ProfilePict = profilePict;
}
/// <inheritdoc cref="User.User"/>
public User()
: this("John", "Doe", "truc@gmail.com", "mdp")
{
}
/// <inheritdoc cref="User.User"/>
/// <param name="user">The user to copy.</param>
public User(User user)
{
Name = user.Name;
Surname = user.Surname;
Mail = user.Mail;
Password = user.Password;
_priorities = user._priorities;
Priorities = new ReadOnlyObservableCollection<Priority>(_priorities);
ProfilePict = user.ProfilePict;
}
#endregion
}
}

@ -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
{
/// <summary>
/// Define a serializer to manage JSON files.
/// </summary>
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),

@ -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
{
/// <summary>
/// Define a serializer to manage XML files.
/// </summary>
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
};

@ -6,8 +6,8 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Model\Model.csproj" />
<ItemGroup>
<ProjectReference Include="..\..\Model\Model.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Managers\Managers.csproj" />
<ProjectReference Include="..\..\Model\Model.csproj" />
</ItemGroup>
</Project>

@ -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
{
/// <summary>
/// The subs class is a group of prefabricated object that can only be loaded. It only use is for testing.
/// </summary>
public class Stubs : IDataManager
public class Stubs : IDataSerializer
{
private IPasswordManager psswdMgr = new PasswordSHA256Manager();
public Dictionary<string, List<object>> Load()
{
Dictionary<string, List<object>> data = new Dictionary<string, List<object>>
@ -22,7 +22,7 @@ namespace DataPersistence
nameof(Recipe),
new List<object>(new[]
{
new Recipe("Cookies classiques", RecipeType.Dessert, null, "admin@mctg.fr", "")
new Recipe("Cookies classiques", RecipeType.Dessert, Priority.Easy, null, "admin@mctg.fr", "")
{
Ingredients = new List<Ingredient>
{
@ -42,7 +42,8 @@ namespace DataPersistence
},
new Recipe(
title: "Cookies au chocolat",
type: RecipeType.Dessert)
type: RecipeType.Dessert,
priority: Priority.Fast)
{
Ingredients = new List<Ingredient>
{
@ -57,7 +58,8 @@ namespace DataPersistence
},
new Recipe(
title: "Gateau nature",
type: RecipeType.Dessert)
type: RecipeType.Dessert,
priority: Priority.Gourmet)
{
Ingredients = new List<Ingredient>
{
@ -77,7 +79,8 @@ namespace DataPersistence
},
new Recipe(
title: "Gateau au pommes",
type: RecipeType.Dessert)
type: RecipeType.Dessert,
priority: Priority.Light)
{
PreparationSteps = new List<PreparationStep>
{
@ -90,6 +93,7 @@ namespace DataPersistence
new Recipe(
title: "Gateau au chocolat",
type: RecipeType.Dessert,
priority: Priority.Economic,
id: null, authorMail: "pedrosamigos@hotmail.com")
{
Ingredients = new List<Ingredient>
@ -110,6 +114,7 @@ namespace DataPersistence
new Recipe(
title: "Dinde au jambon",
type: RecipeType.Dish,
priority: Priority.Easy,
id: null, authorMail: "pedrosamigos@hotmail.com")
{
Ingredients = new List<Ingredient>
@ -130,6 +135,7 @@ namespace DataPersistence
new Recipe(
title: "Poulet au curry",
type: RecipeType.Dish,
priority: Priority.Gourmet,
id: null, authorMail: "pedrosamigos@hotmail.com")
{
Ingredients = new List<Ingredient>
@ -165,12 +171,12 @@ namespace DataPersistence
name: "Admin",
surname: "Admin",
mail: "admin@mctg.fr",
password: "admin"),
hashedPassword: psswdMgr.HashPassword("admin")),
new User(
name: "Pedros",
surname: "Amigos",
mail: "pedrosamigos@hotmail.com",
password: "pamigos")
hashedPassword: psswdMgr.HashPassword("pamigos"))
})
#endregion

@ -1,55 +1,77 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33516.290
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp", "ConsoleApp\ConsoleApp.csproj", "{666C2211-8EBB-4FC8-9484-CB93BC854153}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model", "Model\Model.csproj", "{42FF86BD-92F9-4A32-A938-68515905378F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Views", "Views\Views.csproj", "{508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model_UnitTests", "Tests\Model_UnitTests\Model_UnitTests.csproj", "{45AB746A-194B-4E43-81EB-83B06F35AA33}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{08B80CE8-A01D-4D86-8989-AF225D5DA48C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataPersistence", "DataPersistence\DataPersistence.csproj", "{432F9D12-B1F7-4A79-8720-4971BB10B831}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{666C2211-8EBB-4FC8-9484-CB93BC854153}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{666C2211-8EBB-4FC8-9484-CB93BC854153}.Debug|Any CPU.Build.0 = Debug|Any CPU
{666C2211-8EBB-4FC8-9484-CB93BC854153}.Release|Any CPU.ActiveCfg = Release|Any CPU
{666C2211-8EBB-4FC8-9484-CB93BC854153}.Release|Any CPU.Build.0 = Release|Any CPU
{42FF86BD-92F9-4A32-A938-68515905378F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{42FF86BD-92F9-4A32-A938-68515905378F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42FF86BD-92F9-4A32-A938-68515905378F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42FF86BD-92F9-4A32-A938-68515905378F}.Release|Any CPU.Build.0 = Release|Any CPU
{508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Debug|Any CPU.Build.0 = Debug|Any CPU
{508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Release|Any CPU.ActiveCfg = Release|Any CPU
{508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Release|Any CPU.Build.0 = Release|Any CPU
{508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Release|Any CPU.Deploy.0 = Release|Any CPU
{45AB746A-194B-4E43-81EB-83B06F35AA33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{45AB746A-194B-4E43-81EB-83B06F35AA33}.Debug|Any CPU.Build.0 = Debug|Any CPU
{45AB746A-194B-4E43-81EB-83B06F35AA33}.Release|Any CPU.ActiveCfg = Release|Any CPU
{45AB746A-194B-4E43-81EB-83B06F35AA33}.Release|Any CPU.Build.0 = Release|Any CPU
{432F9D12-B1F7-4A79-8720-4971BB10B831}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{432F9D12-B1F7-4A79-8720-4971BB10B831}.Debug|Any CPU.Build.0 = Debug|Any CPU
{432F9D12-B1F7-4A79-8720-4971BB10B831}.Release|Any CPU.ActiveCfg = Release|Any CPU
{432F9D12-B1F7-4A79-8720-4971BB10B831}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{45AB746A-194B-4E43-81EB-83B06F35AA33} = {08B80CE8-A01D-4D86-8989-AF225D5DA48C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {ADEA5603-1EF6-4D43-9493-7D6D9DE7FA3F}
EndGlobalSection
EndGlobal
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33516.290
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp", "ConsoleApp\ConsoleApp.csproj", "{666C2211-8EBB-4FC8-9484-CB93BC854153}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model", "Model\Model.csproj", "{42FF86BD-92F9-4A32-A938-68515905378F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Views", "Views\Views.csproj", "{508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model_UnitTests", "Tests\Model_UnitTests\Model_UnitTests.csproj", "{45AB746A-194B-4E43-81EB-83B06F35AA33}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{08B80CE8-A01D-4D86-8989-AF225D5DA48C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataPersistence", "Persistance\DataPersistence\DataPersistence.csproj", "{432F9D12-B1F7-4A79-8720-4971BB10B831}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Managers", "Managers\Managers.csproj", "{A3703A19-687C-4F63-A5DE-18E6D8995C77}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppException", "AppException\AppException.csproj", "{77E6BD97-B1E5-45F5-ABFB-9A1D985A8EDE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FakePersistance", "Persistance\FakePersistance\FakePersistance.csproj", "{7C340CB2-8925-4BC4-9D8C-9058D9657F3F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Persistance", "Persistance", "{F6413DA3-CE67-4097-8FF7-8D221AF2A5E8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{666C2211-8EBB-4FC8-9484-CB93BC854153}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{666C2211-8EBB-4FC8-9484-CB93BC854153}.Debug|Any CPU.Build.0 = Debug|Any CPU
{666C2211-8EBB-4FC8-9484-CB93BC854153}.Release|Any CPU.ActiveCfg = Release|Any CPU
{666C2211-8EBB-4FC8-9484-CB93BC854153}.Release|Any CPU.Build.0 = Release|Any CPU
{42FF86BD-92F9-4A32-A938-68515905378F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{42FF86BD-92F9-4A32-A938-68515905378F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42FF86BD-92F9-4A32-A938-68515905378F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42FF86BD-92F9-4A32-A938-68515905378F}.Release|Any CPU.Build.0 = Release|Any CPU
{508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Debug|Any CPU.Build.0 = Debug|Any CPU
{508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Release|Any CPU.ActiveCfg = Release|Any CPU
{508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Release|Any CPU.Build.0 = Release|Any CPU
{508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Release|Any CPU.Deploy.0 = Release|Any CPU
{45AB746A-194B-4E43-81EB-83B06F35AA33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{45AB746A-194B-4E43-81EB-83B06F35AA33}.Debug|Any CPU.Build.0 = Debug|Any CPU
{45AB746A-194B-4E43-81EB-83B06F35AA33}.Release|Any CPU.ActiveCfg = Release|Any CPU
{45AB746A-194B-4E43-81EB-83B06F35AA33}.Release|Any CPU.Build.0 = Release|Any CPU
{432F9D12-B1F7-4A79-8720-4971BB10B831}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{432F9D12-B1F7-4A79-8720-4971BB10B831}.Debug|Any CPU.Build.0 = Debug|Any CPU
{432F9D12-B1F7-4A79-8720-4971BB10B831}.Release|Any CPU.ActiveCfg = Release|Any CPU
{432F9D12-B1F7-4A79-8720-4971BB10B831}.Release|Any CPU.Build.0 = Release|Any CPU
{A3703A19-687C-4F63-A5DE-18E6D8995C77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A3703A19-687C-4F63-A5DE-18E6D8995C77}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A3703A19-687C-4F63-A5DE-18E6D8995C77}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A3703A19-687C-4F63-A5DE-18E6D8995C77}.Release|Any CPU.Build.0 = Release|Any CPU
{77E6BD97-B1E5-45F5-ABFB-9A1D985A8EDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{77E6BD97-B1E5-45F5-ABFB-9A1D985A8EDE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{77E6BD97-B1E5-45F5-ABFB-9A1D985A8EDE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{77E6BD97-B1E5-45F5-ABFB-9A1D985A8EDE}.Release|Any CPU.Build.0 = Release|Any CPU
{7C340CB2-8925-4BC4-9D8C-9058D9657F3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7C340CB2-8925-4BC4-9D8C-9058D9657F3F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C340CB2-8925-4BC4-9D8C-9058D9657F3F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C340CB2-8925-4BC4-9D8C-9058D9657F3F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{45AB746A-194B-4E43-81EB-83B06F35AA33} = {08B80CE8-A01D-4D86-8989-AF225D5DA48C}
{432F9D12-B1F7-4A79-8720-4971BB10B831} = {F6413DA3-CE67-4097-8FF7-8D221AF2A5E8}
{7C340CB2-8925-4BC4-9D8C-9058D9657F3F} = {F6413DA3-CE67-4097-8FF7-8D221AF2A5E8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {ADEA5603-1EF6-4D43-9493-7D6D9DE7FA3F}
EndGlobalSection
EndGlobal

@ -1,27 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<Configurations>Debug;Release;CI</Configurations>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Model\Model.csproj" />
<ProjectReference Include="..\..\DataPersistence\DataPersistence.csproj" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<Configurations>Debug;Release;CI</Configurations>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Managers\Managers.csproj" />
<ProjectReference Include="..\..\Persistance\DataPersistence\DataPersistence.csproj" />
<ProjectReference Include="..\..\Persistance\FakePersistance\FakePersistance.csproj" />
</ItemGroup>
</Project>

@ -1,25 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DataPersistence;
using Model;
using Model.Managers;
namespace Model_UnitTests
{
public class RecipeCollection_UT
{
[Fact]
public void TestResearchByName()
{
MasterManager masterManager = new MasterManager(new Stubs());
RecipeCollection recipes = masterManager.DataMgr.GetRecipes("test rc");
Recipe? search_result = recipes.ResearchByName("chocolat").FirstOrDefault();
Assert.NotNull(search_result);
Assert.Equal("Cookies au chocolat", search_result.Title);
}
}
}

@ -8,7 +8,7 @@ namespace Model_UnitTests
public void TestVoidConstructor()
{
Recipe r = new Recipe(
title: "test recipe", type: RecipeType.Unspecified);
title: "test recipe", type: RecipeType.Unspecified, priority: Priority.Easy);
Assert.NotNull(r.Title);
}

@ -12,8 +12,8 @@ namespace Model_UnitTests
[Fact]
public void TestConstructUser()
{
PasswordSHA256 passwordManager = new PasswordSHA256();
User user = new User("Bob", "Dylan", "bd@gmail.com", "bobby");
PasswordSHA256Manager passwordManager = new PasswordSHA256Manager();
User user = new User("Bob", "Dylan", "bd@gmail.com", passwordManager.HashPassword("bobby"));
Assert.Equal("Bob", user.Name);
Assert.Equal("Dylan", user.Surname);
Assert.Equal("bd@gmail.com", user.Mail);

@ -1,52 +1,74 @@
#if WINDOWS
using Microsoft.UI;
using Microsoft.UI.Windowing;
using Windows.Graphics;
#endif
using Model;
using FakePersistance;
using DataPersistence;
using Model;
using System.Collections.ObjectModel;
using Model.Managers;
using Microsoft.Maui.Controls;
using System.Linq;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Managers;
using System.Diagnostics;
namespace Views
{
public partial class App : Application
{
//Point d'entrée de l'application
public MasterManager MasterMgr { get; private set; } = new MasterManager(new Stubs());
//L'utilisateur courant de l'application
public User CurrentUser { get; set; }
private Recipe currentRecipe { get; set; }
public Recipe CurrentRecipe
{
/// <summary>
/// Master manager - access to the Model.
/// </summary>
public MasterManager Master { get; private set; }
public App()
{
get => currentRecipe;
set
Debug.WriteLine("Hello, World!\n\n");
string path = FileSystem.Current.AppDataDirectory; // - 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);
// Initialize the data manager
IDataManager dataManager = (!File.Exists(Path.Combine(path, $"data.{strategy}"))) ?
new DataDefaultManager(new Stubs())
: new DataDefaultManager(dataSerializer);
// Initialize the other managers
IRecipeManager recipeManager = new RecipeDefaultManager(dataManager);
IPasswordManager passwordManager = new PasswordSHA256Manager();
IUserManager userManager = new UserDefaultManager(dataManager, passwordManager);
// Initialize the master manager
Master = new MasterManager(dataManager, recipeManager, userManager);
Master.Setup();
// Change the data serializer if the one in place is 'Stubs'
if (Master.Data.Serializer.GetType() == typeof(Stubs))
{
currentRecipe = value;
OnPropertyChanged(nameof(CurrentRecipe));
var data = Master.Data.Data;
dataManager = new DataDefaultManager(dataSerializer, data);
Master = new MasterManager(dataManager, recipeManager, userManager);
}
}
//collection de recette de l'application
public RecipeCollection AllRecipes { get; set; }
// Save data.
Debug.Write("[ --SAVE-- ]:\t");
Master.Data.SaveData();
Debug.WriteLine("Done.");
public App()
{
CurrentUser = MasterMgr.CurrentConnectedUser;
AllRecipes = MasterMgr.DataMgr.GetRecipes("All recipes");
CurrentRecipe = MasterMgr.DataMgr.GetRecipes().First();
InitializeComponent();
UserAppTheme = AppTheme.Light;
MainPage = new Home();
//MainPage = new MyPosts();
}
protected override void OnSleep()
{
// Save data.
Debug.Write("[ --SAVE-- ]:\t");
Master.Data.SaveData();
Debug.WriteLine("Done.");
Debug.WriteLine(FileSystem.Current.AppDataDirectory);
base.OnSleep();
}
}
}

@ -1,5 +1,3 @@
using CommunityToolkit.Maui.Behaviors;
using DataPersistence;
using Model;
using Model.Managers;
using System.Diagnostics;
@ -9,7 +7,8 @@ namespace Views
{
public partial class AddRecipe : ContentPage
{
public MasterManager MasterMgr => (App.Current as App).MasterMgr;
public MasterManager Master => (Application.Current as App).Master;
public List<Unit> UnitList { get; set; } = new List<Unit> { Unit.unit, Unit.kG, Unit.mG, Unit.G, Unit.L, Unit.cL, Unit.mL };
public bool IsCaptureSupported => throw new NotImplementedException();

@ -1,89 +1,89 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:local="clr-namespace:Views"
xmlns:model="clr-namespace:Model;assembly=Model"
x:Class="Views.Home"
x:Name="homepage">
<local:ContainerBase
IsNotConnected="{Binding IsNotConnected, Source={x:Reference homepage}}"
NeedReturn="{Binding NeedReturn, Source={x:Reference homepage}}">
<!-- Flyout -->
<local:ContainerBase.MyFlyoutContent>
<VerticalStackLayout Grid.Row="1">
<!-- Research -->
<Label
Text="Recherche de recettes :" FontSize="14"
Margin="20, 10, 15, 0"/>
<SearchBar
Placeholder="Mots-clés (ex.: rapide, fromage)"
FontAttributes="Italic" TextColor="Black"
BackgroundColor="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Gray300}}"
Margin="15, 10, 15, 40"
SearchButtonPressed="SearchBar_SearchButtonPressed"/>
<!-- Direct research -->
<Button
Text="Toutes les recettes"
ImageSource="home_icon.png"
Style="{StaticResource button1}"
Clicked="AllRecipes_Clicked"/>
<Button
Text="Entrées"
ImageSource="flatware_icon.png"
Style="{StaticResource button1}"
Clicked="Entrees_Clicked"/>
<Button
Text="Plats"
ImageSource="room_service_icon.png"
Style="{StaticResource button1}"
Clicked="Plats_Clicked"/>
<Button
Text="Desserts"
ImageSource="coffee_icon.png"
Style="{StaticResource button1}"
Clicked="Desserts_Clicked"/>
</VerticalStackLayout>
</local:ContainerBase.MyFlyoutContent>
<!-- Master -->
<local:ContainerBase.MyContent>
<ScrollView>
<StackLayout BindingContext="{Binding RecipesDisplayed}" MinimumWidthRequest="400">
<!--Modification du prof apportée sur le stacklayout pour empecher l'affichage d'une seule case recipe-->
<Label
Text="{Binding Description}"
TextColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource Gray100}}"
FontSize="24"
Padding="15"/>
<FlexLayout
Margin="0, 15"
Wrap="Wrap"
JustifyContent="Start"
AlignItems="Center"
AlignContent="SpaceEvenly"
HorizontalOptions="Center"
BindableLayout.ItemsSource="{Binding}">
<BindableLayout.ItemTemplate>
<DataTemplate x:DataType="model:Recipe">
<local:RecipeCase
CaseImageSource="{Binding Image}"
RecipeTitle="{Binding Title}"/>
</DataTemplate>
</BindableLayout.ItemTemplate>
</FlexLayout>
</StackLayout>
</ScrollView>
</local:ContainerBase.MyContent>
</local:ContainerBase>
</ContentPage>
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:local="clr-namespace:Views"
xmlns:model="clr-namespace:Model;assembly=Model"
x:Class="Views.Home"
x:Name="homepage">
<local:ContainerBase x:Name="container_base"
NeedReturn="False">
<!-- Flyout -->
<local:ContainerBase.MyFlyoutContent>
<VerticalStackLayout Grid.Row="1">
<!-- Research -->
<Label
Text="Recherche de recettes :" FontSize="14"
Margin="20, 10, 15, 0"/>
<SearchBar
Placeholder="Mots-clés (ex.: rapide, fromage)"
TextColor="Black"
BackgroundColor="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Gray300}}"
Margin="15, 10, 15, 40"
SearchButtonPressed="SearchBar_SearchButtonPressed"/>
<!-- Direct research -->
<Button
Text="Toutes les recettes"
ImageSource="home_icon.png"
Style="{StaticResource button1}"
Clicked="AllRecipes_Clicked"/>
<Button
Text="Entrées"
ImageSource="flatware_icon.png"
Style="{StaticResource button1}"
Clicked="Entrees_Clicked"/>
<Button
Text="Plats"
ImageSource="room_service_icon.png"
Style="{StaticResource button1}"
Clicked="Plats_Clicked"/>
<Button
Text="Desserts"
ImageSource="coffee_icon.png"
Style="{StaticResource button1}"
Clicked="Desserts_Clicked"/>
</VerticalStackLayout>
</local:ContainerBase.MyFlyoutContent>
<!-- Master -->
<local:ContainerBase.MyContent>
<ScrollView>
<StackLayout BindingContext="{Binding ., Source={x:Reference homepage}}"
MinimumWidthRequest="400">
<!--Modification du prof apportée sur le stacklayout pour empecher l'affichage d'une seule case recipe-->
<Label
Text="{Binding RecipesDisplayed.Description}"
TextColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource Gray100}}"
FontSize="24"
Padding="15"/>
<FlexLayout
Margin="0, 15"
Wrap="Wrap"
JustifyContent="Start"
AlignItems="Center"
AlignContent="SpaceEvenly"
HorizontalOptions="Center"
BindableLayout.ItemsSource="{Binding RecipesDisplayed}">
<BindableLayout.ItemTemplate>
<DataTemplate x:DataType="model:Recipe">
<local:RecipeCase
CaseImageSource="{Binding Image}"
RecipeTitle="{Binding Title}"/>
</DataTemplate>
</BindableLayout.ItemTemplate>
</FlexLayout>
</StackLayout>
</ScrollView>
</local:ContainerBase.MyContent>
</local:ContainerBase>
</ContentPage>

@ -0,0 +1,80 @@
using Model;
using System.Collections.ObjectModel;
namespace Views
{
public partial class Home : ContentPage
{
public MasterManager Master => (Application.Current as App).Master;
private readonly RecipeCollection _recipesDisplayed;
public ReadOnlyObservableRecipeCollection RecipesDisplayed { get; private set; }
public string Description => _recipesDisplayed.Description;
public Home()
{
_recipesDisplayed = Master.Recipe.GetAllRecipes();
RecipesDisplayed = new ReadOnlyObservableRecipeCollection(_recipesDisplayed);
InitializeComponent();
BindingContext = this;
container_base.IsConnected = Master.User.CurrentConnected is not null;
}
private void ModifyRecipesDisplayed(RecipeCollection recipes)
{
_recipesDisplayed.Clear();
_recipesDisplayed.Description = recipes.Description;
foreach (Recipe recipe in recipes)
{
_recipesDisplayed.Add(recipe);
}
}
private void SearchBar_SearchButtonPressed(object sender, EventArgs e)
{
string searchStr = (sender as SearchBar).Text;
ModifyRecipesDisplayed(Master.Recipe.SearchRecipeByTitle(searchStr));
}
public void OnImageClicked(object sender, EventArgs e)
{
Master.Recipe.CurrentSelected = (Recipe)(sender as ImageButton).BindingContext;
Navigation.PushModalAsync(new ViewRecette());
}
private void Entrees_Clicked(object sender, EventArgs e)
{
ModifyRecipesDisplayed(new RecipeCollection("Entrées",
Master.Recipe.GetAllRecipes()
.ToList()
.FindAll(r => r.Type == RecipeType.Starter)
.ToArray()));
}
private void Plats_Clicked(object sender, EventArgs e)
{
ModifyRecipesDisplayed(new RecipeCollection("Plats",
Master.Recipe.GetAllRecipes()
.ToList()
.FindAll(r => r.Type == RecipeType.Dish)
.ToArray()));
}
private void Desserts_Clicked(object sender, EventArgs e)
{
ModifyRecipesDisplayed(new RecipeCollection("Desserts",
Master.Recipe.GetAllRecipes()
.ToList()
.FindAll(r => r.Type == RecipeType.Dessert)
.ToArray()));
}
private void AllRecipes_Clicked(object sender, EventArgs e)
{
ModifyRecipesDisplayed(Master.Recipe.GetAllRecipes());
}
}
}

@ -6,7 +6,7 @@
Title="MyPosts">
<local:ContainerBase
IsNotConnected="False"
IsConnected="False"
NeedReturn="True">
<local:ContainerBase.MyFlyoutContent>

@ -1,20 +1,20 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Views"
xmlns:model="clr-namespace:Model;assembly=Model"
x:Class="Views.MyProfil"
Title="MyProfil">
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Views"
xmlns:model="clr-namespace:Model;assembly=Model"
x:Class="Views.MyProfil"
Title="MyProfil">
<ContentPage.Resources>
<local:EnumToValuesConverter x:Key="musicTypeToValueConverter"
x:TypeArguments="model:Priority"/>
</ContentPage.Resources>
<local:ContainerBase
IsNotConnected="False"
NeedReturn="True">
<local:ContainerBase.MyFlyoutContent>
<Grid RowDefinitions="250, *, *" VerticalOptions="Fill">
<VerticalStackLayout Grid.Row="1">
<local:ContainerBase
IsConnected="False"
NeedReturn="True">
<local:ContainerBase.MyFlyoutContent>
<Grid RowDefinitions="250, *, *" VerticalOptions="Fill">
<VerticalStackLayout Grid.Row="1">
<Button Text="Mes Recettes"
ImageSource="person_default.png"
Style="{StaticResource button1}"
@ -25,44 +25,44 @@
Style="{StaticResource button1}"
Grid.Row="2"
Clicked="OnAddRecipeClicked"/>
</VerticalStackLayout>
</Grid>
</local:ContainerBase.MyFlyoutContent>
<local:ContainerBase.MyContent>
<ScrollView>
<StackLayout >
<!--user's informations-->
<Label Text="Mon profil" TextColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource Gray100}}"
FontAttributes="Bold"
FontSize="24" Padding="15, 15, 20, 5"/>
<HorizontalStackLayout>
<VerticalStackLayout >
<Label Text="Informations personnelles :" TextColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource Gray100}}"
FontSize="20" Padding="15"/>
<Label Text="Nom :"
Padding="50,0,0,0"
FontSize="18"/>
<Entry BackgroundColor="#D1E8E2"
Margin="50,10,0,20"
Text="{Binding CurrentUser.Name}"/>
<Label Text="Prénom :"
Padding="50,0,0,0"
FontSize="18"/>
<Entry BackgroundColor="#D1E8E2"
Margin="50,10,0,20"
Text="{Binding CurrentUser.Surname} "/>
<Label Text="Mail :"
Padding="50,0,0,0"
FontSize="18"/>
<Entry BackgroundColor="#D1E8E2"
Margin="50,10,0,20"
IsEnabled="False"
Text="{Binding CurrentUser.Mail}"/>
<!--liste drag and drop-->
</VerticalStackLayout>
<VerticalStackLayout Padding="100,0,0,0">
<Label Text="Priorités du compte : " TextColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource Gray100}}"
FontSize="20" Padding="15"/>
</VerticalStackLayout>
</Grid>
</local:ContainerBase.MyFlyoutContent>
<local:ContainerBase.MyContent>
<ScrollView>
<StackLayout >
<!--user's informations-->
<Label Text="Mon profil" TextColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource Gray100}}"
FontAttributes="Bold"
FontSize="24" Padding="15, 15, 20, 5"/>
<HorizontalStackLayout>
<VerticalStackLayout >
<Label Text="Informations personnelles :" TextColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource Gray100}}"
FontSize="20" Padding="15"/>
<Label Text="Nom :"
Padding="50,0,0,0"
FontSize="18"/>
<Entry BackgroundColor="#D1E8E2"
Margin="50,10,0,20"
Text="{Binding CurrentUser.Name}"/>
<Label Text="Prénom :"
Padding="50,0,0,0"
FontSize="18"/>
<Entry BackgroundColor="#D1E8E2"
Margin="50,10,0,20"
Text="{Binding CurrentUser.Surname} "/>
<Label Text="Mail :"
Padding="50,0,0,0"
FontSize="18"/>
<Entry BackgroundColor="#D1E8E2"
Margin="50,10,0,20"
IsEnabled="False"
Text="{Binding CurrentUser.Mail}"/>
<!--liste drag and drop-->
</VerticalStackLayout>
<VerticalStackLayout Padding="100,0,0,0">
<Label Text="Priorités du compte : " TextColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource Gray100}}"
FontSize="20" Padding="15"/>
<FlexLayout BindableLayout.ItemsSource="{Binding PriorityList}" MinimumHeightRequest="80"
BackgroundColor="{StaticResource Primary}" AlignItems="Center" Wrap="Wrap">
<BindableLayout.ItemTemplate>
@ -77,10 +77,10 @@
</DataTemplate>
</BindableLayout.ItemTemplate>
</FlexLayout>
</VerticalStackLayout>
</HorizontalStackLayout>
</StackLayout>
</ScrollView>
</local:ContainerBase.MyContent>
</local:ContainerBase>
</ContentPage>
</VerticalStackLayout>
</HorizontalStackLayout>
</StackLayout>
</ScrollView>
</local:ContainerBase.MyContent>
</local:ContainerBase>
</ContentPage>

@ -0,0 +1,16 @@
using Model;
namespace Views;
public partial class MyProfil : ContentPage
{
public MasterManager Master => (Application.Current as App).Master;
public User user => Master.User.CurrentConnected;
public MyProfil()
{
InitializeComponent();
BindingContext = this;
}
}

@ -0,0 +1,19 @@
using Model;
namespace Views;
/// <summary>
/// Classe de la page contenant le detail de la recette
///
/// </summary>
public partial class ViewRecette : ContentPage
{
public MasterManager Master => (Application.Current as App).Master;
public User user => Master.User.CurrentConnected;
public Recipe Recipe => Master.Recipe.CurrentSelected;
public RecipeCollection AllRecipes => Master.Recipe.GetAllRecipes();
public ViewRecette()
{
InitializeComponent();
BindingContext = this;
}
}

@ -17,8 +17,7 @@
Grid.RowSpan="2"
MinimumWidthRequest="300"
HorizontalOptions="StartAndExpand"
IsNotConnected="{Binding IsNotConnected, Source={x:Reference root}}"
NeedReturn="{Binding NeedReturn, Source={x:Reference root}}">
x:Name="container_flayout">
<local:ContainerFlyout.MyFlyoutContent>
<ContentView
Content="{Binding MyFlyoutContent, Source={x:Reference root}}"/>

@ -2,11 +2,25 @@ namespace Views;
public partial class ContainerBase : ContentView
{
public bool IsConnected
{
get => container_flayout.IsConnected;
set => container_flayout.IsConnected = value;
}
public bool NeedReturn
{
get => container_flayout.NeedReturn;
set => container_flayout.NeedReturn = value;
}
public ContainerBase()
{
InitializeComponent();
BindingContext = this;
}
// Bind MyContent
public static readonly BindableProperty MyContentProperty =
BindableProperty.Create("MyContent", typeof(View), typeof(ContainerBase), new Grid());
@ -26,24 +40,4 @@ public partial class ContainerBase : ContentView
get => (View)GetValue(MyFlyoutContentProperty);
set => SetValue(MyFlyoutContentProperty, value);
}
// Bind IsNotConnected
public static readonly BindableProperty IsNotConnectedProperty =
BindableProperty.Create("IsNotConnected", typeof(bool), typeof(Button), true);
public bool IsNotConnected
{
get => (bool)GetValue(IsNotConnectedProperty);
set => SetValue(IsNotConnectedProperty, value);
}
// bind NeedReturn
public static readonly BindableProperty NeedReturnProperty =
BindableProperty.Create("NeedReturn", typeof(bool), typeof(Border), false);
public bool NeedReturn
{
get => (bool)GetValue(NeedReturnProperty);
set => SetValue(NeedReturnProperty, value);
}
}

@ -20,27 +20,29 @@
WidthRequest="100" HeightRequest="100"
CornerRadius="50" Margin="0, 30, 0, 10"
BorderWidth="5" BorderColor="Black"
IsEnabled="{Binding IsNotConnected, Source={x:Reference fl}}"
IsVisible="{Binding IsConnected, Source={x:Reference fl}}"
Grid.RowSpan="2"
Clicked="ProfileButton_Clicked"/>
</Grid>
<!-- Connection button -->
<Button Text="Connection" ImageSource="login_icon.png"
Style="{StaticResource button2}"
IsVisible="{Binding IsNotConnected, Source={x:Reference fl}}"
IsEnabled="{Binding IsNotConnected, Source={x:Reference fl}}"
Margin="15, 120"
IsVisible="{Binding IsConnected, Converter={toolkit:InvertedBoolConverter}, Source={x:Reference fl}}"
Clicked="ConnectionButton_Clicked"/>
<StackLayout BindingContext="{Binding user}">
<!-- Display name -->
<StackLayout BindingContext="{Binding ConnectedUser}"
IsVisible="{Binding IsConnected, Source={x:Reference fl}}">
<Label Text="{Binding Name}"
HorizontalOptions="Center" Margin="0,15"
FontSize="20" FontAttributes="Bold" HorizontalTextAlignment="Center"
TextColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}"
IsVisible="{Binding IsNotConnected, Converter={toolkit:InvertedBoolConverter} ,Source={x:Reference fl}}"/>
TextColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}"/>
<Label Text="{Binding Surname}"
HorizontalOptions="Center"
FontSize="20" FontAttributes="Bold" HorizontalTextAlignment="Center"
TextColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}"
IsVisible="{Binding IsNotConnected, Converter={toolkit:InvertedBoolConverter} ,Source={x:Reference fl}}"/>
TextColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}"/>
</StackLayout>
</VerticalStackLayout>
@ -56,7 +58,7 @@
<!-- Footer -->
<Button Text="Déconnection" ImageSource="logout_icon.png"
Style="{StaticResource button2}"
IsVisible="{Binding IsNotConnected, Converter={toolkit:InvertedBoolConverter}, Source={x:Reference fl}}"/>
IsVisible="{Binding IsConnected, Source={x:Reference fl}}"/>
</VerticalStackLayout>
</Grid>

@ -1,61 +1,60 @@
using DataPersistence;
using Model;
using Model.Managers;
namespace Views;
public partial class ContainerFlyout : ContentView
{
public MasterManager MasterMgr => (App.Current as App).MasterMgr;
public User user => (App.Current as App).CurrentUser;
public ContainerFlyout()
{
InitializeComponent();
BindingContext = this;
}
#region Bindable XAML Properties
// Bind MyFlyoutContent
public static readonly BindableProperty MyFlyoutContentProperty =
BindableProperty.Create("MyFlyoutContent", typeof(View), typeof(ContainerFlyout), new Grid());
public View MyFlyoutContent
{
get => (View)GetValue(MyFlyoutContentProperty);
set => SetValue(MyFlyoutContentProperty, value);
}
// Bind IsNotConnected
public static readonly BindableProperty IsNotConnectedProperty =
BindableProperty.Create("IsNotConnected", typeof(bool), typeof(Button), true);
public bool IsNotConnected
{
get => (bool)GetValue(IsNotConnectedProperty);
set => SetValue(IsNotConnectedProperty, value);
}
// bind NeedReturn
public static readonly BindableProperty NeedReturnProperty =
BindableProperty.Create("NeedReturn", typeof(bool), typeof(Border), false);
public bool NeedReturn
{
get => (bool)GetValue(NeedReturnProperty);
set => SetValue(NeedReturnProperty, value);
}
#endregion
public async void ProfileButton_Clicked(object sender, EventArgs e)
{
await Navigation.PushModalAsync(new MyProfil());
}
public async void ConnectionButton_Clicked(object sender, EventArgs e)
{
await Navigation.PushModalAsync(new Login());
}
}
using DataPersistence;
using Model;
namespace Views;
public partial class ContainerFlyout : ContentView
{
public MasterManager Master => (Application.Current as App).Master;
public User ConnectedUser => Master.User.CurrentConnected;
public ContainerFlyout()
{
InitializeComponent();
BindingContext = this;
}
#region Bindable XAML Properties
// Bind MyFlyoutContent
public static readonly BindableProperty MyFlyoutContentProperty =
BindableProperty.Create("MyFlyoutContent", typeof(View), typeof(ContainerFlyout), new Grid());
public View MyFlyoutContent
{
get => (View)GetValue(MyFlyoutContentProperty);
set => SetValue(MyFlyoutContentProperty, value);
}
// Bind IsNotConnected
public static readonly BindableProperty IsConnectedProperty =
BindableProperty.Create("IsConnected", typeof(bool), typeof(Button), true);
public bool IsConnected
{
get => (bool)GetValue(IsConnectedProperty);
set => SetValue(IsConnectedProperty, value);
}
// bind NeedReturn
public static readonly BindableProperty NeedReturnProperty =
BindableProperty.Create("NeedReturn", typeof(bool), typeof(Border), false);
public bool NeedReturn
{
get => (bool)GetValue(NeedReturnProperty);
set => SetValue(NeedReturnProperty, value);
}
#endregion
public async void ProfileButton_Clicked(object sender, EventArgs e)
{
await Navigation.PushModalAsync(new MyProfil());
}
public async void ConnectionButton_Clicked(object sender, EventArgs e)
{
await Navigation.PushModalAsync(new Login());
}
}

@ -1,27 +1,27 @@
namespace Views;
public partial class MiniHeader : ContentView
{
public MiniHeader()
{
InitializeComponent();
}
public static readonly BindableProperty TitleMiniProperty =
BindableProperty.Create(nameof(TitleMini), typeof(string), typeof(Label), "Erreur de titre");
public string TitleMini
{
get => (string)GetValue(TitleMiniProperty);
set => SetValue(TitleMiniProperty, value);
}
// bind NeedReturn
public static readonly BindableProperty NeedReturnProperty =
BindableProperty.Create("NeedReturn", typeof(bool), typeof(Border), false);
public bool NeedReturn
{
get => (bool)GetValue(NeedReturnProperty);
set => SetValue(NeedReturnProperty, value);
}
namespace Views;
public partial class MiniHeader : ContentView
{
public MiniHeader()
{
InitializeComponent();
}
public static readonly BindableProperty TitleMiniProperty =
BindableProperty.Create(nameof(TitleMini), typeof(string), typeof(Label), "Erreur de titre");
public string TitleMini
{
get => (string)GetValue(TitleMiniProperty);
set => SetValue(TitleMiniProperty, value);
}
// bind NeedReturn
public static readonly BindableProperty NeedReturnProperty =
BindableProperty.Create("NeedReturn", typeof(bool), typeof(Border), false);
public bool NeedReturn
{
get => (bool)GetValue(NeedReturnProperty);
set => SetValue(NeedReturnProperty, value);
}
}

@ -1,32 +1,32 @@
namespace Views;
public partial class RecipeCase : ContentView
{
public RecipeCase()
{
InitializeComponent();
}
public static readonly BindableProperty CaseImageSourceProperty =
BindableProperty.Create("CaseImageSource", typeof(ImageSource), typeof(Image));
public ImageSource CaseImageSource
{
get => (ImageSource)GetValue(CaseImageSourceProperty);
set => SetValue(CaseImageSourceProperty, value);
}
public static readonly BindableProperty RecipeTitleProperty =
BindableProperty.Create("RecipeTitle", typeof(string), typeof(Label));
public string RecipeTitle
{
get => (string)GetValue(RecipeTitleProperty);
set => SetValue(RecipeTitleProperty, value);
}
private async void ImageButton_Clicked(object sender, EventArgs e)
{
await Navigation.PushModalAsync(new ViewRecette());
}
}
namespace Views;
public partial class RecipeCase : ContentView
{
public RecipeCase()
{
InitializeComponent();
}
public static readonly BindableProperty CaseImageSourceProperty =
BindableProperty.Create("CaseImageSource", typeof(ImageSource), typeof(Image));
public ImageSource CaseImageSource
{
get => (ImageSource)GetValue(CaseImageSourceProperty);
set => SetValue(CaseImageSourceProperty, value);
}
public static readonly BindableProperty RecipeTitleProperty =
BindableProperty.Create("RecipeTitle", typeof(string), typeof(Label));
public string RecipeTitle
{
get => (string)GetValue(RecipeTitleProperty);
set => SetValue(RecipeTitleProperty, value);
}
private async void ImageButton_Clicked(object sender, EventArgs e)
{
await Navigation.PushModalAsync(new ViewRecette());
}
}

@ -1,98 +0,0 @@
//using Android.Media;
using DataPersistence;
using Model;
using Model.Managers;
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace Views
{
public partial class Home : ContentPage
{
public MasterManager MasterMgr => (App.Current as App).MasterMgr;
public User? user => (App.Current as App).CurrentUser;
public RecipeCollection AllRecipe => (App.Current as App).AllRecipes;
private readonly RecipeCollection _recipesDisplayed;
public ReadOnlyObservableCollection<Recipe> RecipesDisplayed { get; private set; }
public static readonly BindableProperty IsNotConnectedProperty =
BindableProperty.Create("IsNotConnected", typeof(bool), typeof(bool));
public bool IsNotConnected
{
get => (bool)GetValue(IsNotConnectedProperty);
set => SetValue(IsNotConnectedProperty, value);
}
public static readonly BindableProperty NeedReturnProperty =
BindableProperty.Create("NeedReturn", typeof(bool), typeof(bool));
public bool NeedReturn
{
get => (bool)GetValue(NeedReturnProperty);
set => SetValue(NeedReturnProperty, value);
}
public Home()
{
_recipesDisplayed = (RecipeCollection)AllRecipe.Clone();
RecipesDisplayed = new ReadOnlyObservableCollection<Recipe>(_recipesDisplayed);
InitializeComponent();
BindingContext = this;
IsNotConnected = true;
NeedReturn = false;
}
public Home(RecipeCollection recipesDisplayed)
{
_recipesDisplayed = recipesDisplayed;
InitializeComponent();
BindingContext = this;
IsNotConnected = true;
NeedReturn = true;
}
private void ModifyRecipesDisplayed(RecipeCollection recipes)
{
_recipesDisplayed.Clear();
_recipesDisplayed.Description = recipes.Description;
foreach (Recipe recipe in recipes)
{
_recipesDisplayed.Add(recipe);
}
}
private void SearchBar_SearchButtonPressed(object sender, EventArgs e)
{
string searchStr = (sender as SearchBar).Text;
ModifyRecipesDisplayed(AllRecipe.ResearchByName(searchStr));
}
public void OnImageClicked(object sender, EventArgs e)
{
(App.Current as App).CurrentRecipe = (Recipe)(sender as ImageButton).BindingContext;
Navigation.PushModalAsync(new ViewRecette());
}
private void Entrees_Clicked(object sender, EventArgs e)
{
ModifyRecipesDisplayed(new RecipeCollection("Entrées", AllRecipe.ToList().FindAll(r => r.Type == RecipeType.Starter).ToArray()));
}
private void Plats_Clicked(object sender, EventArgs e)
{
ModifyRecipesDisplayed(new RecipeCollection("Plats", AllRecipe.ToList().FindAll(r => r.Type == RecipeType.Dish).ToArray()));
}
private void Desserts_Clicked(object sender, EventArgs e)
{
ModifyRecipesDisplayed(new RecipeCollection("Desserts", AllRecipe.ToList().FindAll(r => r.Type == RecipeType.Dessert).ToArray()));
}
private void AllRecipes_Clicked(object sender, EventArgs e)
{
ModifyRecipesDisplayed(AllRecipe);
}
}
}

@ -1,51 +0,0 @@
using CommunityToolkit.Maui.Behaviors;
using DataPersistence;
using Model;
using Model.Managers;
using System.Collections.ObjectModel;
using System.Diagnostics;
namespace Views;
public partial class MyProfil : ContentPage
{
public MasterManager MasterMgr => (App.Current as App).MasterMgr;
public User CurrentUser => (App.Current as App).CurrentUser;
public MyProfil()
{
InitializeComponent();
BindingContext = this;
}
public ObservableCollection<Priority> PriorityList { get; private set; } = new ObservableCollection<Priority>()
{ Priority.Economic, Priority.Fast, Priority.Easy, Priority.Light, Priority.Gourmet };
void DragGestureRecognizer_DragStarting2(System.Object sender, Microsoft.Maui.Controls.DragStartingEventArgs e)
{
e.Data.Properties["value"] = (sender as Element).Parent.BindingContext;
}
void DropGestureRecognizer_Drop2(System.Object sender, Microsoft.Maui.Controls.DropEventArgs e)
{
var receivingElement = (Priority)((sender as Element).Parent.BindingContext);
var draggedElement = (Priority)e.Data.Properties["value"];
int draggedIndex = PriorityList.IndexOf(draggedElement);
PriorityList.RemoveAt(draggedIndex);
int receivingIndex = PriorityList.IndexOf(receivingElement);
PriorityList.Insert(receivingIndex + 1, draggedElement);
}
private void OnMyRecipeClicked(object sender, EventArgs e)
{
Navigation.PushModalAsync(new MyPosts());
}
private void OnAddRecipeClicked(object sender, EventArgs e)
{
Navigation.PushModalAsync(new AddRecipe());
}
}

@ -1,21 +0,0 @@
using Model.Managers;
using Model;
namespace Views;
/// <summary>
/// Classe de la page contenant le detail de la recette
///
/// </summary>
public partial class ViewRecette : ContentPage
{
public MasterManager MasterMgr => (App.Current as App).MasterMgr;
public User user => (App.Current as App).CurrentUser;
public Recipe Recipe => (App.Current as App).CurrentRecipe;
public RecipeCollection AllRecipes => (App.Current as App).AllRecipes;
public ViewRecette()
{
InitializeComponent();
BindingContext = this;
}
}

@ -57,48 +57,52 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DataPersistence\DataPersistence.csproj" />
<ProjectReference Include="..\AppException\AppException.csproj" />
<ProjectReference Include="..\Managers\Managers.csproj" />
<ProjectReference Include="..\Model\Model.csproj" />
<ProjectReference Include="..\Persistance\DataPersistence\DataPersistence.csproj" />
<ProjectReference Include="..\Persistance\FakePersistance\FakePersistance.csproj" />
</ItemGroup>
<ItemGroup>
<MauiXaml Update="AddRecipe.xaml">
<MauiXaml Update="ContentPages\AddRecipe.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="ContainerBase.xaml">
<MauiXaml Update="ContentViews\ContainerBase.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="ContainerFlyout.xaml">
<MauiXaml Update="ContentViews\ContainerFlyout.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="CustomHeader.xaml">
<MauiXaml Update="ContentViews\CustomHeader.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="Login.xaml">
<MauiXaml Update="ContentPages\Login.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="MiniHeader.xaml">
<MauiXaml Update="ContentViews\MiniHeader.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="MyPosts.xaml">
<MauiXaml Update="ContentPages\MyPosts.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="MyProfil.xaml">
<MauiXaml Update="ContentPages\MyProfil.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="RecipeCase.xaml">
<MauiXaml Update="ContentViews\RecipeCase.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="RecipeReviews.xaml">
<MauiXaml Update="ContentViews\RecipeReviews.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="ReturnButton.xaml">
<MauiXaml Update="ContentViews\ReturnButton.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="UserReview.xaml">
<MauiXaml Update="ContentPages\UserReview.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="ViewRecette.xaml">
<MauiXaml Update="ContentPages\ViewRecette.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
</ItemGroup>

Loading…
Cancel
Save