This commit is contained in:
Paul Schneider 2026-02-14 16:54:27 +00:00
commit 45514010f2
3505 changed files with 154 additions and 523 deletions

View file

@ -0,0 +1,5 @@
{
"version": 1,
"isRoot": true,
"tools": {}
}

View file

@ -0,0 +1,12 @@
namespace Yavsc
{
/// <summary>
/// Chat User Flags
/// </summary>
public enum ChatUserFlags : byte
{
away = 1,
invisible = 2,
cop = 4
}
}

89
src/Server/Config.cs Normal file
View file

@ -0,0 +1,89 @@

using IdentityServer8;
using IdentityServer8.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Yavsc.Settings;
namespace Yavsc;
public static class Config
{
public static string? Authority { get; set; }
public static IConfigurationRoot? GoogleWebClientConfiguration { get; set; }
public static GoogleServiceAccount? GServiceAccount { get; set; }
public static SiteSettings SiteSetup { get; set; } = new SiteSettings();
public static FileServerOptions? UserFilesOptions { get; set; }
public static FileServerOptions? GitOptions { get; set; }
public static string AvatarsDirName { set; get; } = "Avatars";
public static string GitDirName { set; get; } = "Git";
public static GoogleAuthSettings? GoogleSettings { get; set; }
public static SmtpSettings? SmtpSetup { get; set; }
public static string? Temp { get; set; }
public static FileServerOptions? AvatarsOptions { get; set; }
public static string UserBillsDirName { set; get; } = "Bills";
public static string UserFilesDirName { set; get; } = "Files";
/// <summary>
/// Lists Available user profile classes,
/// populated at startup, using reflection.
/// </summary>
public static List<Type> ProfileTypes = new List<Type>();
public static IEnumerable<IdentityResource> IdentityResources =>
[
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email()
];
public static IEnumerable<ApiScope> ApiScopes =>
[
new ApiScope("scope1",new string[] {"scope1"}),
new ApiScope("scope2",new string[] {"scope2"}),
];
public static IEnumerable<Client> Clients =>
[
// m2m client credentials flow client
new Client
{
ClientId = "m2m.client",
ClientName = "Client Credentials Client",
ClientSecrets = { new Secret("511536EF-F270-4058-80CA-1C89C192F69A".Sha256()) },
AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
AllowedScopes = { "scope1" }
},
// interactive client using code flow + pkce
new Client
{
ClientId = "mvc",
ClientSecrets = { new Secret("49C1A7E1-0C79-4A89-A3D6-A37998FB86B0".Sha256()) },
AllowedGrantTypes = GrantTypes.Code,
AlwaysIncludeUserClaimsInIdToken = true,
RedirectUris = { "https://localhost:5003/signin-oidc",
"http://localhost:5002/signin-oidc" },
PostLogoutRedirectUris = { "https://localhost:5003/signout-callback-oidc",
"http://localhost:5002/signout-callback-oidc" },
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.OfflineAccess,
"scope2" },
},
];
public static PayPalSettings? PayPalSettings { get; set; }
}

17
src/Server/Constants.cs Normal file
View file

@ -0,0 +1,17 @@
namespace Yavsc.Server
{
public static class ServerConstants
{
public const string ApplicationName = "Yavsc";
public const string CompanyInfoUrl = " https://societeinfo.com/app/rest/api/v1/company/json?registration_number={0}&key={1}";
private static readonly string[] GoogleScopes = { "openid", "profile", "email" };
public static readonly string[] GoogleCalendarScopes =
{ "openid", "profile", "email", "https://www.googleapis.com/auth/calendar" };
}
}

View file

@ -0,0 +1,17 @@
namespace Yavsc.Server.Exceptions;
[Serializable]
public class AuthorizationFailureException : Exception
{
public AuthorizationFailureException(Microsoft.AspNetCore.Authorization.AuthorizationResult auth) : base(auth?.Failure?.ToString()??auth?.ToString()??"AuthorizationResult failure")
{
}
public AuthorizationFailureException(string? message) : base(message)
{
}
public AuthorizationFailureException(string? message, Exception? innerException) : base(message, innerException)
{
}
}

View file

@ -0,0 +1,15 @@
using System;
namespace Yavsc.Exceptions
{
public class InvalidWorkflowModelException : Exception
{
public InvalidWorkflowModelException(string descr) : base(descr)
{
}
public InvalidWorkflowModelException(string descr, Exception inner) : base(descr,inner)
{
}
}
}

View file

@ -0,0 +1,9 @@
using System;
namespace Yavsc.Exceptions
{
public class InvalidPathException: Exception
{
}
}

View file

@ -0,0 +1,18 @@
namespace Yavsc.Services
{
[Serializable]
internal class YavscInfrastructureException : Exception
{
public YavscInfrastructureException()
{
}
public YavscInfrastructureException(string? message) : base(message)
{
}
public YavscInfrastructureException(string? message, Exception? innerException) : base(message, innerException)
{
}
}
}

View file

@ -0,0 +1,29 @@
using System.Globalization;
using Yavsc.Billing;
using Yavsc.Models.Billing;
using Yavsc.Server.Helpers;
using Yavsc.Services;
namespace Yavsc.Helpers
{
public static class BillingHelpers
{
public static decimal Addition(this List<IBillItem> items) => items.Aggregate<IBillItem, decimal>(0m, (t, l) => t + l.Count * l.UnitaryCost);
public static decimal Addition(this List<CommandLine> items) => items.Select(i=>((IBillItem)i)).ToList().Addition();
public static string GetBillText(this IBillable query) {
string total = query.GetBillItems().Addition().ToString("C", CultureInfo.CurrentUICulture);
string bill = string.Join("\n", query.GetBillItems().Select(l=> $"{l.Name} {l.Description} : {l.UnitaryCost} € " + ((l.Count != 1) ? "*"+l.Count.ToString() : ""))) +
$"\n\nTotal: {total}";
return bill;
}
public static FileInfo GetBillInfo(this IBillable bill, IBillingService service)
{
var suffix = bill.GetIsAcquitted() ? "-ack":null;
var filename = bill.GetFileBaseName(service)+".pdf";
return new FileInfo(Path.Combine(AbstractFileSystemHelpers.UserBillsDirName, filename));
}
}
}

View file

@ -0,0 +1,23 @@
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
namespace Yavsc.Helpers
{
using Models.societe.com;
using Yavsc.Server;
public static class ComapnyInfoHelpers { 
public static async Task<CompanyInfoMessage> CheckSiren(this HttpClient web,
string siren, CompanyInfoSettings api)
{
using (var request = new HttpRequestMessage(HttpMethod.Get,
string.Format(ServerConstants.CompanyInfoUrl,siren,api.ApiKey))) {
using (var response = await web.SendAsync(request)) {
var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
return payload.ToObject<CompanyInfoMessage>();
}
}
}
}
}

View file

@ -0,0 +1,262 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Html;
using Microsoft.Extensions.FileProviders;
using Yavsc.Models;
using Yavsc.Models.FileSystem;
using Yavsc.Models.Streaming;
using Yavsc.ViewModels;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using Microsoft.AspNetCore.Http;
using Yavsc.Exceptions;
using Yavsc.Helpers;
using Yavsc.Abstract.Helpers;
namespace Yavsc.Server.Helpers
{
public static class FileSystemHelpers
{
public static async Task SaveAsAsync(this IFormFile formFile, string path)
{
if (formFile.Length > 0) {
using (Stream fileStream = new FileStream(path, FileMode.Create)) {
await formFile.CopyToAsync(fileStream);
}
}
}
public static FileReceivedInfo ReceiveProSignature(this ClaimsPrincipal user, string billingCode, long estimateId, IFormFile formFile, string signType)
{
var item = new FileReceivedInfo(
Config.SiteSetup.Bills,
AbstractFileSystemHelpers.SignFileNameFormat("pro", billingCode, estimateId));
var fi = new FileInfo(item.FullName);
if (fi.Exists) item.Overridden = true;
using (var org = formFile.OpenReadStream())
{
using Image image = Image.Load(org);
image.Save(fi.FullName);
}
return item;
}
public static string GetAvatarUri(this ApplicationUser user)
{
return $"/{Config.SiteSetup.Avatars}/{user.UserName}.png";
}
public static string EnsureDestinationDirectory(
this ClaimsPrincipal user,
string subpath)
{
var root = Path.Combine(AbstractFileSystemHelpers.UserFilesDirName, user.Identity.Name);
if (!string.IsNullOrWhiteSpace(subpath))
{
if (!subpath.IsValidYavscPath())
{
throw new InvalidPathException();
}
root = Path.Combine(root, subpath);
}
var di = new DirectoryInfo(root);
if (!di.Exists) di.Create();
return di.FullName;
}
/// <summary>
/// Deletes user file.
/// User info is modified, but not save in db.
/// </summary>
/// <param name="user"></param>
/// <param name="fileName"></param>
public static void DeleteUserFile(this ApplicationUser user, string fileName)
{
var root = Path.Combine(AbstractFileSystemHelpers.UserFilesDirName, user.UserName);
var fi = new FileInfo(Path.Combine(root, fileName));
if (!fi.Exists) return ;
fi.Delete();
user.DiskUsage -= fi.Length;
}
public static FsOperationInfo DeleteUserDirOrFile(this ApplicationUser user, string dirName)
{
var root = Path.Combine(AbstractFileSystemHelpers.UserFilesDirName, user.UserName);
if (string.IsNullOrEmpty(dirName))
return new FsOperationInfo { Done = false, ErrorCode = ErrorCode.InvalidRequest, ErrorMessage = "specify a directory or file name"} ;
var di = new DirectoryInfo(Path.Combine(root, dirName));
if (!di.Exists) {
var fi = new FileInfo(Path.Combine(root, dirName));
if (!fi.Exists) return new FsOperationInfo { Done = false, ErrorCode = ErrorCode.NotFound, ErrorMessage = "non existent"} ;
fi.Delete();
user.DiskUsage -= fi.Length;
}
else {
if (di.GetDirectories().Length>0 || di.GetFiles().Length>0)
return new FsOperationInfo { Done = false, ErrorCode = ErrorCode.InvalidRequest, ErrorMessage = "dir is not empty, refusing to remove it"} ;
di.Delete();
}
return new FsOperationInfo { Done = true };
}
public static FsOperationInfo MoveUserDir(this ApplicationUser user, string fromDirName, string toDirName)
{
var root = Path.Combine(AbstractFileSystemHelpers.UserFilesDirName, user.UserName);
if (string.IsNullOrEmpty(fromDirName))
return new FsOperationInfo { Done = false, ErrorCode = ErrorCode.InvalidRequest , ErrorMessage = "specify a dir name "} ;
var di = new DirectoryInfo(Path.Combine(root, fromDirName));
if (!di.Exists) return new FsOperationInfo { Done = false, ErrorCode = ErrorCode.NotFound, ErrorMessage = "fromDirName: non existent"} ;
if (string.IsNullOrEmpty(toDirName)) toDirName = ".";
var destPath = Path.Combine(root, toDirName);
var fout = new FileInfo(destPath);
if (fout.Exists) return new FsOperationInfo { Done = false, ErrorCode = ErrorCode.InvalidRequest, ErrorMessage = "destination is a regular file" } ;
var dout = new DirectoryInfo(destPath);
if (dout.Exists) {
destPath = Path.Combine(destPath, dout.Name);
}
di.MoveTo(destPath);
return new FsOperationInfo { Done = true };
}
public static FsOperationInfo MoveUserFileToDir(this ApplicationUser user, string fileNameFrom, string fileNameDest)
{
var root = Path.Combine(AbstractFileSystemHelpers.UserFilesDirName, user.UserName);
var fi = new FileInfo(Path.Combine(root, fileNameFrom));
if (!fi.Exists) return new FsOperationInfo { ErrorCode = ErrorCode.NotFound, ErrorMessage = "no file to move" } ;
string dest;
if (!string.IsNullOrEmpty(fileNameDest)) dest = Path.Combine(root, fileNameDest);
else dest = root;
var fo = new FileInfo(dest);
if (fo.Exists) return new FsOperationInfo { ErrorCode = ErrorCode.DestExists , ErrorMessage = "destination file name is an existing file" } ;
var dout = new DirectoryInfo(dest);
if (!dout.Exists) dout.Create();
fi.MoveTo(Path.Combine(dout.FullName, fi.Name));
return new FsOperationInfo { Done = true };
}
public static FsOperationInfo MoveUserFile(this ApplicationUser user, string fileNameFrom, string fileNameDest)
{
var root = Path.Combine(AbstractFileSystemHelpers.UserFilesDirName, user.UserName);
var fi = new FileInfo(Path.Combine(root, fileNameFrom));
if (!fi.Exists) return new FsOperationInfo { ErrorCode = ErrorCode.NotFound, ErrorMessage = "no file to move" } ;
var fo = new FileInfo(Path.Combine(root, fileNameDest));
if (fo.Exists) return new FsOperationInfo { ErrorCode = ErrorCode.DestExists , ErrorMessage = "destination file name is an existing file" } ;
fi.MoveTo(fo.FullName);
return new FsOperationInfo { Done = true };
}
static string ParseFileNameFromDisposition(string disposition)
{
// form-data_ name=_file__ filename=_Constants.Private.cs_
var parts = disposition.Split(' ');
var filename = parts[2].Split('=')[1];
filename = filename.Substring(1,filename.Length-2);
return filename;
}
public static void AddQuota(this ApplicationUser user, int quota)
{
user.DiskQuota += quota;
}
public static FileReceivedInfo ReceiveUserFile(this ApplicationUser user, string root, IFormFile f, string destFileName = null)
{
return ReceiveUserFile(user, root, f.OpenReadStream(), destFileName ?? ParseFileNameFromDisposition(f.ContentDisposition), f.ContentType, CancellationToken.None);
}
public static FileReceivedInfo ReceiveUserFile(this ApplicationUser user, string root, Stream inputStream, string destFileName, string contentType, CancellationToken token)
{
// TODO lock user's disk usage for this scope,
// this process is not safe at concurrent access.
long usage = user.DiskUsage;
var item = new FileReceivedInfo
(root, AbstractFileSystemHelpers.FilterFileName(destFileName));
var fi = new FileInfo(Path.Combine(root, item.FileName));
if (fi.Exists)
{
item.Overridden = true;
usage -= fi.Length;
}
using (var dest = fi.OpenWrite())
{
using (inputStream)
{
const int blen = 1024;
byte[] buffer = new byte[blen];
int len = 0;
while (!token.IsCancellationRequested && (len=inputStream.Read(buffer, 0, blen))>0)
{
dest.Write(buffer, 0, len);
usage += len;
if (usage >= user.DiskQuota) break;
}
user.DiskUsage = usage;
dest.Close();
inputStream.Close();
}
}
if (usage >= user.DiskQuota) {
item.QuotaOffense = true;
}
user.DiskUsage = usage;
return item;
}
public static HtmlString FileLink(this RemoteFileInfo info, string username, string subpath)
{
return new HtmlString(
$"{Config.UserFilesOptions.RequestPath}/{username}/{subpath}/{info.Name}" );
}
public static RemoteFileInfo FileInfo(this ApplicationUser user, string path)
{
IFileInfo info = Config.UserFilesOptions.FileProvider.GetFileInfo($"{user.UserName}/{path}");
if (!info.Exists) return null;
return new RemoteFileInfo{ Name = info.Name, Size = info.Length, LastModified = info.LastModified.UtcDateTime };
}
public static FileReceivedInfo ReceiveAvatar(this ApplicationUser user, IFormFile formFile)
{
var item = new FileReceivedInfo
(Config.AvatarsOptions.RequestPath.ToUriComponent(),
user.UserName + ".png");
using (var org = formFile.OpenReadStream())
{
using Image image = Image.Load(org);
image.Mutate(x=>x.Resize(128,128));
image.Save(Path.Combine(Config.SiteSetup.Avatars,item.FileName));
image.Mutate(x=>x.Resize(64,64));
image.Save(Path.Combine(Config.SiteSetup.Avatars,user.UserName + ".s.png"));
image.Mutate(x=>x.Resize(32,32));
image.Save(Path.Combine(Config.SiteSetup.Avatars,user.UserName + ".xs.png"));
}
user.Avatar = $"{item.DestDir}/{item.FileName}";
return item;
}
public static string GetFileUrl (this LiveFlow flow)
{
// no server-side backup for this stream
return $"{Config.UserFilesOptions.RequestPath}/{flow.Owner.UserName}/live/"+GetFileName(flow);
}
public static string GetFileName (this LiveFlow flow)
{
var fileInfo = new FileInfo(flow.DifferedFileName);
var ext = fileInfo.Extension;
var namelen = flow.DifferedFileName.Length - ext.Length;
var basename = flow.DifferedFileName.Substring(0,namelen);
return $"{basename}-{flow.SequenceNumber}{ext}";
}
}
}

View file

@ -0,0 +1,21 @@
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Http;
using Yavsc.Models.Drawing;
namespace Yavsc.Helpers
{
public static class HtmlHelpers
{
public static HtmlString Color(this Color c)
{
if (c == null) return new HtmlString("#000");
return new HtmlString(String.Format("#{0:X2}{1:X2}{2:X2}", c.Red, c.Green, c.Blue));
}
public static string ToAbsolute(this HttpRequest request, string url)
{
var host = request.Host;
var isSecure = request.Headers[Constants.SshHeaderKey] == "on";
return (isSecure ? "https" : "http") + $"://{host}/{url}";
}
}
}

View file

@ -0,0 +1,175 @@
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
using Yavsc.Models.Billing;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using PayPal.PayPalAPIInterfaceService.Model;
using PayPal.PayPalAPIInterfaceService;
using Yavsc.ViewModels.PayPal;
using Yavsc.Models;
using System.Linq;
using Yavsc.Models.Payment;
using Microsoft.EntityFrameworkCore;
namespace Yavsc.Helpers
{
public static class PayPalHelpers
{
private static Dictionary<string,string> payPalProperties = null;
public static Dictionary<string,string> GetPayPalProperties() {
if (payPalProperties==null) {
payPalProperties = new Dictionary<string,string>();
var paypalSettings = Config.PayPalSettings;
// Don't do:
// payPalProperties.Add("mode", Startup.PayPalSettings.Mode);
// Instead, set the endpoint parameter.
if (paypalSettings.Mode == "production") {
// use nvp end point: https://api-3t.paypal.com/nvp
payPalProperties.Add("endpoint", "https://api-3t.paypal.com/nvp");
} else {
payPalProperties.Add("endpoint", "https://api-3t.sandbox.paypal.com/nvp");
}
payPalProperties.Add("clientId", paypalSettings.ClientId);
payPalProperties.Add("clientSecret", paypalSettings.ClientSecret);
int numClient = 0;
if (paypalSettings.Accounts!=null)
foreach (var account in paypalSettings.Accounts) {
numClient++;
payPalProperties.Add ($"account{numClient}.apiUsername",account.ApiUsername);
payPalProperties.Add ($"account{numClient}.apiPassword",account.ApiPassword);
payPalProperties.Add ($"account{numClient}.apiSignature",account.Signature);
}
}
return payPalProperties;
}
private static PayPalAPIInterfaceServiceService payPalService = null;
public static PayPalAPIInterfaceServiceService PayPalService {
get {
if (payPalService==null)
payPalService = new PayPal.PayPalAPIInterfaceService.PayPalAPIInterfaceServiceService(GetPayPalProperties());
return payPalService;
}}
public class PaymentUrls
{
public string ReturnUrl { get; set; }
public string CancelUrl { get; set; }
public string CGVUrl { get; set; }
}
public static PaymentUrls GetPaymentUrls(this HttpRequest request, string controllerName, string id)
{
var result = new PaymentUrls
{
ReturnUrl = request.ToAbsolute($"{controllerName}/PaymentConfirmation/{id}"),
CancelUrl = request.ToAbsolute($"{controllerName}/ClientCancel/{id}"),
CGVUrl = request.ToAbsolute($"{controllerName}/CGV")
};
return result;
}
public static SetExpressCheckoutResponseType CreatePayment(this HttpRequest request, string controllerName, NominativeServiceCommand query, string intent = "sale", ILogger logger = null)
{
var items = query.GetBillItems();
var total = items.Addition().ToString("F2");
var coreq = new SetExpressCheckoutReq {};
var urls = request.GetPaymentUrls(controllerName, query.Id.ToString());
var pitem = new PaymentDetailsItemType {};
coreq.SetExpressCheckoutRequest = new SetExpressCheckoutRequestType{
DetailLevel = new List<DetailLevelCodeType?> { DetailLevelCodeType.RETURNALL },
SetExpressCheckoutRequestDetails = new SetExpressCheckoutRequestDetailsType
{
PaymentDetails = new List<PaymentDetailsType>( new [] { new PaymentDetailsType{
OrderDescription = query.Description,
OrderTotal = new BasicAmountType {
currencyID = CurrencyCodeType.EUR,
value = total
},
PaymentDetailsItem = new List <PaymentDetailsItemType> (
items.Select(i => new PaymentDetailsItemType {
Amount = new BasicAmountType { currencyID = CurrencyCodeType.EUR, value = i.UnitaryCost.ToString("F2") },
Name = i.Name,
Quantity = i.Count,
Description = i.Description
})
)
}}),
InvoiceID = query.GetInvoiceId(),
// NOTE don't set OrderDescription : "You cannot pass both the new and deprecated order description.","ErrorCode":"11804
CancelURL = urls.CancelUrl,
ReturnURL = urls.ReturnUrl
}
};
var d = new SetExpressCheckoutRequestDetailsType();
logger.LogInformation($"Creating express checkout for {Config.PayPalSettings.MerchantAccountUserName} : "+JsonConvert.SerializeObject(coreq));
var response = PayPalService.SetExpressCheckout( coreq, Config.PayPalSettings.MerchantAccountUserName );
return response;
}
public static async Task<PaymentInfo> GetCheckoutInfo(
this ApplicationDbContext context,
string token)
{
return await CreatePaymentViewModel(context,token,GetExpressCheckoutDetails(token));
}
private static GetExpressCheckoutDetailsResponseType GetExpressCheckoutDetails(string token)
{
GetExpressCheckoutDetailsReq req = new GetExpressCheckoutDetailsReq{
GetExpressCheckoutDetailsRequest = new GetExpressCheckoutDetailsRequestType {
Token = token
}
};
return PayPalService.GetExpressCheckoutDetails(req,Config.PayPalSettings.Accounts[0].ApiUsername);
}
public static async Task<PaymentInfo> ConfirmPayment(
this ApplicationDbContext context,
string userId,
string payerId,
string token)
{
var details = GetExpressCheckoutDetails(token);
var payment = await context.PayPalPayment.SingleOrDefaultAsync(p=>p.CreationToken == token);
if (payment == null)
{
payment = new PayPalPayment{
ExecutorId = userId,
PaypalPayerId = payerId,
CreationToken = token,
// NOTE: 1 order <=> 1 bill <=> 1 payment
OrderReference = details.GetExpressCheckoutDetailsResponseDetails.InvoiceID,
State = details.Ack.ToString()
};
context.PayPalPayment.Add(payment);
}
else {
payment.ExecutorId = userId;
payment.PaypalPayerId = payerId;
payment.State = details.Ack.ToString();
}
await context.SaveChangesAsync(userId);
// GetCheckoutInfo(,token);
return new PaymentInfo { DbContent = payment, DetailsFromPayPal = details };
}
public static async Task<PaymentInfo> CreatePaymentViewModel (
this ApplicationDbContext context,
string token, GetExpressCheckoutDetailsResponseType fromPayPal)
{
return new PaymentInfo {
DbContent = await context.PayPalPayment
.Include(p=>p.Executor)
.SingleOrDefaultAsync(
p=>p.CreationToken==token),
DetailsFromPayPal = fromPayPal
};
}
}
}

View file

@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Yavsc.Server.Model;
namespace Yavsc.Server.Helpers
{
/// <summary>
/// Thanks to Stefan @ Stackoverflow
/// </summary>
public class RequestHelper
{
string WRPostMultipart(string url, Dictionary<string, object> parameters, string authorizationHeader = null)
{
string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
byte[] boundaryBytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.ContentType = "multipart/form-data; boundary=" + boundary;
request.Method = "POST";
request.KeepAlive = true;
request.Credentials = System.Net.CredentialCache.DefaultCredentials;
if (authorizationHeader != null)
request.Headers["Authorization"] = authorizationHeader;
if (parameters != null && parameters.Count > 0)
{
using (Stream requestStream = request.GetRequestStream())
{
using (WebResponse response = request.GetResponse())
{
foreach (KeyValuePair<string, object> pair in parameters)
{
requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
if (pair.Value is FormFile)
{
FormFile file = pair.Value as FormFile;
string header = "Content-Disposition: form-data; name=\"" + pair.Key + "\"; filename=\"" + file.Name + "\"\r\nContent-Type: " + file.ContentType + "\r\n\r\n";
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(header);
requestStream.Write(bytes, 0, bytes.Length);
byte[] buffer = new byte[32768];
int bytesRead;
if (file.Stream == null)
{
// upload from file
using (FileStream fileStream = File.OpenRead(file.FilePath))
{
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
requestStream.Write(buffer, 0, bytesRead);
fileStream.Close();
}
}
else
{
// upload from given stream
while ((bytesRead = file.Stream.Read(buffer, 0, buffer.Length)) != 0)
requestStream.Write(buffer, 0, bytesRead);
}
}
else
{
string data = "Content-Disposition: form-data; name=\"" + pair.Key + "\"\r\n\r\n" + pair.Value;
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(data);
requestStream.Write(bytes, 0, bytes.Length);
}
}
byte[] trailer = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
requestStream.Write(trailer, 0, trailer.Length);
requestStream.Close();
using (Stream responseStream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(responseStream))
{
return reader.ReadToEnd();
}
} // end WebResponse response
} // end using requestStream
}
else throw new ArgumentOutOfRangeException("no parameter found ");
}
public static async Task<string> PostMultipart(string url, FormFile[] formFiles, string access_token = null)
{
if (formFiles != null && formFiles.Length > 0)
{
var client = new HttpClient();
var formData = new MultipartFormDataContent();
if (access_token != null)
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", access_token);
foreach (var formFile in formFiles)
{
HttpContent fileStreamContent = new StreamContent(formFile.Stream);
if (formFile.ContentType!=null)
fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue(formFile.ContentType);
else fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
// fileStreamContent.Headers.ContentDisposition = formFile.ContentDisposition!=null? new ContentDispositionHeaderValue(
// formFile.ContentDisposition) : new ContentDispositionHeaderValue("form-data; name=\"file\"; filename=\"" + formFile.Name + "\"");
fileStreamContent.Headers.Add("Content-Disposition", formFile.ContentDisposition);
fileStreamContent.Headers.Add("Content-Length", formFile.Stream.Length.ToString());
//fileStreamContent.Headers.Add("FilePath", formFile.FilePath);
formData.Add(fileStreamContent, "file", formFile.Name);
}
var response = client.PostAsync(url, formData).Result;
if (!response.IsSuccessStatusCode)
{
return null;
}
return await response.Content.ReadAsStringAsync();
} // end if formFiles != null
return null;
}
}
}

View file

@ -0,0 +1,6 @@
using Microsoft.Extensions.Localization;
public static class ResourcesHelpers {
public static IStringLocalizer GlobalLocalizer = null ;
}

View file

@ -0,0 +1,82 @@
//
// PostJson.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2015 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System.Net;
using System.IO;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System;
namespace Yavsc.Server.Helpers
{
/// <summary>
/// Simple json post method.
/// </summary>
public class SimpleJsonPostMethod : IDisposable
{
private readonly HttpWebRequest request=null;
/// <summary>
/// Initializes a new instance of the Yavsc.Helpers.SimpleJsonPostMethod class.
/// </summary>
/// <param name="pathToMethod">Path to method.</param>
public SimpleJsonPostMethod (string pathToMethod, string authorizationHeader = null, string method = "POST")
{
request = (HttpWebRequest) WebRequest.Create (pathToMethod);
request.Method = method;
request.Accept = "application/json";
request.ContentType = "application/json";
request.SendChunked = true;
request.TransferEncoding = "UTF-8";
if (authorizationHeader!=null)
request.Headers["Authorization"]=authorizationHeader;
}
public void Dispose()
{
request.Abort();
}
/// <summary>
/// Invoke the specified query.
/// </summary>
/// <param name="query">Query.</param>
public async Task<TAnswer> Invoke<TAnswer>(object query)
{
using (Stream streamQuery = await request.GetRequestStreamAsync()) {
using (StreamWriter writer = new StreamWriter(streamQuery)) {
writer.Write (JsonConvert.SerializeObject(query));
}}
TAnswer ans = default (TAnswer);
using (WebResponse response = await request.GetResponseAsync ()) {
using (Stream responseStream = response.GetResponseStream ()) {
using (StreamReader rdr = new StreamReader (responseStream)) {
ans = (TAnswer) JsonConvert.DeserializeObject<TAnswer> (rdr.ReadToEnd ());
}
}
response.Close();
}
return ans;
}
}
}

View file

@ -0,0 +1,28 @@
using System.Security.Claims;
namespace Yavsc.Server.Helpers
{
public static class UserHelpers
{
public static string GetUserId(this ClaimsPrincipal user)
{
return user.FindFirstValue("sub");
}
public static string GetUserName(this ClaimsPrincipal user)
{
return user.FindFirstValue("name");
}
public static bool IsSignedIn(this ClaimsPrincipal user)
{
return user.Identity.IsAuthenticated;
}
public static bool IsInMsRole(this ClaimsPrincipal user, string roleName)
{
return user.HasClaim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", roleName);
}
}
}

View file

@ -0,0 +1,95 @@
namespace Yavsc.Helpers
{
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Yavsc.Abstract.Workflow;
using Yavsc.Billing;
using Yavsc.Models;
using Yavsc.Models.Billing;
using Yavsc.Models.Haircut;
using Yavsc.Models.Workflow;
using Yavsc.Services;
using Yavsc.ViewModels.FrontOffice;
public static class WorkflowHelpers
{
public static async Task<List<PerformerProfileViewModel>>
ListPerformersAsync(this ApplicationDbContext context,
IBillingService billing,
string actCode)
{
var actors = context.Performers
.Include(p => p.Activity)
.Include(p => p.Performer)
.Where(p => p.Active && p.Activity.Any(u => u.DoesCode == actCode)).OrderBy(x => x.Rate)
.ToArray();
List<PerformerProfileViewModel> result = new();
foreach (var a in actors)
{
var settings = await billing.GetPerformersSettingsAsync(actCode, a.PerformerId);
result.Add(new PerformerProfileViewModel(a, actCode, settings));
}
return result;
}
public static void RegisterBilling<T>(string code, Func<ApplicationDbContext, long,
IDecidableQuery> getter) where T : IBillable
{
if (BillingService.Billing.ContainsKey(code)
|| BillingService.GlobalBillingMap.ContainsKey(code))
{
throw new InvalidOperationException("Billing setup");
}
BillingService.Billing.Add(code, getter);
BillingService.GlobalBillingMap.Add(typeof(T).Name, code);
}
public static void ConfigureBillingService()
{
foreach (var a in System.AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var c in a.GetTypes())
{
if (c.IsClass && !c.IsAbstract &&
c.GetInterface("ISpecializationSettings") != null)
{
Config.ProfileTypes.Add(c);
}
}
}
foreach (var propertyInfo in typeof(ApplicationDbContext).GetProperties())
{
foreach (var attr in propertyInfo.CustomAttributes)
{
// something like a DbSet?
if (typeof(Yavsc.Attributes.ActivitySettingsAttribute).IsAssignableFrom(attr.AttributeType))
{
BillingService.UserSettings.Add(propertyInfo);
}
}
}
RegisterBilling<HairCutQuery>(BillingCodes.Brush, new Func<ApplicationDbContext, long, IDecidableQuery>
((db, id) =>
{
var query = db.HairCutQueries.Include(q => q.Prestation).Include(q => q.Regularisation).Single(q => q.Id == id);
query.SelectedProfile = db.BrusherProfile.Single(b => b.UserId == query.PerformerId);
return query;
}));
RegisterBilling<HairMultiCutQuery>(BillingCodes.MBrush, new Func<ApplicationDbContext, long, IDecidableQuery>
((db, id) => db.HairMultiCutQueries.Include(q => q.Regularisation).Single(q => q.Id == id)));
RegisterBilling<RdvQuery>(BillingCodes.Rdv, new Func<ApplicationDbContext, long, IDecidableQuery>
((db, id) => db.RdvQueries.Include(q => q.Regularisation).Single(q => q.Id == id)));
}
}
}

401
src/Server/Hubs/ChatHub.cs Normal file
View file

@ -0,0 +1,401 @@
//
// ChatHub.cs
//
// Author:
// Paul Schneider <paul@pschneider.fr>
//
// Copyright (c) 2016-2019 GNU GPL
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Localization;
namespace Yavsc
{
using System.Diagnostics;
using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using Models;
using Models.Chat;
using Yavsc.Abstract.Chat;
using Yavsc.Helpers;
using Yavsc.Server.Helpers;
using Yavsc.Services;
public partial class ChatHub : Hub, IDisposable
{
private readonly ApplicationDbContext _dbContext;
private readonly IConnexionManager _cxManager;
private readonly IStringLocalizer _localizer;
private readonly ILogger _logger;
public HubInputValidator InputValidator { get; }
public ChatHub(ApplicationDbContext dbContext,
ILoggerFactory loggerFactory,
IStringLocalizerFactory stringLocalizerFactory,
IConnexionManager connexionManager)
{
_dbContext = dbContext;
_localizer = stringLocalizerFactory.Create(typeof(ChatHub));
_cxManager = connexionManager;
_cxManager.SetErrorHandler ((context, error) =>
{
NotifyUser(NotificationTypes.Error, context, error);
});
_logger = loggerFactory.CreateLogger<ChatHub>();
InputValidator = new HubInputValidator { NotifyUser = async (type, target, msg) => await this.NotifyUser(type, target, msg) };
}
void SetUserName(string cxId, string userName)
{
_cxManager.SetUserName(cxId, userName);
}
public override async Task OnConnectedAsync()
{
bool isAuth = Context.User?.Identity?.IsAuthenticated ?? false;
bool isCop = false;
string userName = setUserName();
if (isAuth)
{
var group = isAuth ?
ChatHubConstants.HubGroupAuthenticated : ChatHubConstants.HubGroupAnonymous;
// Log ("Cx: " + group);
await Groups.AddToGroupAsync(Context.ConnectionId, group);
_logger.LogInformation(_localizer.GetString(ChatHubConstants.LabAuthChatUser));
var userId = _dbContext.Users.First(u => u.UserName == Context.User.Identity.Name).Id;
await Clients.Group(ChatHubConstants.HubGroupFollowingPrefix + userId).SendAsync("notifyUser", NotificationTypes.Connected, userName, null);
isCop = Context.User.IsInMsRole(Constants.AdminGroupName) ;
if (isCop)
{
await Groups.AddToGroupAsync(Context.ConnectionId, ChatHubConstants.HubGroupCops);
}
foreach (var uid in _dbContext.CircleMembers.Select(m => m.MemberId))
{
await Groups.AddToGroupAsync(Context.ConnectionId, ChatHubConstants.HubGroupFollowingPrefix + uid);
}
}
else
{
await Groups.AddToGroupAsync(Context.ConnectionId, ChatHubConstants.HubGroupAnonymous);
}
_cxManager.OnConnected(Context.ConnectionId, isCop);
await base.OnConnectedAsync();
}
string setUserName(string queryUname = "anon")
{
if (Context.User != null)
if (Context.User.Identity.IsAuthenticated)
{
SetUserName(Context.ConnectionId, Context.User.Identity.Name);
return Context.User.Identity.Name;
}
anonymousSequence++;
var aname = $"{ChatHubConstants.AnonymousUserNamePrefix}{queryUname}{anonymousSequence}";
SetUserName(Context.ConnectionId, aname);
return aname;
}
static long anonymousSequence = 0;
public override async Task OnDisconnectedAsync(Exception ?ex)
{
string userName = Context.User?.Identity.Name;
if (userName != null)
{
var user = _dbContext.Users.FirstOrDefault(u => u.UserName == userName);
var userId = user.Id;
await Clients.Group(ChatHubConstants.HubGroupFollowingPrefix + userId).SendAsync("notifyUser", NotificationTypes.DisConnected, userName, null);
_cxManager.OnDisctonnected(Context.ConnectionId);
}
await base.OnDisconnectedAsync(ex);
}
public async Task Nick(string nickName)
{
if (!InputValidator.ValidateUserName(nickName)) return;
var candidate = "?" + nickName;
if (_cxManager.IsConnected(candidate))
{
await NotifyUser(NotificationTypes.ExistingUserName, nickName, "aborting");
return;
}
_cxManager.SetUserName( Context.ConnectionId, candidate);
}
bool IsPresent(string roomName, string userName)
{
return _cxManager.IsPresent(roomName, userName);
}
public async Task<ChatRoomInfo> Join(string roomName)
{
_logger.LogInformation($"Join:{roomName}");
if (!InputValidator.ValidateRoomName(roomName))
{
_logger.LogError("!InputValidator.ValidateRoomName(roomName)");
return null;
}
var roomGroupName = ChatHubConstants.HubGroupRomsPrefix + roomName;
var user = _cxManager.GetUserName(Context.ConnectionId);
await Groups.AddToGroupAsync(Context.ConnectionId, roomGroupName);
ChatRoomInfo chanInfo;
if (!_cxManager.IsPresent(roomName, user))
{
_logger.LogInformation($"Joining");
chanInfo = _cxManager.Join(roomName, Context.ConnectionId);
await Clients.Group(roomGroupName).SendAsync("notifyRoom", NotificationTypes.UserJoin, roomName, user);
} else {
_logger.LogInformation($"already present");
// in case in an additional connection,
// one only send info on room without
// warning any other user.
_cxManager.TryGetChanInfo(roomName, out chanInfo);
}
_logger.LogInformation($"returning chan info");
await Clients.Caller.SendAsync("joint", chanInfo);
return chanInfo;
}
[Authorize]
public void Register(string room)
{
if (!InputValidator.ValidateRoomName(room)) return ;
var existent = _dbContext.ChatRoom.Any(r => r.Name == room);
if (existent)
{
NotifyUserInRoom(NotificationTypes.Error, room, "already registered.");
return;
}
Debug.Assert(Context.User != null);
string userName = Context.User.GetUserName();
var user = _dbContext.Users.FirstOrDefault(u => u.UserName == userName);
var newroom = new ChatRoom { Name = room, OwnerId = Context.User.GetUserId() };
ChatRoomInfo chanInfo;
if (_cxManager.TryGetChanInfo(room, out chanInfo))
{
// TODO get and require some admin status for current user on this chan
newroom.Topic = chanInfo.Topic;
}
newroom.LatestJoinPart = DateTime.Now;
_dbContext.ChatRoom.Add(newroom);
_dbContext.SaveChanges(user.Id);
}
[Authorize]
public void KickBan(string roomName, string userName, string reason)
{
if (!InputValidator.ValidateRoomName(roomName)) return ;
if (!InputValidator.ValidateUserName(userName)) return ;
if (!InputValidator.ValidateReason(reason)) return;
Kick(roomName, userName, reason);
Ban(roomName, userName, reason);
}
[Authorize]
public async Task Kick(string roomName, string userName, string reason)
{
if (!InputValidator.ValidateRoomName(roomName)) return ;
if (!InputValidator.ValidateUserName(userName)) return ;
if (!InputValidator.ValidateReason(reason)) return;
ChatRoomInfo chanInfo;
var roomGroupName = ChatHubConstants.HubGroupRomsPrefix + roomName;
if (_cxManager.TryGetChanInfo(roomName, out chanInfo))
{
if (!_cxManager.IsPresent(roomName,userName))
{
NotifyErrorToCallerInRoom(roomName, $"{userName} was not found in {roomName}.");
return;
}
// in case of Kick returned false, being not allowed to, or for what ever other else failure,
// the error handler will send an error message while handling the error.
if (!_cxManager.Kick(Context.ConnectionId, userName, roomName, reason)) return;
}
var ukeys = _cxManager.GetConnexionIds(userName);
if (ukeys!=null) foreach(var ukey in ukeys)
await Groups.RemoveFromGroupAsync(ukey, roomGroupName);
await Clients.Group(roomGroupName).SendAsync("notifyRoom", NotificationTypes.Kick, roomName, $"{userName}: {reason}");
}
[Authorize]
public void Ban(string roomName, string userName, string reason)
{
if (!InputValidator.ValidateRoomName(roomName)) return ;
if (!InputValidator.ValidateUserName(userName)) return ;
if (!InputValidator.ValidateReason(reason)) return;
var cxIds = _cxManager.GetConnexionIds(userName);
throw new NotImplementedException();
}
[Authorize]
public void Gline(string userName, string reason)
{
if (!InputValidator.ValidateUserName(userName)) return ;
if (!InputValidator.ValidateReason(reason)) return;
throw new NotImplementedException();
}
public void Part(string roomName, string reason)
{
if (!InputValidator.ValidateRoomName(roomName)) return ;
if (!InputValidator.ValidateReason(reason)) return;
if (_cxManager.Part(Context.ConnectionId, roomName, reason))
{
var roomGroupName = ChatHubConstants.HubGroupRomsPrefix + roomName;
var group = Clients.Group(roomGroupName);
var userName = _cxManager.GetUserName(Context.ConnectionId);
group.SendAsync("notifyRoom", NotificationTypes.UserPart, roomName, $"{userName}: {reason}");
Groups.RemoveFromGroupAsync(Context.ConnectionId, roomGroupName);
}
else {
_logger.LogError("Could not part");
}
}
void NotifyErrorToCallerInRoom(string room, string reason)
{
NotifyUserInRoom(NotificationTypes.Error, room, reason);
_logger.LogError($"NotifyErrorToCallerInRoom: {room}, {reason}");
}
public async Task Send(string roomName, string message)
{
_logger.LogInformation($"Send {roomName} {message}");
if (!InputValidator.ValidateRoomName(roomName)) {
_logger.LogError($"Invalid roomName : {roomName}");
return ;
}
if (!InputValidator.ValidateMessage(message)) {
_logger.LogError($"Invalid message : {message}");
return ;
}
var groupname = ChatHubConstants.HubGroupRomsPrefix + roomName;
ChatRoomInfo chanInfo ;
if (!_cxManager.TryGetChanInfo(roomName, out chanInfo))
{
_logger.LogError($"No such room : {roomName}");
var noChanMsg = _localizer.GetString(ChatHubConstants.LabNoSuchChan).ToString();
NotifyUserInRoom(NotificationTypes.Error, roomName, noChanMsg);
return;
}
var userName = _cxManager.GetUserName(Context.ConnectionId);
if (!_cxManager.IsPresent(roomName, userName))
{
_logger.LogError($"{userName} Not present in room : {roomName}");
var notSentMsg = _localizer.GetString(ChatHubConstants.LabnoJoinNoSend).ToString();
NotifyUserInRoom(NotificationTypes.Error, roomName, notSentMsg);
return;
}
var group = Clients.Group(groupname);
var msg = new { Name = userName, Room = roomName, Message = message};
await group.SendAsync("ReceiveMessage", msg);
}
async Task NotifyUser(string type, string targetId, string message)
{
_logger.LogInformation($"notifying user {type} {targetId} : {message}");
await Clients.Caller.SendAsync("notifyUser", type, targetId, message);
}
async Task NotifyUserInRoom(string type, string room, string message)
{
await Clients.Caller.SendAsync("notifyUserInRoom", type, room, message);
}
[Authorize]
public async Task SendPV(string userName, string message)
{
// Authorized code
Debug.Assert(Context.User != null);
_logger.LogInformation($"Sending pv to {userName}");
if (!InputValidator.ValidateUserName(userName))
{
_logger.LogError($"Invalid username : {userName}");
return ;
}
if (!InputValidator.ValidateMessage(message))
{
_logger.LogError($"Invalid message : {message}");
return ;
}
_logger.LogInformation($"Message form is validated.");
var identityUserName = Context.User.GetUserName();
if (userName[0] != '?' && Context.User!=null)
if (!Context.User.IsInMsRole(Constants.AdminGroupName))
{
var bl = _dbContext.BlackListed
.Include(r => r.User)
.Include(r => r.Owner)
.Where(r => r.User.UserName == identityUserName && r.Owner.UserName == userName)
.Select(r => r.OwnerId);
if (bl.Count() > 0)
{
_logger.LogError($"Black listed : {identityUserName}");
await NotifyUser(NotificationTypes.PrivateMessageDenied, userName, "you are black listed.");
return;
}
_logger.LogInformation("Sender is no black listed");
}
_logger.LogInformation("getting cx id´s");
var cxIds = _cxManager.GetConnexionIds(userName);
if (cxIds==null || cxIds.Count()==0)
_logger.LogError($"No such connected user : {userName}");
else foreach (var connectionId in cxIds)
{
_logger.LogInformation($"cx: {connectionId}");
var cli = Clients.Client(connectionId);
_logger.LogInformation($"cli: {cli.ToString()}");
await cli.SendAsync("addPV", identityUserName, message);
_logger.LogInformation($"Sent pv to cx {connectionId}");
}
}
[Authorize]
public async Task SendStream(string connectionId, long streamId, string message)
{
// Authorized code
Debug.Assert(Context.User != null);
Debug.Assert(Context.User.Identity != null);
if (!InputValidator.ValidateMessage(message)) return;
var sender = Context.User.Identity.Name;
var cli = Clients.Client(connectionId);
await cli.SendAsync("addStreamInfo", sender, streamId, message);
}
}
}

View file

@ -0,0 +1,43 @@
//
// ICalendarManager.cs
//
// Author:
// Paul Schneider <paul@pschneider.fr>
//
// Copyright (c) 2015 GNU GPL
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using Google.Apis.Calendar.v3.Data;
namespace Yavsc.Services
{
using System.Threading.Tasks;
using Yavsc.ViewModels.Calendar;
/// <summary>
/// I calendar manager.
/// </summary>
public interface ICalendarManager {
Task<CalendarList> GetCalendarsAsync (string pageToken);
Task<Events> GetCalendarAsync (string calid, DateTime minDate, DateTime maxDate, string pageToken);
Task<DateTimeChooserViewModel> CreateViewModelAsync(
string inputId,
string calid, DateTime mindate, DateTime maxdate);
Task<Event> CreateEventAsync(string userId, string calid,
DateTime startDate, int lengthInSeconds, string summary,
string description, string location, bool available);
}
}

View file

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using Yavsc.ViewModels.Chat;
namespace Yavsc.Services
{
public interface IConnexionManager {
void SetUserName(string cxId, string userName);
string GetUserName (string cxId);
void OnConnected(string cxId, bool isCop);
bool IsConnected(string candidate);
void OnDisctonnected (string cxId);
bool IsPresent(string roomName, string userName);
ChatRoomInfo Join(string roomName, string cxId);
bool Part(string cxId, string roomName, string reason);
bool Kick(string cxId, string userName, string roomName, string reason);
bool Op(string roomName, string userName);
bool Deop(string roomName, string userName);
bool Hop(string roomName, string userName);
bool Dehop(string roomName, string userName);
bool TryGetChanInfo(string room, out ChatRoomInfo chanInfo);
IEnumerable<string> GetConnexionIds(string userName);
void SetErrorHandler(Action<string,string> errorHandler);
IEnumerable<ChannelShortInfo> ListChannels(string pattern);
}
}

View file

@ -0,0 +1,12 @@
using System;
namespace Yavsc.Services
{
public interface IDiskUsageTracker
{
bool GetSpace(string userName, long space);
void Release(string userName, long space);
}
}

View file

@ -0,0 +1,54 @@
// FreeDate.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2015 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Collections.Generic;
using Yavsc.Server.Models.Calendar;
namespace Yavsc.Models.Calendar
{
/// <summary>
/// Free date.
/// </summary>
public interface IFreeDateSet
{
/// <summary>
/// Gets or sets the reference.
/// </summary>
/// <value>The reference.</value>
IEnumerable<Period> Values { get; set; }
/// <summary>
/// Gets or sets the duration.
/// </summary>
/// <value>The duration.</value>
TimeSpan Duration { get; set; }
/// <summary>
/// Gets or sets the attendees.
/// </summary>
/// <value>The attendees.</value>
string UserName { get; set; }
/// <summary>
/// Gets or sets the location.
/// </summary>
/// <value>The location.</value>
string Location { get; set; }
}
}

View file

@ -0,0 +1,26 @@
using System.Collections.Concurrent;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Yavsc.Models;
using Yavsc.ViewModels.Streaming;
namespace Yavsc.Services
{
public interface ILiveProcessor {
/// <summary>
/// instance keeping reference on
/// all collections of casting and listenning websockets
/// </summary>
/// <value></value>
ConcurrentDictionary<string, LiveCastHandler> Casters { get; }
/// <summary>
/// Try and accept websocket from aspnet http context
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
Task<bool> AcceptStream (HttpContext context, ApplicationUser user, string destDir, string fileName);
}
}

View file

@ -0,0 +1,36 @@
//
// IScheduledEvent.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2015 - 2017 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using Yavsc.Server.Models.Calendar;
namespace Yavsc.Models.Calendar
{
public interface IScheduledEvent
{
/// <summary>
/// Gets or sets the period.
/// </summary>
/// <value>The period.</value>
Periodicity Reccurence { get; set; }
Period Period { get; set; }
}
}

View file

@ -0,0 +1,10 @@
using System.Threading.Tasks;
namespace Yavsc.Services
{
public interface ISmsSender
{
Task SendSmsAsync(TwilioSettings settings, string number, string message);
}
}

View file

@ -0,0 +1,7 @@
namespace Yavsc.Server
{
public interface ITranslator
{
string[] Translate (string slang, string dlang, string[] text);
}
}

View file

@ -0,0 +1,15 @@
namespace Yavsc.Interface
{
public interface ITrueEmailSender
{
//
// Résumé :
// This API supports the ASP.NET Core Identity default UI infrastructure and is
// not intended to be used directly from your code. This API may change or be removed
// in future releases.
Task<string> SendEmailAsync(string name, string email, string subject, string htmlMessage);
}
}

View file

@ -0,0 +1,28 @@

using System.Collections.Generic;
using System.Threading.Tasks;
using Yavsc.Interfaces.Workflow;
using Yavsc.Models.Google.Messaging;
using Yavsc.Models.Haircut;
using Yavsc.Models.Messaging;
namespace Yavsc.Services
{
public interface IYavscMessageSender
{
Task<MessageWithPayloadResponse> NotifyBookQueryAsync(
IEnumerable<string> connectionIds,
RdvQueryEvent ev);
Task<MessageWithPayloadResponse> NotifyEstimateAsync(
IEnumerable<string> connectionIds,
EstimationEvent ev);
Task<MessageWithPayloadResponse> NotifyHairCutQueryAsync(
IEnumerable<string> connectionIds,
HairCutQueryEvent ev);
Task<MessageWithPayloadResponse> NotifyAsync(
IEnumerable<string> connectionIds,
IEvent yaev);
}
}

2
src/Server/Makefile Normal file
View file

@ -0,0 +1,2 @@
listConnections:
dotnet ef migrations list --connection "$(YAVSC_CONNECTION_STRING)"

View file

@ -0,0 +1,17 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace Yavsc.Models.Access
{
public class AccountOptions
{
public static bool AllowLocalLogin = true;
public static bool AllowRememberLogin = true;
public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30);
public static bool ShowLogoutPrompt = true;
public static bool AutomaticRedirectAfterSignOut = false;
public static string InvalidCredentialsErrorMessage = "Invalid username or password";
}
}

View file

@ -0,0 +1,48 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Yavsc.Models.Access
{
using Yavsc;
public class Ban : ITrackedEntity
{
public DateTime DateCreated
{
get; set;
}
public DateTime DateModified
{
get; set;
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public string UserCreated
{
get; set;
}
public string UserModified
{
get; set;
}
[Required]
public string TargetId
{
get; set;
}
[ForeignKey("TargetId")]
public virtual ApplicationUser TargetUser {
get; set;
}
[Required]
public string Reason { get; set; }
}
}

View file

@ -0,0 +1,18 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Yavsc.Models.Access
{
public class BanByEmail
{
[Required]
public long BanId { get; set; }
[ForeignKey("BanId")]
public virtual Ban UserBan { get; set; }
[Required]
public string email { get; set; }
}
}

View file

@ -0,0 +1,25 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Newtonsoft.Json;
namespace Yavsc.Models.Access
{
public class BlackListed: IBlackListed
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
[Required]
public string UserId { get; set; }
[Required]
public string OwnerId { get; set; }
[ForeignKey("OwnerId"), JsonIgnore]
public virtual ApplicationUser Owner { get; set; }
[ForeignKey("UserId"), JsonIgnore]
public virtual ApplicationUser User { get; set; }
}
}

View file

@ -0,0 +1,26 @@
namespace Yavsc.Models.Access
{
using System.ComponentModel.DataAnnotations.Schema;
using Models.Relationship;
using Newtonsoft.Json;
using Blog;
using Yavsc.Abstract.Identity.Security;
public class CircleAuthorizationToBlogPost : ICircleAuthorization
{
public long CircleId { get; set; }
public long BlogPostId { get; set; }
[JsonIgnore]
[ForeignKey("BlogPostId")]
public virtual BlogPost Target { get; set; }
[JsonIgnore]
[ForeignKey("CircleId")]
public virtual Circle Allowed { get; set; }
public bool Comment { get; set; }
}
}

View file

@ -0,0 +1,17 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
using System.Collections.Generic;
namespace Yavsc.Models.Access
{
public class ConsentInputModel
{
public string Button { get; set; }
public IEnumerable<string> ScopesConsented { get; set; }
public bool RememberConsent { get; set; }
public string ReturnUrl { get; set; }
public string Description { get; set; }
}
}

View file

@ -0,0 +1,19 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
using System.Collections.Generic;
namespace Yavsc.Models.Access
{
public class ConsentViewModel : ConsentInputModel
{
public string ClientName { get; set; }
public string ClientUrl { get; set; }
public string ClientLogoUrl { get; set; }
public bool AllowRememberConsent { get; set; }
public IEnumerable<ScopeViewModel> IdentityScopes { get; set; }
public IEnumerable<ScopeViewModel> ApiScopes { get; set; }
}
}

View file

@ -0,0 +1,11 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace Yavsc.Models.Access
{
public class DeviceAuthorizationInputModel : ConsentInputModel
{
public string UserCode { get; set; }
}
}

View file

@ -0,0 +1,13 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace Yavsc.Models.Access
{
public class DeviceAuthorizationViewModel : ConsentViewModel
{
public string UserCode { get; set; }
public bool ConfirmUserCode { get; set; }
}
}

View file

@ -0,0 +1,19 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace Yavsc.Models.Access
{
public class GrantViewModel
{
public string ClientId { get; set; }
public string ClientName { get; set; }
public string ClientUrl { get; set; }
public string ClientLogoUrl { get; set; }
public string Description { get; set; }
public DateTime Created { get; set; }
public DateTime? Expires { get; set; }
public IEnumerable<string> IdentityGrantNames { get; set; }
public IEnumerable<string> ApiGrantNames { get; set; }
}
}

View file

@ -0,0 +1,11 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace Yavsc.Models.Access
{
public class GrantsViewModel
{
public IEnumerable<GrantViewModel> Grants { get; set; }
}
}

View file

@ -0,0 +1,19 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace Yavsc.Models.Access
{
public class LoggedOutViewModel
{
public string PostLogoutRedirectUri { get; set; }
public string ClientName { get; set; }
public string SignOutIframeUrl { get; set; }
public bool AutomaticRedirectAfterSignOut { get; set; }
public string LogoutId { get; set; }
public bool TriggerExternalSignout => ExternalAuthenticationScheme != null;
public string ExternalAuthenticationScheme { get; set; }
}
}

View file

@ -0,0 +1,11 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace Yavsc.Models.Access
{
public class LogoutInputModel
{
public string LogoutId { get; set; }
}
}

View file

@ -0,0 +1,21 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
using IdentityServer8.Models;
namespace Yavsc.Models.Access
{
public class ProcessConsentResult
{
public bool IsRedirect => RedirectUri != null;
public string RedirectUri { get; set; }
public Client Client { get; set; }
public bool ShowView => ViewModel != null;
public ConsentViewModel ViewModel { get; set; }
public bool HasValidationError => ValidationError != null;
public string ValidationError { get; set; }
}
}

View file

@ -0,0 +1,50 @@
//
// Publishing.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2015 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace Yavsc.Models.Access
{
/// <summary>
/// Publishing.
/// </summary>
public enum Publishing {
/// <summary>
/// In the context of immediate use, with no related stored content.
/// </summary>
None,
/// <summary>
/// In the context of private use of an uploaded content.
/// </summary>
Private,
/// <summary>
/// In the context of restricted access areas, like circle members views.
/// </summary>
Restricted,
/// <summary>
/// Publishing a content in a public access area.
/// </summary>
Public
}
}

View file

@ -0,0 +1,10 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace Yavsc.Models.Access
{
public class RedirectViewModel
{
public string RedirectUrl { get; set; }
}
}

View file

@ -0,0 +1,14 @@
namespace Yavsc.Models.Access
{
public abstract class Rule<TResource,TRequirement>
{
public Rule()
{
}
// Abstract method to compute any authorization on a resource
public abstract bool Allow(string userId, TResource resource, TRequirement requirement);
}
}

View file

@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace Yavsc.Models.Access
{
public abstract class RuleSet <TResource,TRequirement>:List<Rule<TResource,TRequirement>> {
public abstract bool Allow(string userId, TResource resource, TRequirement requirement);
}
}

View file

@ -0,0 +1,16 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace Yavsc.Models.Access
{
public class ScopeViewModel
{
public string Value { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
public bool Emphasize { get; set; }
public bool Required { get; set; }
public bool Checked { get; set; }
}
}

View file

@ -0,0 +1,8 @@
namespace Yavsc.Models.Access
{
public class WhiteCard {
}
}

View file

@ -0,0 +1,294 @@

using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Yavsc.Abstract.Models.Messaging;
using Yavsc.Server.Models.EMailing;
using Yavsc.Server.Models.IT.SourceCode;
using Yavsc.Server.Models.IT;
using Yavsc.Abstract.Identity;
using Yavsc.Server.Models.Calendar;
namespace Yavsc.Models
{
using Haircut;
using IT.Evolution;
using IT.Fixing;
using Streaming;
using Relationship;
using Forms;
using Auth;
using Billing;
using Musical;
using Workflow;
using Identity;
using Market;
using Chat;
using Messaging;
using Access;
using Musical.Profiles;
using Workflow.Profiles;
using Drawing;
using Attributes;
using Bank;
using Payment;
using Blog;
using IdentityServer8.Models;
using IdentityServer8.EntityFramework.Entities;
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IdentityServer8.EntityFramework.Interfaces.IConfigurationDbContext
{
public ApplicationDbContext()
{
}
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
builder.Entity<Contact>().HasKey(x => new { x.OwnerId, x.UserId });
builder.Entity<DeviceDeclaration>().Property(x => x.DeclarationDate).HasDefaultValueSql("LOCALTIMESTAMP");
builder.Entity<BlogTag>().HasKey(x => new { x.PostId, x.TagId });
builder.Entity<ApplicationUser>().Property(u => u.FullName).IsRequired(false);
builder.Entity<ApplicationUser>().Property(u => u.DedicatedGoogleCalendar).IsRequired(false);
builder.Entity<ApplicationUser>().HasMany<ChatConnection>(c => c.Connections);
builder.Entity<ApplicationUser>().Property(u => u.Avatar).HasDefaultValue(Constants.DefaultAvatar);
builder.Entity<ApplicationUser>().Property(u => u.DiskQuota).HasDefaultValue(Constants.DefaultFSQ);
builder.Entity<ApplicationUser>().HasAlternateKey(u => u.Email);
builder.Entity<BlackListed>().HasOne<ApplicationUser>(bl => bl.User);
builder.Entity<BlackListed>().HasOne<ApplicationUser>(bl => bl.Owner);
builder.Entity<UserActivity>().HasKey(u => new { u.DoesCode, u.UserId });
builder.Entity<Instrumentation>().HasKey(u => new { u.InstrumentId, u.UserId });
builder.Entity<CircleAuthorizationToBlogPost>().HasKey(a => new { a.CircleId, a.BlogPostId });
builder.Entity<CircleMember>().HasKey(c => new { c.MemberId, c.CircleId });
builder.Entity<DismissClicked>().HasKey(c => new { uid = c.UserId, notid = c.NotificationId });
builder.Entity<HairTaintInstance>().HasKey(ti => new { ti.TaintId, ti.PrestationId });
builder.Entity<HyperLink>().HasKey(l => new { l.HRef, l.Method });
builder.Entity<Period>().HasKey(l => new { l.Start, l.End });
builder.Entity<Cratie.Option>().HasKey(o => new { o.Code, o.CodeScrutin });
builder.Entity<Notification>().Property(n => n.icon).HasDefaultValue("exclam");
builder.Entity<ChatRoomAccess>().HasKey(p => new { room = p.ChannelName, user = p.UserId });
builder.Entity<InstrumentRating>().HasAlternateKey(i => new { Instrument = i.InstrumentId, owner = i.OwnerId });
foreach (var et in builder.Model.GetEntityTypes())
{
if (et.ClrType.GetInterface("IBaseTrackedEntity") != null)
et.FindProperty("DateCreated").SetAfterSaveBehavior(Microsoft.EntityFrameworkCore.Metadata.PropertySaveBehavior.Ignore);
}
builder.Entity<Activity>().Property(a => a.ParentCode).IsRequired(false);
// builder.Entity<IdentityUserLogin<String>>().HasKey(i=> new { i.LoginProvider, i.UserId, i.ProviderKey });
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
string? envCxStr = Environment.GetEnvironmentVariable(Constants.YavscConnectionStringEnvName);
if (envCxStr != null)
optionsBuilder.UseNpgsql(envCxStr);
base.OnConfiguring(optionsBuilder);
}
/// <summary>
/// Activities referenced on this site
/// </summary>
/// <returns></returns>
public DbSet<Activity> Activities { get; set; }
public DbSet<UserActivity> UserActivities { get; set; }
/// <summary>
/// Users posts
/// </summary>
/// <returns></returns>
public DbSet<BlogPost> BlogSpot { get; set; }
/// <summary>
/// Skills powered by this site
/// </summary>
/// <returns></returns>
public DbSet<Skill> SiteSkills { get; set; }
/// <summary>
/// Circle members
/// </summary>
/// <returns></returns>
public DbSet<CircleMember> CircleMembers { get; set; }
/// <summary>
/// Special commands, talking about
/// a given place and date.
/// </summary>
public DbSet<RdvQuery> RdvQueries { get; set; }
public DbSet<HairCutQuery> HairCutQueries { get; set; }
public DbSet<HairPrestation> HairPrestation { get; set; }
public DbSet<HairMultiCutQuery> HairMultiCutQueries { get; set; }
public DbSet<PerformerProfile> Performers { get; set; }
public DbSet<Estimate> Estimates { get; set; }
public DbSet<AccountBalance> BankStatus { get; set; }
public DbSet<BalanceImpact> BalanceImpact { get; set; }
/// <summary>
/// References all declared external NativeConfidential devices
/// </summary>
/// <returns></returns>
public DbSet<DeviceDeclaration> DeviceDeclaration { get; set; }
public DbSet<Service> Services { get; set; }
public DbSet<Product> Products { get; set; }
public DbSet<ExceptionSIREN> ExceptionsSIREN { get; set; }
public DbSet<Location> Locations { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<BlogTag> TagsDomain { get; set; }
public DbSet<EstimateTemplate> EstimateTemplates { get; set; }
public DbSet<Contact> Contact { get; set; }
public DbSet<ClientProviderInfo> ClientProviderInfo { get; set; }
public DbSet<BlackListed> BlackListed { get; set; }
public DbSet<MusicalPreference> MusicalPreference { get; set; }
public DbSet<MusicalTendency> MusicalTendency { get; set; }
public DbSet<Instrument> Instrument { get; set; }
[ActivitySettings]
public DbSet<DjSettings> DjSettings { get; set; }
[ActivitySettings]
public DbSet<Instrumentation> Instrumentation { get; set; }
[ActivitySettings]
public DbSet<FormationSettings> FormationSettings { get; set; }
[ActivitySettings]
public DbSet<GeneralSettings> GeneralSettings { get; set; }
public DbSet<CoWorking> CoWorking { get; set; }
private void AddTimestamps(string userId)
{
var entities =
ChangeTracker.Entries()
.Where(x => x.Entity.GetType().GetInterface(nameof(ITrackedEntity)) != null
&& (x.State == EntityState.Added || x.State == EntityState.Modified));
foreach (var entity in entities)
{
if (entity.State == EntityState.Added)
{
((ITrackedEntity)entity.Entity).DateCreated = DateTime.Now;
((ITrackedEntity)entity.Entity).UserCreated = userId;
}
((ITrackedEntity)entity.Entity).DateModified = DateTime.Now;
((ITrackedEntity)entity.Entity).UserModified = userId;
}
}
public int SaveChanges(string userId)
{
AddTimestamps(userId);
return base.SaveChanges();
}
public async Task<int> SaveChangesAsync(string userId, CancellationToken ctoken = default(CancellationToken))
{
AddTimestamps(userId);
return await base.SaveChangesAsync(ctoken);
}
public DbSet<Circle> Circle { get; set; }
public DbSet<CircleAuthorizationToBlogPost> CircleAuthorizationToBlogPost { get; set; }
public DbSet<CommandForm> CommandForm { get; set; }
public DbSet<Form> Form { get; set; }
public DbSet<Ban> Ban { get; set; }
public DbSet<HairTaint> HairTaint { get; set; }
public DbSet<Color> Color { get; set; }
public DbSet<Notification> Notification { get; set; }
public DbSet<DismissClicked> DismissClicked { get; set; }
[ActivitySettings]
public DbSet<BrusherProfile> BrusherProfile { get; set; }
public DbSet<BankIdentity> BankIdentity { get; set; }
public DbSet<PayPalPayment> PayPalPayment { get; set; }
public DbSet<HyperLink> HyperLink { get; set; }
public DbSet<Period> Period { get; set; }
public DbSet<BlogTag> BlogTag { get; set; }
public DbSet<ApplicationUser> ApplicationUser { get; set; }
public DbSet<Feature> Feature { get; set; }
public DbSet<Bug> Bug { get; set; }
public DbSet<Comment> Comment { get; set; }
public DbSet<Announce> Announce { get; set; }
// TODO remove and opt for for memory only storing,
// as long as it must be set empty each time the service is restarted,
// and that chatting should be kept as must as possible independent from db context
public DbSet<ChatConnection> ChatConnection { get; set; }
public DbSet<ChatRoom> ChatRoom { get; set; }
public DbSet<MailingTemplate> MailingTemplate { get; set; }
public DbSet<GitRepositoryReference> GitRepositoryReference { get; set; }
public DbSet<Project> Project { get; set; }
[Obsolete("use signaled flows")]
public DbSet<LiveFlow> LiveFlow { get; set; }
public DbSet<ChatRoomAccess> ChatRoomAccess { get; set; }
public DbSet<InstrumentRating> InstrumentRating { get; set; }
public DbSet<Scope> Scopes { get; set; }
public DbSet<BlogSpotPublication> blogSpotPublications { get; set; }
public DbSet<IdentityServer8.EntityFramework.Entities.Client> Clients { get; set; }
public DbSet<ClientCorsOrigin> ClientCorsOrigins { get; set; }
public DbSet<IdentityServer8.EntityFramework.Entities.IdentityResource> IdentityResources { get; set; }
public DbSet<IdentityServer8.EntityFramework.Entities.ApiResource> ApiResources { get; set; }
public DbSet<IdentityServer8.EntityFramework.Entities.ApiScope> ApiScopes { get; set; }
public DbSet<IdentityResourceClaim> IdentityResourceClaims { get; set; }
public DbSet<IdentityResourceProperty> IdentityResourceProperties { get; set; }
public DbSet<ApiResourceSecret> ApiResourceSecrets { get; set; }
public DbSet<ApiResourceScope> ApiResourceScopes { get; set; }
public DbSet<ApiResourceClaim> ApiResourceClaims { get; set; }
public DbSet<ApiResourceProperty> ApiResourceProperties { get; set; }
public DbSet<ApiScopeClaim> ApiScopeClaims { get; set; }
public DbSet<ApiScopeProperty> ApiScopeProperties { get; set; }
}
}

View file

@ -0,0 +1,121 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Identity;
using Yavsc.Models.Relationship;
using Yavsc.Models.Identity;
using Yavsc.Models.Chat;
using Yavsc.Models.Bank;
using Yavsc.Models.Access;
using Yavsc.Abstract.Identity;
namespace Yavsc.Models
{
[Table("AspNetUsers")]
public class ApplicationUser : IdentityUser, IApplicationUser
{
/// <summary>
/// Another me, as a byte array.TG7@Eu%80rufzkhbb
/// This value points a picture that may be used
/// to present the user
/// </summary>
/// <returns>the path to an user's image, relative to it's user dir<summary>
/// <see>Startup.UserFilesOptions</see>
/// </summary>
/// <returns></returns>
[MaxLength(512)]
public string? Avatar { get; set; }
[MaxLength(512)]
public string? FullName { get; set; }
/// <summary>
/// WIP Paypal
/// </summary>
/// <returns></returns>
[Display(Name = "Account balance")]
public virtual AccountBalance? AccountBalance { get; set; }
/// <summary>
/// User's posts
/// </summary>
/// <returns></returns>
[InverseProperty("Author"), JsonIgnore]
public virtual List<Blog.BlogPost>? Posts { get; set; }
/// <summary>
/// User's contact list
/// </summary>
/// <returns></returns>
[InverseProperty("Owner"), JsonIgnore]
public virtual List<Contact>? Book { get; set; }
/// <summary>
/// External devices using the API
/// </summary>
/// <returns></returns>
[InverseProperty("DeviceOwner"), JsonIgnore]
public virtual List<DeviceDeclaration>? DeviceDeclaration { get; set; }
[InverseProperty("Owner"), JsonIgnore]
public virtual List<ChatConnection>? Connections { get; set; }
/// <summary>
/// User's circles
/// </summary>
/// <returns></returns>
[InverseProperty("Owner"), JsonIgnore]
public virtual List<Circle>? Circles { get; set; }
/// <summary>
/// Billing postal address
/// </summary>
/// <returns></returns>
[ForeignKey("PostalAddressId")]
public virtual Location? PostalAddress { get; set; }
public long? PostalAddressId { get; set; }
/// <summary>
/// User's Google calendar
/// </summary>
/// <returns></returns>
[MaxLength(512)]
public string? DedicatedGoogleCalendar { get; set; }
public override string ToString()
{
return this.Id + " " + this.AccountBalance?.Credits.ToString() + this.Email + " " + this.UserName + " $" + this.AccountBalance?.Credits.ToString();
}
public virtual List<BankIdentity>? BankInfo { get; set; }
public long DiskQuota { get; set; } = 512 * 1024 * 1024;
public long DiskUsage { get; set; } = 0;
public long MaxFileSize { get; set; } = 512 * 1024 * 1024;
[JsonIgnore]
[InverseProperty("Owner")]
public virtual List<BlackListed>? BlackList { get; set; }
public bool AllowMonthlyEmail { get; set; } = false;
[JsonIgnore]
[InverseProperty("Owner")]
public virtual List<ChatRoom>? Rooms { get; set; }
[JsonIgnore]
[InverseProperty("User")]
public virtual List<ChatRoomAccess>? RoomAccess { get; set; }
[JsonIgnore]
[InverseProperty("Member")]
public virtual List<CircleMember>? Membership { get; set; }
IAccountBalance? IApplicationUser.AccountBalance => AccountBalance;
ILocation? IApplicationUser.PostalAddress { get => PostalAddress; }
}
}

View file

@ -0,0 +1,10 @@
namespace Yavsc.Models.Auth
{
public enum ApplicationTypes: int
{
JavaScript = 0,
NativeConfidential = 1
};
}

View file

@ -0,0 +1,42 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Newtonsoft.Json;
namespace Yavsc.Models.Identity
{
[JsonObject]
public class DeviceDeclaration : IMobileDeviceDeclaration {
[Key,Required]
public string DeviceId { get; set; }
public string Model { get; set; }
public string Platform { get; set; }
public string Version { get; set; }
public string DeviceOwnerId { get; set; }
public DateTime DeclarationDate { get; set; }
/// <summary>
/// Latest Activity Update
///
/// Let's says,
/// the latest time this device downloaded functional info from server
/// activity list, let's say, promoted ones, those thar are at index, and
/// all others, that are not listed as unsupported ones (not any more, after
/// has been annonced as obsolete a decent laps of time).
///
/// In order to say, is any activity has changed here.
/// </summary>
/// <returns></returns>
public DateTime ? LatestActivityUpdate { get; set; }
[JsonIgnore,ForeignKey("DeviceOwnerId")]
public virtual ApplicationUser DeviceOwner { get; set; }
}
}

View file

@ -0,0 +1,33 @@
using System.ComponentModel.DataAnnotations;
namespace Yavsc.Models.Auth {
public class ExternalLoginViewModel
{
public string Name { get; set; }
public string Url { get; set; }
public string State { get; set; }
}
public class RegisterExternalBindingModel
{
[Required]
public string UserName { get; set; }
[Required]
public string Provider { get; set; }
[Required]
public string ExternalAccessToken { get; set; }
}
public class ParsedExternalAccessToken
{
public string user_id { get; set; }
public string app_id { get; set; }
}
}

View file

@ -0,0 +1,48 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace Yavsc.Models.Auth
{
/// <summary>
/// OffLine OAuth2 Token
/// To use against a third party Api
/// </summary>
public partial class OAuth2Tokens
{
/// <summary>
/// Unique identifier, equals the user email from OAuth provider
/// </summary>
/// <returns></returns>
[Key]
public string UserId { get; set; }
/// <summary>
/// Expiration date &amp; time
/// </summary>
/// <returns></returns>
public DateTime Expiration { get; set; }
/// <summary>
/// Expiration time span in seconds
/// </summary>
/// <returns></returns>
public string ExpiresIn { get; set; }
/// <summary>
/// Should always be <c>Bearer</c> ...
/// </summary>
/// <returns></returns>
public string TokenType { get; set; }
/// <summary>
/// The Access Token!
/// </summary>
/// <returns></returns>
public string AccessToken { get; set; }
/// <summary>
/// The refresh token
/// </summary>
/// <returns></returns>
public string RefreshToken { get; set; }
}
}

View file

@ -0,0 +1,23 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace Yavsc.Models.Auth
{
public class RefreshToken
{
[Key]
public string Id { get; set; }
[Required]
[MaxLength(50)]
public string Subject { get; set; }
[Required]
[MaxLength(50)]
public string ClientId { get; set; }
public DateTime IssuedUtc { get; set; }
public DateTime ExpiresUtc { get; set; }
[Required]
public string ProtectedTicket { get; set; }
}
}

View file

@ -0,0 +1,20 @@
namespace Yavsc.Models.Auth {
public class UserCredential {
public string UserId { get; set; }
public OAuth2Tokens Tokens { get; set; }
public UserCredential(string userId, OAuth2Tokens tokens)
{
UserId = userId;
Tokens = tokens;
}
public string GetHeader()
{
return Tokens.TokenType+" "+Tokens.AccessToken;
}
}
}

View file

@ -0,0 +1,22 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Yavsc.Models
{
using Yavsc;
public partial class AccountBalance: IAccountBalance {
[Key]
public string UserId { get; set; }
[ForeignKey("UserId")]
public virtual ApplicationUser Owner { get; set; }
[Required,Display(Name="Credits en €")]
public decimal Credits { get; set; }
public long ContactCredits { get; set; }
}
}

View file

@ -0,0 +1,26 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Yavsc.Models
{
public partial class BalanceImpact {
[Required,Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
[Required,Display(Name="Impact")]
public decimal Impact { get; set; }
[Required,Display(Name="Execution date")]
public DateTime ExecDate { get; set; }
[Required,Display(Name="Reason")]
public string Reason { get; set; }
[Required]
public string BalanceId { get; set; }
[ForeignKey("BalanceId")]
public virtual AccountBalance Balance { get; set; }
}
}

View file

@ -0,0 +1,79 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Yavsc.Attributes.Validation;
namespace Yavsc.Models.Bank
{
public class BankIdentity
{
[Key(), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
/// <summary>
/// Gets or sets the BI.
/// </summary>
/// <value>The BI.</value>
[DisplayName("Code BIC")]
[YaStringLength(15)]
public string BIC { get; set; }
/// <summary>
/// Gets or sets the IBA.
/// </summary>
/// <value>The IBA.</value>
[DisplayName("Code IBAN")]
[YaStringLength(33)]
public string IBAN { get; set; }
/// <summary>
/// Gets or sets the bank code.
/// </summary>
/// <value>The bank code.</value>
[DisplayName("Code Banque")]
[YaStringLength(5)]
public string BankCode { get; set; }
/// <summary>
/// Gets or sets the wicket code.
/// </summary>
/// <value>The wicket code.</value>
[DisplayName("Code Guichet")]
[YaStringLength(5)]
public string WicketCode { get; set; }
/// <summary>
/// Gets or sets the account number.
/// </summary>
/// <value>The account number.</value>
[DisplayName("Numéro de compte")]
[YaStringLength(15)]
public string AccountNumber { get; set; }
/// <summary>
/// Gets or sets the banked key.
/// </summary>
/// <value>The banked key.</value>
[DisplayName("Clé RIB")]
public int BankedKey { get; set; }
public virtual ApplicationUser User { get; set; }
public string UserId { get; set; }
public override bool Equals(object? obj)
{
if (obj==null) return false;
if (! typeof(BankIdentity).IsAssignableFrom(obj.GetType())) return false;
BankIdentity tobj = (BankIdentity)obj;
return tobj.IBAN == IBAN &&
tobj.BIC == BIC &&
tobj.AccountNumber == AccountNumber &&
tobj.BankedKey == BankedKey;
}
}
}

View file

@ -0,0 +1,48 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Newtonsoft.Json;
namespace Yavsc.Models.Billing
{
using Yavsc.Billing;
public class CommandLine : ICommandLine {
[Key(), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
[Required,MaxLength(256)]
public string Name { get; set; }
[Required,MaxLength(512)]
public string Description { get; set; }
[Display(Name="Nombre")]
public int Count { get; set; } = 1;
[DisplayFormat(DataFormatString="{0:C}")]
public decimal UnitaryCost { get; set; }
public long EstimateId { get; set; }
[JsonIgnore,NotMapped,ForeignKey("EstimateId")]
virtual public Estimate Estimate { get; set; }
public string Currency
{
get;
set;
} = "EUR";
[NotMapped]
public string Reference {
get {
return "CL/"+this.Id;
}
}
}
}

View file

@ -0,0 +1,10 @@
using Yavsc.Models.Market;
namespace Yavsc.Models.Billing
{
public class ServiceContract<P> where P : Service
{
}
}

View file

@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
namespace Yavsc.Models.Billing
{
using Models.Workflow;
using Newtonsoft.Json;
public partial class Estimate : IEstimate
{
[Key(), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public long? CommandId { get; set; }
/// <summary>
/// A command is not required to create
/// an estimate,
/// it will result in a new estimate template
/// </summary>
/// <returns></returns>
[ForeignKey("CommandId"),JsonIgnore]
public RdvQuery Query { get; set; }
public string Description { get; set; }
public string Title { get; set; }
[InverseProperty("Estimate")]
public virtual List<CommandLine> Bill { get; set; }
/// <summary>
/// List of attached graphic files
/// to this estimate, as relative pathes to
/// the command performer's root path.
/// In db, they are separated by <c>:</c>
/// </summary>
/// <returns></returns>
[NotMapped]
public List<string> AttachedGraphics { get; set; }
public string AttachedGraphicsString
{
get { return string.Join(":", AttachedGraphics); }
set { AttachedGraphics = value.Split(':').ToList(); }
}
/// <summary>
/// List of attached files
/// to this estimate, as relative pathes to
/// the command performer's root path.
/// In db, they are separated by <c>:</c>
/// </summary>
/// <returns></returns>
[NotMapped]
public List<string> AttachedFiles { get; set; }
public string AttachedFilesString
{
get { return string.Join(":", AttachedFiles); }
set { AttachedFiles = value.Split(':').ToList(); }
}
public string OwnerId { get; set; }
[ForeignKey("OwnerId"),JsonIgnore]
public virtual PerformerProfile Owner { get; set; }
[Required]
public string ClientId { get; set; }
[ForeignKey("ClientId"),JsonIgnore]
public virtual ApplicationUser Client { get; set; }
[Required]
public string CommandType
{
get; set;
}
public DateTime ProviderValidationDate { get; set; }
public DateTime ClientValidationDate { get; set; }
}
}

View file

@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Yavsc.Models.Billing
{
public partial class EstimateTemplate
{
[Key(), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public string Description { get; set; }
public string Title { get; set; }
public List<CommandLine> Bill { get; set; }
[Required]
public string OwnerId { get; set; }
}
}

View file

@ -0,0 +1,11 @@
using System.ComponentModel.DataAnnotations;
using Yavsc.Attributes.Validation;
namespace Yavsc.Models.Billing
{
public class ExceptionSIREN {
[Key, YaStringLength(9, 9)]
public string SIREN { get; set; }
}
}

View file

@ -0,0 +1,16 @@
using Yavsc.Billing;
namespace Yavsc.Models.Billing   {
public class FixedImpacter : IBillingImpacter
{
public decimal ImpactedValue { get; set; }
public FixedImpacter (decimal impact)
{
ImpactedValue = impact;
}
public decimal Impact(decimal orgValue)
{
return orgValue + ImpactedValue;
}
}
}

View file

@ -0,0 +1,107 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Yavsc.Models.Billing
{
using Newtonsoft.Json;
using Workflow;
using Yavsc.Models.Payment;
using Yavsc;
using Yavsc.Billing;
using Yavsc.Abstract.Workflow;
using Yavsc.Services;
public abstract class NominativeServiceCommand : IDecidableQuery, IIdentified<long>, IBillable
{
public string GetInvoiceId() { return GetType().Name + "/" + Id; }
public abstract long Id { get; set; }
public abstract string Description { get; set; }
[Required()]
public bool Consent { get; set; }
public DateTime DateCreated
{
get; set;
}
public DateTime DateModified
{
get; set;
}
public string UserCreated
{
get; set;
}
public string UserModified
{
get; set;
}
[DisplayAttribute(Name="Status de la requête")]
public QueryStatus Status { get; set; }
[Required]
public string ClientId { get; set; }
/// <summary>
/// The client
/// </summary>
[ForeignKey("ClientId"),Display(Name="Client")]
public ApplicationUser Client { get; set; }
[Required]
public string PerformerId { get; set; }
/// <summary>
/// The performer identifier
/// </summary>
[ForeignKey("PerformerId"),Display(Name="Préstataire")]
public PerformerProfile PerformerProfile { get; set; }
public DateTime? ValidationDate {get; set;}
[Display(Name="Previsional")]
public decimal? Previsional { get; set; }
/// <summary>
/// The bill
/// </summary>
/// <returns></returns>
[Required]
public string ActivityCode { get; set; }
[ForeignKey("ActivityCode"),JsonIgnore,Display(Name="Domaine d'activité")]
public virtual Activity Context  { get; set ; }
public bool Decided { get; set; }
public abstract System.Collections.Generic.List<IBillItem> GetBillItems();
public bool GetIsAcquitted()
{
return Regularisation?.IsOk() ?? false;
}
public string GetFileBaseName(IBillingService billingService)
{
string type = GetType().Name;
string ack = GetIsAcquitted() ? "-ack" : null;
var bcode = billingService.BillingMap[type];
return $"facture-{bcode}-{Id}{ack}";
}
[ForeignKey("Regularisation")]
public string? PaymentId { get; set; }
[Display(Name = "Acquittement de la facture")]
public virtual PayPalPayment Regularisation { get; set; }
public bool Accepted { get; set; }
}
}

View file

@ -0,0 +1,13 @@
using Yavsc.Billing;
namespace Yavsc.Models.Billing   {
public class ProportionalImpacter : IBillingImpacter
{
public decimal K { get; set; }
public decimal Impact(decimal orgValue)
{
return orgValue * K;
}
}
}

View file

@ -0,0 +1,33 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Yavsc.Billing;
namespace Yavsc.Models.Billing {
public class ReductionCode : IBillingClause
{
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public ReductionCode(string descr, decimal impact) {
Description = descr;
impacter = new FixedImpacter(impact);
}
public string Description
{
get;
set;
}
IBillingImpacter impacter;
public IBillingImpacter Impacter
{
get
{
return impacter ;
}
private set {
impacter = value;
}
}
}
}

View file

@ -0,0 +1,5 @@
class ChatBilling { 
}

View file

@ -0,0 +1,14 @@
using System;
namespace Yavsc.Models.Billing
{
public partial class histoestim
{
public long _id { get; set; }
public string applicationname { get; set; }
public DateTime datechange { get; set; }
public long estid { get; set; }
public int? status { get; set; }
public string username { get; set; }
}
}

View file

@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Yavsc;
using Yavsc.Abstract.Identity;
using Yavsc.Abstract.Identity.Security;
using Yavsc.Attributes.Validation;
using Yavsc.Interfaces;
using Yavsc.Models.Access;
using Yavsc.Models.Relationship;
using Yavsc.ViewModels.Blog;
namespace Yavsc.Models.Blog
{
public class BlogPost : BlogPostBase,
IBlogPost, ICircleAuthorized, ITaggable<long>
{
[Key(), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Display(Name="Identifiant du post")]
public long Id { get; set; }
[Display(Name="Identifiant de l'auteur")]
[ForeignKey("Author")]
public string? AuthorId { get; set; }
[Display(Name="Auteur")]
public virtual ApplicationUser? Author { set; get; }
[Display(Name="Date de création")]
public DateTime DateCreated
{
get; set;
}
[Display(Name="Créateur")]
public string? UserCreated
{
get; set;
}
[Display(Name="Dernière modification")]
public DateTime DateModified
{
get; set;
}
[Display(Name="Utilisateur ayant modifé le dernier")]
public string? UserModified
{
get; set;
}
public bool AuthorizeCircle(long circleId)
{
return ACL?.Any( i=>i.CircleId == circleId) ?? true;
}
public ICircleAuthorization[] GetACL()
{
return ACL.ToArray();
}
public void Tag(Tag tag)
{
var existent = Tags.SingleOrDefault(t => t.PostId == Id && t.TagId == tag.Id);
if (existent==null) Tags.Add(new BlogTag { PostId = Id, Tag = tag } );
}
public void DeTag(Tag tag)
{
var existent = Tags.SingleOrDefault(t => (( t.TagId == tag.Id) && t.PostId == Id));
if (existent!=null) Tags.Remove(existent);
}
public string[] GetTags()
{
return Tags.Select(t=>t.Tag.Name).ToArray();
}
[InverseProperty("Post")]
public virtual List<BlogTag> Tags { get; set; }
[InverseProperty("Post")]
public virtual List<Comment> Comments { get; set; }
IApplicationUser IBlogPost.Author { get => this.Author; }
}
}

View file

@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations.Schema;
using Yavsc.Models.Relationship;
namespace Yavsc.Models.Blog
{
public partial class BlogTag
{
[ForeignKey("PostId")]
public virtual BlogPost Post { get; set; }
public long PostId { get; set; }
[ForeignKey("TagId")]
public virtual Tag Tag{ get; set; }
public long TagId { get; set; }
}
}

View file

@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Yavsc.Models.Blog;
namespace Yavsc.Models
{
public class BlogSpotPublication
{
[Key]
public long BlogpostId { get; set; }
[ForeignKey("BlogpostId")]
public virtual BlogPost BlogPost{ get; set; }
}
}

View file

@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Newtonsoft.Json;
using Yavsc.Attributes.Validation;
using Yavsc.Interfaces;
namespace Yavsc.Models.Blog
{
public class Comment : IComment<long>, ITrackedEntity
{
[Key(), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
[YaStringLength(1024)]
public string Content { get; set; }
[ForeignKeyAttribute(nameof(ReceiverId))][JsonIgnore]
public virtual BlogPost Post { get; set; }
[Required]
public long ReceiverId { get; set; }
public bool Visible { get; set; }
[ForeignKeyAttribute("AuthorId")][JsonIgnore]
public virtual ApplicationUser Author {
get; set;
}
[Required]
public string AuthorId
{
get; set;
}
public string UserCreated { get => AuthorId; set => AuthorId=value; }
public DateTime DateModified
{
get; set;
}
public string UserModified
{
get; set;
}
public DateTime DateCreated
{
get; set;
}
public long? ParentId { get; set; }
[ForeignKeyAttribute("ParentId")]
public virtual Comment? Parent { get; set; }
[InversePropertyAttribute("Parent")]
public virtual List<Comment> Children { get; set; }
}
}

View file

@ -0,0 +1,9 @@
using Yavsc.Models.Calendar;
namespace Yavsc.Server.Models.Calendar
{
public class Availability : List<Period>
{
}
}

View file

@ -0,0 +1,52 @@
//
// Period.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2015 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.ComponentModel.DataAnnotations;
namespace Yavsc.Server.Models.Calendar
{
/// <summary>
/// Hollydays.
/// </summary>
public class Period {
/// <summary>
/// Gets or sets the start.
/// </summary>
/// <value>The start.</value>
[Required,Display(Name="Début")]
public DateTime Start { get; set; }
/// <summary>
/// Gets or sets the end.
/// </summary>
/// <value>The end.</value>
[Required,Display(Name="Fin")]
public DateTime End { get; set; }
public static Period operator ^ (Period foo, Period bar) {
var min = ( DateTime.Compare(foo.Start, bar.Start) > 0 ) ? foo.Start : bar.Start;
var max = ( DateTime.Compare(bar.End, foo.End) > 0 ) ? foo.End : bar.End;
if (DateTime.Compare(max, min)>0) return new Period { Start = min, End = max };
return null;
}
}
}

View file

@ -0,0 +1,59 @@
//
// Periodicity.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2015 - 2017 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace Yavsc.Server.Models.Calendar
{
/// <summary>
/// Periodicity.
/// </summary>
public enum Periodicity {
/// <summary>
/// On Demand.
/// </summary>
OnDemand=-1,
/// <summary>
/// The daily.
/// </summary>
Daily,
/// <summary>
/// The weekly.
/// </summary>
Weekly,
/// <summary>
/// The monthly.
/// </summary>
Monthly,
/// <summary>
/// The three m.
/// </summary>
ThreeM,
/// <summary>
/// The six m.
/// </summary>
SixM,
/// <summary>
/// The yearly.
/// </summary>
Yearly
}
}

View file

@ -0,0 +1,42 @@
//
// PositionAndKeyphrase.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2015 - 2017 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using Yavsc.Models.Relationship;
namespace Yavsc.Server.Models.Calendar
{
/// <summary>
/// Position and keyphrase.
/// </summary>
public class PositionAndKeyphrase {
/// <summary>
/// The phrase.
/// </summary>
public string phrase;
/// <summary>
/// The position.
/// </summary>
public Position pos;
}
}

View file

@ -0,0 +1,45 @@
//
// ProvidedEvent.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2015 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System.ComponentModel.DataAnnotations;
using Yavsc.Models.Messaging;
using Yavsc.Models.Access;
namespace Yavsc.Models.Calendar
{
/// <summary>
/// Provided event.
/// </summary>
public class ProvidedEvent : BaseEvent {
/// <summary>
/// The privacy.
/// </summary>
[Required]
public Publishing Privacy;
public override string CreateBody()
{
throw new System.NotImplementedException();
}
}
}

View file

@ -0,0 +1,64 @@
//
// Schedule.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2015 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// Calendar.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2015 - 2017 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Yavsc.Models.Calendar
{
/// <summary>
/// Le calendrier, l'emploi du temps.
/// </summary>
public class Schedule {
[Key,Required]
public string OwnerId { get; set; }
[ForeignKey("OwnerId")]
[Display(Name="Professionnel")]
public virtual ApplicationUser Owner { get ; set; }
public ScheduledEvent [] Events { get ; set; }
}
}

View file

@ -0,0 +1,46 @@
//
// ScheduledEvent.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2015 - 2017 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Yavsc.Server.Models.Calendar;
namespace Yavsc.Models.Calendar
{
public class ScheduledEvent : IScheduledEvent
{
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public Period Period
{
get;
set;
}
public Periodicity Reccurence
{
get;
set;
}
}
}

View file

@ -0,0 +1,58 @@
//
// WeekDay.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2015 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace Yavsc.Models.Calendar
{
/// <summary>
/// Week day.
/// </summary>
public enum WeekDay:int {
/// <summary>
/// The monday (0).
/// </summary>
Monday=0,
/// <summary>
/// The tuesday.
/// </summary>
Tuesday,
/// <summary>
/// The wednesday.
/// </summary>
Wednesday,
/// <summary>
/// The thursday.
/// </summary>
Thursday,
/// <summary>
/// The friday.
/// </summary>
Friday,
/// <summary>
/// The saturday.
/// </summary>
Saturday,
/// <summary>
/// The sunday.
/// </summary>
Sunday
}
}

View file

@ -0,0 +1,46 @@
//
// Connection.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2015 - 2017 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using Newtonsoft.Json;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Yavsc.Models.Chat
{
public class ChatConnection
{
[Required]
public string ApplicationUserId { get; set; }
[ForeignKey("ApplicationUserId"),JsonIgnore]
public virtual ApplicationUser Owner { get; set; }
[Key]
public string ConnectionId { get; set; }
public string UserAgent { get; set; }
public bool Connected { get; set; }
}
}

View file

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Newtonsoft.Json;
using Yavsc.Abstract.Chat;
using Yavsc.Attributes.Validation;
namespace Yavsc.Models.Chat
{
public class ChatRoom: IChatRoom<ChatRoomAccess>, ITrackedEntity
{
public string Topic { get; set; }
[Key]
[YaStringLength(ChatHubConstants.MaxChanelName, MinimumLength=3)]
public string Name { get; set;}
public string OwnerId { get; set; }
[ForeignKey("OwnerId")][JsonIgnore]
public virtual ApplicationUser Owner { get; set; }
[InverseProperty("Room")][JsonIgnore]
public virtual List<ChatRoomAccess> Moderation { get; set; }
public DateTime LatestJoinPart { get; set;}
public DateTime DateCreated { get; set; }
public string UserCreated { get; set; }
public DateTime DateModified { get; set;}
public string UserModified { get; set; }
}
}

View file

@ -0,0 +1,31 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Yavsc.Abstract.Chat;
namespace Yavsc.Models.Chat
{
public class ChatRoomAccess: IChatRoomAccess
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id
{
get; set;
}
public string ChannelName { get; set; }
[ForeignKey("ChannelName")]
public virtual ChatRoom Room { get; set; }
[Required]
public string UserId { get; set; }
public ChatRoomAccessLevel Level
{
get; set;
}
[ForeignKey("UserId")]
public virtual ApplicationUser User { get; set; }
}
}

View file

@ -0,0 +1,16 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace Yavsc
{
public class ConsentOptions
{
public static bool EnableOfflineAccess = true;
public static string OfflineAccessDisplayName = "Offline Access";
public static string OfflineAccessDescription = "Access to your applications and resources, even when you are offline";
public static readonly string MustChooseOneErrorMessage = "You must pick at least one permission";
public static readonly string InvalidSelectionErrorMessage = "Invalid selection";
}
}

View file

@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations;
namespace Yavsc.Models.Cratie.AName
{
public class NameSubmission : Submission
{
[RegularExpression(@"[a-zA-Z]+", ErrorMessage = "Nom invalide (seules les lettres de l'alphabet sont autorisées).", ErrorMessageResourceName = "EInvalidName")]
public string FirstChoice {get; set;}
[RegularExpression(@"[a-zA-Z]+", ErrorMessage = "Nom invalide (seules les lettres de l'alphabet sont autorisées).", ErrorMessageResourceName = "EInvalidName")]
public string SecondChoice {get; set;}
[RegularExpression(@"[a-zA-Z]+", ErrorMessage = "Nom invalide (seules les lettres de l'alphabet sont autorisées).", ErrorMessageResourceName = "EInvalidName")]
public string ThirdChoice {get; set;}
}
}

View file

@ -0,0 +1,15 @@
using System;
namespace Yavsc.Models.Cratie
{
public class Option: ITrackedEntity
{
public string CodeScrutin { get; set; }
public string Code { get; set ; }
public string Description { get; set; }
public DateTime DateCreated { get ; set ; }
public string UserCreated { get ; set ; }
public DateTime DateModified { get ; set ; }
public string UserModified { get ; set ; }
}
}

View file

@ -0,0 +1,16 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace Yavsc.Models.Cratie
{
public class Scrutin : ITrackedEntity
{
[Key]
public string Code { get; set ; }
public string Description { get ; set ; }
public DateTime DateCreated { get; set; }
public string UserCreated { get; set; }
public DateTime DateModified { get; set; }
public string UserModified { get; set; }
}
}

View file

@ -0,0 +1,19 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace Yavsc.Models.Cratie
{
public class Submission
{
[ForeignKey("CodeScrutin")]
public virtual Scrutin Context { get; set; }
public string CodeScrutin { get; set ; }
[ForeignKey("CodeOption")]
public virtual Option Choice { get; set; }
public string CodeOption { get; set; }
[ForeignKey("AuthorId")]
public virtual ApplicationUser Author { get; set; }
public string AuthorId { get ; set ;}
}
}

View file

@ -0,0 +1,46 @@
//
// Color.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2015 - 2017 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Yavsc.Models.Drawing
{
public class Color
{
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public Color(){
}
public Color(Color c)
{
Red=c.Red;
Green=c.Green;
Blue=c.Blue;
Name=c.Name;
}
public byte Red {get;set;}
public byte Green {get;set;}
public byte Blue {get;set;}
public string Name { get; set; }
}
}

View file

@ -0,0 +1,67 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using RazorEngine.Templating;
using Yavsc.Attributes.Validation;
using Yavsc.Models;
using Yavsc.Models.Calendar;
using Yavsc.Server.Models.Calendar;
namespace Yavsc.Server.Models.EMailing
{
public class MailingTemplate : ITrackedEntity, ITemplateSource
{
/// <summary>
/// Date Created
/// </summary>
/// <returns></returns>
public DateTime DateCreated
{
get;
set;
}
public DateTime DateModified
{
get;
set;
}
[Key][YaStringLength(3, 256)]
public string Id { get; set; }
[YaStringLength(3, 256)]
public string Topic { get; set; }
/// <summary>
/// Markdown template to process
/// </summary>
/// <returns></returns>
[MaxLength(64*1024)]
public string Body { get; set; }
[EmailAddress()]
public string ReplyToAddress { get; set; }
public Periodicity ToSend { get; set; }
public string UserCreated
{
get;
set;
}
public string UserModified
{
get;
set;
}
public string Template => Body;
public string TemplateFile { get => Id; }
public TextReader GetTemplateReader()
{
return new StringReader(Body);
}
}
}

View file

@ -0,0 +1,37 @@
//
// IDocument.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2015 - 2017 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
namespace Yavsc.Models {
public class Parameter {
public string Name { get; set; }
public string Value { get; set; }
}
[Obsolete("Templates are ala Razor")]
public interface IDocument {
string Template { get; set; }
Parameter [] Parameters { get; set; }
}
}

View file

@ -0,0 +1,10 @@
namespace Yavsc.Models;
public class ErrorViewModel
{
public string? RequestId { get; set; }
public string? Description { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}

View file

@ -0,0 +1,54 @@
//
// FileRecievedInfo.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2015 - 2017 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using Yavsc.Abstract.FileSystem;
namespace Yavsc.Models.FileSystem
{
public class FileReceivedInfo : IFileReceivedInfo
{
public FileReceivedInfo(string destDir, string fileName, bool quotaOffense=false)
{
this.DestDir = destDir;
this.FileName = fileName;
this.QuotaOffense = quotaOffense;
}
public static FileReceivedInfo FromPath(string filePath)
{
FileInfo fi = new FileInfo(filePath);
return new FileReceivedInfo(
fi.Directory.FullName,
fi.Name
);
}
public string DestDir { get; set; }
public string FileName { get; set; }
public bool Overridden { get; set; }
public bool QuotaOffense { get; set; }
public string FullName { get => Path.Combine(DestDir, FileName); }
}
}

View file

@ -0,0 +1,26 @@
using System.IO;
using System.Net.Mime;
namespace Yavsc.Server.Model
{
public class FormFile
{
public string Name { get; set; }
string contentDispositionString;
public string ContentDisposition { get {
return contentDispositionString;
} set {
ContentDisposition contentDisposition = new ContentDisposition(value);
Name = contentDisposition.FileName;
contentDispositionString = value;
} }
public string ContentType { get; set; }
public string FilePath { get; set; }
public Stream Stream { get; set; }
}
}

View file

@ -0,0 +1,12 @@
using System.ComponentModel.DataAnnotations;
namespace Yavsc.Models.Forms
{
public class Form
{
[Key]
public string Id {get; set;}
public string Summary { get; set; }
}
}

View file

@ -0,0 +1,17 @@
using System.ComponentModel.DataAnnotations;
namespace Yavsc.Models.Forms.Validation
{
public class Method
{
[Key]
public string Name {get; set; }
/// <summary>
/// TODO localisation ...
/// </summary>
/// <returns></returns>
[Required]
public string ErrorMessage { get; set; }
}
}

Some files were not shown because too many files have changed in this diff Show more