Add project files.

This commit is contained in:
Hamid Reza Mohammadi 2020-08-14 21:06:22 +04:30
commit 5991d7fa5f
144 changed files with 36919 additions and 0 deletions

63
.gitattributes vendored Normal file
View File

@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

263
.gitignore vendored Normal file
View File

@ -0,0 +1,263 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
project.fragment.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
#*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
/Museum/dist/js
/Museum/src/dist

31
GanjoorService.sln Normal file
View File

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30011.22
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RSecurityBackend", "RSecurityBackend\RSecurityBackend.csproj", "{1BF45DCD-40F4-4D6E-9134-D6B3DE39C56E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RMuseum", "RMuseum\RMuseum.csproj", "{113EF3DE-E8A9-40EF-9DA0-8DFABB3CDD68}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1BF45DCD-40F4-4D6E-9134-D6B3DE39C56E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1BF45DCD-40F4-4D6E-9134-D6B3DE39C56E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1BF45DCD-40F4-4D6E-9134-D6B3DE39C56E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1BF45DCD-40F4-4D6E-9134-D6B3DE39C56E}.Release|Any CPU.Build.0 = Release|Any CPU
{113EF3DE-E8A9-40EF-9DA0-8DFABB3CDD68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{113EF3DE-E8A9-40EF-9DA0-8DFABB3CDD68}.Debug|Any CPU.Build.0 = Debug|Any CPU
{113EF3DE-E8A9-40EF-9DA0-8DFABB3CDD68}.Release|Any CPU.ActiveCfg = Release|Any CPU
{113EF3DE-E8A9-40EF-9DA0-8DFABB3CDD68}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {CC36BB5B-D678-42F3-9EF2-149A535A8191}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,189 @@
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using RSecurityBackend.Services;
using RSecurityBackend.Controllers;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.Extensions.Configuration;
using Audit.WebApi;
namespace RMuseum.Controllers
{
/// <summary>
/// User login/logout/register/...
/// </summary>
[Produces("application/json")]
[Route("api/users")]
public class AppUserController : AppUserControllerBase
{
/// <summary>
/// constructor
/// </summary>
/// <param name="configuration"></param>
/// <param name="appUserService"></param>
/// <param name="httpContextAccessor"></param>
/// <param name="userPermissionChecker"></param>
/// <param name="emailSender"></param>
/// <param name="imageFileService"></param>
/// <param name="captchaService"></param>
public AppUserController(IConfiguration configuration, IAppUserService appUserService, IHttpContextAccessor httpContextAccessor, IUserPermissionChecker userPermissionChecker, IEmailSender emailSender, IImageFileService imageFileService, ICaptchaService captchaService)
: base(configuration, appUserService, httpContextAccessor, userPermissionChecker, emailSender, imageFileService, captchaService)
{
}
/// <summary>
/// Sign Up Email Subject
/// </summary>
/// <returns>
/// subject
/// </returns>
/// <param name="secretCode"></param>
protected override string GetSignUpEmailSubject(string secretCode)
{
return $"{secretCode} کد ثبت نام شما در گنجینهٔ گنجور";
}
/// <summary>
/// Sign Up Email Html Content
/// </summary>
/// <param name="secretCode"></param>
/// <returns>html content</returns>
protected override string GetSignUpEmailHtmlContent(string secretCode)
{
string content =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
+
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"fa\">"
+
"<head>"
+
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />"
+
$"<title>کد ثبت نام شما در گنجینهٔ گنجور: {secretCode}</title>"
+
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"/>"
+
"</head>"
+
"<body style=\"font:normal 12px tahoma;direction:rtl\">"
+
"<table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"600\">"
+
"<tr>"
+
"<td align=\"center\" style=\"padding: 40px 0 30px 0;\">"
+
"<img src=\"https://i.ganjoor.net/gm.gif\" alt=\"گنجینهٔ گنجور\" width=\"150\" height=\"150\" style=\"display: block;\" />"
+
"</td>"
+
"</tr>"
+
"<tr><td>"
+
$"<p style=\"font:normal 12px tahoma;direction:rtl\">لطفاً جهت تکمیل ثبت نام در گنجینهٔ گنجور <a href=\"{SignupCallbackUrl}?secret={secretCode}\">اینجا</a> کلیک کنید یا اگر صفحهٔ ثبت نام هنوز باز است کد <strong>{secretCode}</strong> را در آن وارد کنید.</p>"
+
"</td></tr>"
+
"<tr><td>"
+
"<p style=\"font:normal 12px tahoma;direction:rtl\">اگر در گنجینهٔ گنجور ثبت نام نکرده‌اید لطفاً این نامه را نادیده بگیرید. به این نامه پاسخ ندهید، کسی پاسخگوی شما نخواهد بود.</p>"
+
"</td></tr>"
+
"</table>"
+
"</body>"
+
"</html>"
;
return content;
}
/// <summary>
/// Forgot Password Email Subject
/// </summary>
/// <returns>
/// subject
/// </returns>
/// <param name="secretCode"></param>
protected override string GetForgotPasswordEmailSubject(string secretCode)
{
return $"{secretCode} کد بازیابی کلمهٔ عبور شما در گنجینهٔ گنجور";
}
/// <summary>
/// Forgot Password Email Html Content
/// </summary>
/// <param name="secretCode"></param>
/// <returns>html content</returns>
protected override string GetForgotPasswordEmailHtmlContent(string secretCode)
{
string content =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
+
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"fa\">"
+
"<head>"
+
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />"
+
$"<title>کد تغییر گذرواژهٔ شما در گنجینهٔ گنجور: {secretCode}</title>"
+
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"/>"
+
"</head>"
+
"<body style=\"font:normal 12px tahoma;direction:rtl\">"
+
"<table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"600\">"
+
"<tr>"
+
"<td align=\"center\" style=\"padding: 40px 0 30px 0;\">"
+
"<img src=\"https://i.ganjoor.net/gm.gif\" alt=\"گنجینهٔ گنجور\" width=\"150\" height=\"150\" style=\"display: block;\" />"
+
"</td>"
+
"</tr>"
+
"<tr><td>"
+
$"<p style=\"font:normal 12px tahoma;direction:rtl\">لطفاً جهت تکمیل فرایند فراموشی گذرواژه در گنجینهٔ گنجور <a href=\"{ForgotPasswordCallbackUrl}?secret={secretCode}\">اینجا</a> کلیک کنید یا اگر صفحهٔ فراموشی گذرواژه هنوز باز است کد <strong>{secretCode}</strong> را در آن وارد کنید.</p>"
+
"</td></tr>"
+
"<tr><td>"
+
"<p style=\"font:normal 12px tahoma;direction:rtl\">اگر در گنجینهٔ گنجور فراموشی گذرواژه را نزده‌اید یا گذرواژه‌تان را به خاطر آوردید لطفاً این نامه را نادیده بگیرید. به این نامه پاسخ ندهید، کسی پاسخگوی شما نخواهد بود.</p>"
+
"</td></tr>"
+
"</table>"
+
"</body>"
+
"</html>"
;
return content;
}
/// <summary>
/// Is Sign-up enabled?
/// </summary>
/// <returns></returns>
protected override bool IsSignupEnabled()
{
return true;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Mvc;
using RSecurityBackend.Controllers;
using RSecurityBackend.Services;
namespace RMuseum.Controllers
{
/// <summary>
/// Audit Log Controller Base
/// </summary>
[Produces("application/json")]
[Route("api/auditlogs")]
public class AuditLogController : AuditLogControllerBase
{
/// <summary>
/// constructor
/// </summary>
/// <param name="auditLogService">
/// </param>
public AuditLogController(IAuditLogService auditLogService)
: base(auditLogService)
{
}
}
}

View File

@ -0,0 +1,121 @@
using Audit.WebApi;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using RMuseum.Models.Artifact;
using RMuseum.Models.Auth.Memory;
using RMuseum.Services;
using RSecurityBackend.Models.Auth.Memory;
using RSecurityBackend.Models.Generic;
using RSecurityBackend.Models.Image;
using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;
namespace RMuseum.Controllers
{
[Produces("application/json")]
[Route("api/images")]
public class ImageController : Controller
{
/// <summary>
/// returns image stream
/// </summary>
/// <param name="id"></param>
/// <param name="size"></param>
/// <returns></returns>
[HttpGet("{size}/{id}.jpg")]
[AllowAnonymous]
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(FileStreamResult))]
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
public async Task<IActionResult> Get(Guid id, string size)
{
RServiceResult<RPictureFile> img =
await _pictureFileService.GetImage(id);
if (!string.IsNullOrEmpty(img.ExceptionString))
return BadRequest(img.ExceptionString);
if (img.Result == null)
return NotFound();
Response.GetTypedHeaders().LastModified = img.Result.LastModified;
var requestHeaders = Request.GetTypedHeaders();
if (requestHeaders.IfModifiedSince.HasValue &&
requestHeaders.IfModifiedSince.Value >= img.Result.LastModified)
{
return StatusCode(StatusCodes.Status304NotModified);
}
RServiceResult<string> imgPath = _pictureFileService.GetImagePath(img.Result, size);
if(!string.IsNullOrEmpty(imgPath.ExceptionString))
return BadRequest(imgPath.ExceptionString);
return new FileStreamResult(new FileStream(imgPath.Result, FileMode.Open, FileAccess.Read), "image/jpeg");
}
/// <summary>
/// Rotate Image in 90 deg. multiplicants: 90, 180 or 270
/// </summary>
/// <param name="id"></param>
/// <param name="degIn90mul"></param>
/// <returns></returns>
[HttpPut("{id}/{degIn90mul}")]
[Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + SecurableItem.ModifyOperationShortName)]
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RPictureFile))]
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
public async Task<IActionResult> Rotate(Guid id, int degIn90mul)
{
RServiceResult<RPictureFile> res = await _pictureFileService.RotateImage(id, degIn90mul);
if (!string.IsNullOrEmpty(res.ExceptionString))
return BadRequest(res.ExceptionString);
return Ok(res.Result);
}
/// <summary>
/// Generate Cropped Image Based On ThumbnailCoordinates For Notes
/// </summary>
/// <param name="id"></param>
/// <param name="left"></param>
/// <param name="top"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns>id of cropped image</returns>
[HttpGet("cropped/{id}")]
[Authorize]
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RImage))]
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
public async Task<IActionResult> GenerateCroppedImageBasedOnThumbnailCoordinates(Guid id, decimal left, decimal top, decimal width, decimal height)
{
RServiceResult<RImage> cropped = await _pictureFileService.GenerateCroppedImageBasedOnThumbnailCoordinates(id, left, top, width, height);
if (!string.IsNullOrEmpty(cropped.ExceptionString))
return BadRequest(cropped.ExceptionString);
return Ok(cropped.Result);
}
/// <summary>
/// constructor
/// </summary>
/// <param name="pictureFileService">
/// </param>
public ImageController(IPictureFileService pictureFileService)
{
_pictureFileService = pictureFileService;
}
/// <summary>
/// Artifact Service
/// </summary>
protected readonly IPictureFileService _pictureFileService;
}
}

View File

@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using RSecurityBackend.Services;
using RSecurityBackend.Controllers;
using Audit.WebApi;
namespace RMuseum.Controllers
{
/// <summary>
/// Generic Image Provider
/// </summary>
[Produces("application/json")]
[Route("api/rimages")]
public class RImageController : RImageControllerBase
{
/// <summary>
/// constructor
/// </summary>
/// <param name="pictureFileService">
/// </param>
public RImageController(IImageFileService pictureFileService) : base(pictureFileService)
{
}
}
}

View File

@ -0,0 +1,25 @@
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using RSecurityBackend.Services;
using RSecurityBackend.Controllers;
using Audit.WebApi;
namespace RMuseum.Controllers
{
/// <summary>
///roles
/// </summary>
[Produces("application/json")]
[Route("api/roles")]
public class RoleController : RoleControllerBase
{
/// <summary>
/// constructor
/// </summary>
/// <param name="roleService"></param>
public RoleController(IUserRoleService roleService)
: base(roleService)
{
}
}
}

View File

@ -0,0 +1,130 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using RMuseum.Models.Artifact;
using RMuseum.Models.Bookmark;
using RMuseum.Models.GanjoorIntegration;
using RMuseum.Models.ImportJob;
using RMuseum.Models.Note;
using RMuseum.Models.Notification;
using RSecurityBackend.DbContext;
using RSecurityBackend.Models.Auth.Db;
using System;
namespace RMuseum.DbContext
{
/// <summary>
/// Museum Database Context
/// </summary>
public class RMuseumDbContext : RSecurityDbContext<RAppUser, RAppRole, Guid>
{
/// <summary>
/// constructor
/// </summary>
public RMuseumDbContext(IConfiguration configuration)
{
Configuration = configuration;
Database.Migrate();
}
/// <summary>
///
/// </summary>
public IConfiguration Configuration { get; }
/// <summary>
///
/// </summary>
/// <param name="optionsBuilder"></param>
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
base.OnConfiguring(optionsBuilder);
}
/// <summary>
///
/// </summary>
/// <param name="builder"></param>
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<RArtifactMasterRecord>()
.HasIndex(m => m.FriendlyUrl)
.IsUnique();
builder.Entity<RArtifactItemRecord>()
.HasIndex(i => new { i.RArtifactMasterRecordId, i.FriendlyUrl })
.IsUnique();
builder.Entity<RArtifactItemRecord>()
.HasIndex(i => new { i.RArtifactMasterRecordId, i.Order })
.IsUnique();
builder.Entity<RTag>()
.HasIndex(t => t.FriendlyUrl);
builder.Entity<RTagValue>()
.HasIndex(t => t.FriendlyUrl);
}
/// <summary>
/// Picture Files
/// </summary>
public DbSet<RPictureFile> PictureFiles { get; set; }
/// <summary>
/// Item Attributes
/// </summary>
public DbSet<RTag> Tags { get; set; }
/// <summary>
/// Artifacts
/// </summary>
public DbSet<RArtifactMasterRecord> Artifacts { get; set; }
/// <summary>
/// Items
/// </summary>
public DbSet<RArtifactItemRecord> Items { get; set; }
/// <summary>
/// Import Jobs
/// </summary>
public DbSet<ImportJob> ImportJobs { get; set; }
/// <summary>
/// Tags
/// </summary>
public DbSet<RTagValue> TagValues { get; set; }
/// <summary>
/// User Bookmarks
/// </summary>
public DbSet<RUserBookmark> UserBookmarks { get; set; }
/// <summary>
/// User Notes
/// </summary>
public DbSet<RUserNote> UserNotes { get; set; }
/// <summary>
/// Ganjoor Links
/// </summary>
public DbSet<GanjoorLink> GanjoorLinks { get; set; }
/// <summary>
/// Notifications
/// </summary>
public DbSet<RUserNotification> Notifications { get; set; }
/// <summary>
/// Pinterest Links
/// </summary>
public DbSet<PinterestLink> PinterestLinks { get; set; }
}
}

View File

@ -0,0 +1,660 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using RMuseum.DbContext;
namespace RMuseum.Migrations
{
[DbContext(typeof(RMuseumDbContext))]
[Migration("20190827145117_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.4-servicing-10062")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<Guid>("RoleId");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<Guid>("UserId");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.Property<string>("LoginProvider");
b.Property<string>("ProviderKey");
b.Property<string>("ProviderDisplayName");
b.Property<Guid>("UserId");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.Property<Guid>("UserId");
b.Property<Guid>("RoleId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.Property<Guid>("UserId");
b.Property<string>("LoginProvider");
b.Property<string>("Name");
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RArtifactItemRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("CoverImageIndex");
b.Property<string>("Description");
b.Property<string>("DescriptionInEnglish");
b.Property<string>("FriendlyUrl");
b.Property<DateTime>("LastModified");
b.Property<string>("Name");
b.Property<string>("NameInEnglish");
b.Property<int>("Order");
b.Property<Guid>("RArtifactMasterRecordId");
b.HasKey("Id");
b.HasIndex("RArtifactMasterRecordId", "FriendlyUrl")
.IsUnique()
.HasFilter("[FriendlyUrl] IS NOT NULL");
b.HasIndex("RArtifactMasterRecordId", "Order")
.IsUnique();
b.ToTable("Items");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RArtifactMasterRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<Guid>("CoverImageId");
b.Property<int>("CoverItemIndex");
b.Property<DateTime>("DateTime");
b.Property<string>("Description");
b.Property<string>("DescriptionInEnglish");
b.Property<string>("FriendlyUrl");
b.Property<int>("ItemCount");
b.Property<DateTime>("LastModified");
b.Property<string>("Name");
b.Property<string>("NameInEnglish");
b.Property<int>("Status");
b.HasKey("Id");
b.HasIndex("CoverImageId");
b.HasIndex("FriendlyUrl")
.IsUnique()
.HasFilter("[FriendlyUrl] IS NOT NULL");
b.ToTable("Artifacts");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RTag", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("FriendlyUrl");
b.Property<bool>("GlobalValue");
b.Property<string>("Name");
b.Property<string>("NameInEnglish");
b.Property<int>("Order");
b.Property<string>("PluralName");
b.Property<string>("PluralNameInEnglish");
b.Property<int>("Status");
b.Property<int>("TagType");
b.HasKey("Id");
b.HasIndex("FriendlyUrl");
b.ToTable("Tags");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RTagValue", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("FriendlyUrl");
b.Property<int>("Order");
b.Property<Guid?>("RArtifactItemRecordId");
b.Property<Guid?>("RArtifactMasterRecordId");
b.Property<Guid>("RTagId");
b.Property<int>("Status");
b.Property<string>("Value");
b.Property<string>("ValueInEnglish");
b.Property<string>("ValueSupplement");
b.HasKey("Id");
b.HasIndex("FriendlyUrl");
b.HasIndex("RArtifactItemRecordId");
b.HasIndex("RArtifactMasterRecordId");
b.HasIndex("RTagId");
b.ToTable("TagValues");
});
modelBuilder.Entity("RMuseum.Models.ImportJob.ImportJob", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<Guid?>("ArtifactId");
b.Property<DateTime>("EndTime");
b.Property<string>("Exception");
b.Property<string>("FriendlyUrl");
b.Property<int>("JobType");
b.Property<decimal>("ProgressPercent");
b.Property<DateTime>("QueueTime");
b.Property<string>("ResourceNumber");
b.Property<string>("SrcContent");
b.Property<string>("SrcUrl");
b.Property<DateTime>("StartTime");
b.Property<int>("Status");
b.HasKey("Id");
b.HasIndex("ArtifactId");
b.ToTable("ImportJobs");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RAppRole", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Description");
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RAppUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<DateTime>("CreateDate");
b.Property<string>("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed");
b.Property<string>("FirstName");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<Guid?>("RImageId");
b.Property<string>("SecurityStamp");
b.Property<int>("Status");
b.Property<string>("SureName");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.HasIndex("RImageId");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RCaptchaImage", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("DateTime");
b.Property<Guid>("RImageId");
b.Property<string>("Value");
b.HasKey("Id");
b.HasIndex("RImageId");
b.ToTable("CaptchaImages");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RPermission", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("OperationShortName");
b.Property<Guid?>("RAppRoleId");
b.Property<string>("SecurableItemShortName");
b.HasKey("Id");
b.HasIndex("RAppRoleId");
b.ToTable("Permissions");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RTemporaryUserSession", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClientAppName");
b.Property<string>("ClientIPAddress");
b.Property<string>("Language");
b.Property<DateTime>("LastRenewal");
b.Property<DateTime>("LoginTime");
b.Property<Guid>("RAppUserId");
b.Property<string>("Token");
b.Property<DateTime>("ValidUntil");
b.HasKey("Id");
b.HasIndex("RAppUserId");
b.ToTable("Sessions");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RVerifyQueueItem", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClientAppName");
b.Property<string>("ClientIPAddress");
b.Property<DateTime>("DateTime");
b.Property<string>("Email");
b.Property<string>("Language");
b.Property<string>("PhoneNumber");
b.Property<int>("QueueType");
b.Property<string>("Secret");
b.HasKey("Id");
b.HasIndex("Secret")
.IsUnique()
.HasFilter("[Secret] IS NOT NULL");
b.ToTable("VerifyQueueItems");
});
modelBuilder.Entity("RSecurityBackend.Models.Image.RImage", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ContentType");
b.Property<DateTime>("DataTime");
b.Property<string>("Discriminator")
.IsRequired();
b.Property<long>("FileSizeInBytes");
b.Property<string>("FolderName");
b.Property<int>("ImageHeight");
b.Property<int>("ImageWidth");
b.Property<DateTime>("LastModified");
b.Property<string>("OriginalFileName");
b.Property<string>("StoredFileName");
b.HasKey("Id");
b.ToTable("GeneralImages");
b.HasDiscriminator<string>("Discriminator").HasValue("RImage");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RPictureFile", b =>
{
b.HasBaseType("RSecurityBackend.Models.Image.RImage");
b.Property<string>("Description");
b.Property<string>("DescriptionInEnglish");
b.Property<DateTime>("LastModifiedMeta");
b.Property<int>("NormalSizeImageHeight");
b.Property<string>("NormalSizeImageStoredFileName");
b.Property<int>("NormalSizeImageWidth");
b.Property<int>("Order");
b.Property<Guid?>("RArtifactItemRecordId");
b.Property<string>("SrcUrl");
b.Property<int>("Status");
b.Property<int>("ThumbnailImageHeight");
b.Property<string>("ThumbnailImageStoredFileName");
b.Property<int>("ThumbnailImageWidth");
b.Property<string>("Title");
b.Property<string>("TitleInEnglish");
b.HasIndex("RArtifactItemRecordId");
b.HasDiscriminator().HasValue("RPictureFile");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Artifact.RArtifactItemRecord", b =>
{
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord")
.WithMany("Items")
.HasForeignKey("RArtifactMasterRecordId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Artifact.RArtifactMasterRecord", b =>
{
b.HasOne("RMuseum.Models.Artifact.RPictureFile", "CoverImage")
.WithMany()
.HasForeignKey("CoverImageId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Artifact.RTagValue", b =>
{
b.HasOne("RMuseum.Models.Artifact.RArtifactItemRecord")
.WithMany("Tags")
.HasForeignKey("RArtifactItemRecordId");
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord")
.WithMany("Tags")
.HasForeignKey("RArtifactMasterRecordId");
b.HasOne("RMuseum.Models.Artifact.RTag", "RTag")
.WithMany()
.HasForeignKey("RTagId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.ImportJob.ImportJob", b =>
{
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord", "Artifact")
.WithMany()
.HasForeignKey("ArtifactId");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RAppUser", b =>
{
b.HasOne("RSecurityBackend.Models.Image.RImage", "RImage")
.WithMany()
.HasForeignKey("RImageId");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RCaptchaImage", b =>
{
b.HasOne("RSecurityBackend.Models.Image.RImage", "RImage")
.WithMany()
.HasForeignKey("RImageId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RPermission", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppRole")
.WithMany("Permissions")
.HasForeignKey("RAppRoleId");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RTemporaryUserSession", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser", "RAppUser")
.WithMany()
.HasForeignKey("RAppUserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Artifact.RPictureFile", b =>
{
b.HasOne("RMuseum.Models.Artifact.RArtifactItemRecord")
.WithMany("Images")
.HasForeignKey("RArtifactItemRecordId");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,624 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
namespace RMuseum.Migrations
{
public partial class Initial : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AspNetRoles",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
Name = table.Column<string>(maxLength: 256, nullable: true),
NormalizedName = table.Column<string>(maxLength: 256, nullable: true),
ConcurrencyStamp = table.Column<string>(nullable: true),
Description = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Tags",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
Order = table.Column<int>(nullable: false),
TagType = table.Column<int>(nullable: false),
FriendlyUrl = table.Column<string>(nullable: true),
Status = table.Column<int>(nullable: false),
Name = table.Column<string>(nullable: true),
NameInEnglish = table.Column<string>(nullable: true),
PluralName = table.Column<string>(nullable: true),
PluralNameInEnglish = table.Column<string>(nullable: true),
GlobalValue = table.Column<bool>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Tags", x => x.Id);
});
migrationBuilder.CreateTable(
name: "VerifyQueueItems",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
QueueType = table.Column<int>(nullable: false),
Email = table.Column<string>(nullable: true),
PhoneNumber = table.Column<string>(nullable: true),
DateTime = table.Column<DateTime>(nullable: false),
Secret = table.Column<string>(nullable: true),
ClientIPAddress = table.Column<string>(nullable: true),
ClientAppName = table.Column<string>(nullable: true),
Language = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_VerifyQueueItems", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetRoleClaims",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
RoleId = table.Column<Guid>(nullable: false),
ClaimType = table.Column<string>(nullable: true),
ClaimValue = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Permissions",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
SecurableItemShortName = table.Column<string>(nullable: true),
OperationShortName = table.Column<string>(nullable: true),
RAppRoleId = table.Column<Guid>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Permissions", x => x.Id);
table.ForeignKey(
name: "FK_Permissions_AspNetRoles_RAppRoleId",
column: x => x.RAppRoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "ImportJobs",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
JobType = table.Column<int>(nullable: false),
ResourceNumber = table.Column<string>(nullable: true),
FriendlyUrl = table.Column<string>(nullable: true),
SrcUrl = table.Column<string>(nullable: true),
QueueTime = table.Column<DateTime>(nullable: false),
StartTime = table.Column<DateTime>(nullable: false),
EndTime = table.Column<DateTime>(nullable: false),
Status = table.Column<int>(nullable: false),
ProgressPercent = table.Column<decimal>(nullable: false),
Exception = table.Column<string>(nullable: true),
ArtifactId = table.Column<Guid>(nullable: true),
SrcContent = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ImportJobs", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Items",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
RArtifactMasterRecordId = table.Column<Guid>(nullable: false),
Order = table.Column<int>(nullable: false),
FriendlyUrl = table.Column<string>(nullable: true),
Name = table.Column<string>(nullable: true),
NameInEnglish = table.Column<string>(nullable: true),
Description = table.Column<string>(nullable: true),
DescriptionInEnglish = table.Column<string>(nullable: true),
CoverImageIndex = table.Column<int>(nullable: false),
LastModified = table.Column<DateTime>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Items", x => x.Id);
});
migrationBuilder.CreateTable(
name: "GeneralImages",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
OriginalFileName = table.Column<string>(nullable: true),
ContentType = table.Column<string>(nullable: true),
FileSizeInBytes = table.Column<long>(nullable: false),
ImageWidth = table.Column<int>(nullable: false),
ImageHeight = table.Column<int>(nullable: false),
FolderName = table.Column<string>(nullable: true),
StoredFileName = table.Column<string>(nullable: true),
DataTime = table.Column<DateTime>(nullable: false),
LastModified = table.Column<DateTime>(nullable: false),
Discriminator = table.Column<string>(nullable: false),
Title = table.Column<string>(nullable: true),
TitleInEnglish = table.Column<string>(nullable: true),
Description = table.Column<string>(nullable: true),
DescriptionInEnglish = table.Column<string>(nullable: true),
Status = table.Column<int>(nullable: true),
Order = table.Column<int>(nullable: true),
NormalSizeImageStoredFileName = table.Column<string>(nullable: true),
ThumbnailImageStoredFileName = table.Column<string>(nullable: true),
NormalSizeImageWidth = table.Column<int>(nullable: true),
NormalSizeImageHeight = table.Column<int>(nullable: true),
ThumbnailImageWidth = table.Column<int>(nullable: true),
ThumbnailImageHeight = table.Column<int>(nullable: true),
SrcUrl = table.Column<string>(nullable: true),
LastModifiedMeta = table.Column<DateTime>(nullable: true),
RArtifactItemRecordId = table.Column<Guid>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_GeneralImages", x => x.Id);
table.ForeignKey(
name: "FK_GeneralImages_Items_RArtifactItemRecordId",
column: x => x.RArtifactItemRecordId,
principalTable: "Items",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "Artifacts",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
FriendlyUrl = table.Column<string>(nullable: true),
Status = table.Column<int>(nullable: false),
Name = table.Column<string>(nullable: true),
NameInEnglish = table.Column<string>(nullable: true),
Description = table.Column<string>(nullable: true),
DescriptionInEnglish = table.Column<string>(nullable: true),
DateTime = table.Column<DateTime>(nullable: false),
LastModified = table.Column<DateTime>(nullable: false),
CoverItemIndex = table.Column<int>(nullable: false),
CoverImageId = table.Column<Guid>(nullable: false),
ItemCount = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Artifacts", x => x.Id);
table.ForeignKey(
name: "FK_Artifacts_GeneralImages_CoverImageId",
column: x => x.CoverImageId,
principalTable: "GeneralImages",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUsers",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
UserName = table.Column<string>(maxLength: 256, nullable: true),
NormalizedUserName = table.Column<string>(maxLength: 256, nullable: true),
Email = table.Column<string>(maxLength: 256, nullable: true),
NormalizedEmail = table.Column<string>(maxLength: 256, nullable: true),
EmailConfirmed = table.Column<bool>(nullable: false),
PasswordHash = table.Column<string>(nullable: true),
SecurityStamp = table.Column<string>(nullable: true),
ConcurrencyStamp = table.Column<string>(nullable: true),
PhoneNumber = table.Column<string>(nullable: true),
PhoneNumberConfirmed = table.Column<bool>(nullable: false),
TwoFactorEnabled = table.Column<bool>(nullable: false),
LockoutEnd = table.Column<DateTimeOffset>(nullable: true),
LockoutEnabled = table.Column<bool>(nullable: false),
AccessFailedCount = table.Column<int>(nullable: false),
FirstName = table.Column<string>(nullable: true),
SureName = table.Column<string>(nullable: true),
CreateDate = table.Column<DateTime>(nullable: false),
Status = table.Column<int>(nullable: false),
RImageId = table.Column<Guid>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
table.ForeignKey(
name: "FK_AspNetUsers_GeneralImages_RImageId",
column: x => x.RImageId,
principalTable: "GeneralImages",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "CaptchaImages",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
RImageId = table.Column<Guid>(nullable: false),
Value = table.Column<string>(nullable: true),
DateTime = table.Column<DateTime>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_CaptchaImages", x => x.Id);
table.ForeignKey(
name: "FK_CaptchaImages_GeneralImages_RImageId",
column: x => x.RImageId,
principalTable: "GeneralImages",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "TagValues",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
RTagId = table.Column<Guid>(nullable: false),
Order = table.Column<int>(nullable: false),
FriendlyUrl = table.Column<string>(nullable: true),
Status = table.Column<int>(nullable: false),
Value = table.Column<string>(nullable: true),
ValueInEnglish = table.Column<string>(nullable: true),
ValueSupplement = table.Column<string>(nullable: true),
RArtifactItemRecordId = table.Column<Guid>(nullable: true),
RArtifactMasterRecordId = table.Column<Guid>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_TagValues", x => x.Id);
table.ForeignKey(
name: "FK_TagValues_Items_RArtifactItemRecordId",
column: x => x.RArtifactItemRecordId,
principalTable: "Items",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_TagValues_Artifacts_RArtifactMasterRecordId",
column: x => x.RArtifactMasterRecordId,
principalTable: "Artifacts",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_TagValues_Tags_RTagId",
column: x => x.RTagId,
principalTable: "Tags",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserClaims",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
UserId = table.Column<Guid>(nullable: false),
ClaimType = table.Column<string>(nullable: true),
ClaimValue = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserLogins",
columns: table => new
{
LoginProvider = table.Column<string>(nullable: false),
ProviderKey = table.Column<string>(nullable: false),
ProviderDisplayName = table.Column<string>(nullable: true),
UserId = table.Column<Guid>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
table.ForeignKey(
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserRoles",
columns: table => new
{
UserId = table.Column<Guid>(nullable: false),
RoleId = table.Column<Guid>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserTokens",
columns: table => new
{
UserId = table.Column<Guid>(nullable: false),
LoginProvider = table.Column<string>(nullable: false),
Name = table.Column<string>(nullable: false),
Value = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
table.ForeignKey(
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Sessions",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
RAppUserId = table.Column<Guid>(nullable: false),
ClientIPAddress = table.Column<string>(nullable: true),
ClientAppName = table.Column<string>(nullable: true),
Language = table.Column<string>(nullable: true),
LoginTime = table.Column<DateTime>(nullable: false),
LastRenewal = table.Column<DateTime>(nullable: false),
ValidUntil = table.Column<DateTime>(nullable: false),
Token = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Sessions", x => x.Id);
table.ForeignKey(
name: "FK_Sessions_AspNetUsers_RAppUserId",
column: x => x.RAppUserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Artifacts_CoverImageId",
table: "Artifacts",
column: "CoverImageId");
migrationBuilder.CreateIndex(
name: "IX_Artifacts_FriendlyUrl",
table: "Artifacts",
column: "FriendlyUrl",
unique: true,
filter: "[FriendlyUrl] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_AspNetRoleClaims_RoleId",
table: "AspNetRoleClaims",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "RoleNameIndex",
table: "AspNetRoles",
column: "NormalizedName",
unique: true,
filter: "[NormalizedName] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserClaims_UserId",
table: "AspNetUserClaims",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserLogins_UserId",
table: "AspNetUserLogins",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserRoles_RoleId",
table: "AspNetUserRoles",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "EmailIndex",
table: "AspNetUsers",
column: "NormalizedEmail");
migrationBuilder.CreateIndex(
name: "UserNameIndex",
table: "AspNetUsers",
column: "NormalizedUserName",
unique: true,
filter: "[NormalizedUserName] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_AspNetUsers_RImageId",
table: "AspNetUsers",
column: "RImageId");
migrationBuilder.CreateIndex(
name: "IX_CaptchaImages_RImageId",
table: "CaptchaImages",
column: "RImageId");
migrationBuilder.CreateIndex(
name: "IX_GeneralImages_RArtifactItemRecordId",
table: "GeneralImages",
column: "RArtifactItemRecordId");
migrationBuilder.CreateIndex(
name: "IX_ImportJobs_ArtifactId",
table: "ImportJobs",
column: "ArtifactId");
migrationBuilder.CreateIndex(
name: "IX_Items_RArtifactMasterRecordId_FriendlyUrl",
table: "Items",
columns: new[] { "RArtifactMasterRecordId", "FriendlyUrl" },
unique: true,
filter: "[FriendlyUrl] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_Items_RArtifactMasterRecordId_Order",
table: "Items",
columns: new[] { "RArtifactMasterRecordId", "Order" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Permissions_RAppRoleId",
table: "Permissions",
column: "RAppRoleId");
migrationBuilder.CreateIndex(
name: "IX_Sessions_RAppUserId",
table: "Sessions",
column: "RAppUserId");
migrationBuilder.CreateIndex(
name: "IX_Tags_FriendlyUrl",
table: "Tags",
column: "FriendlyUrl");
migrationBuilder.CreateIndex(
name: "IX_TagValues_FriendlyUrl",
table: "TagValues",
column: "FriendlyUrl");
migrationBuilder.CreateIndex(
name: "IX_TagValues_RArtifactItemRecordId",
table: "TagValues",
column: "RArtifactItemRecordId");
migrationBuilder.CreateIndex(
name: "IX_TagValues_RArtifactMasterRecordId",
table: "TagValues",
column: "RArtifactMasterRecordId");
migrationBuilder.CreateIndex(
name: "IX_TagValues_RTagId",
table: "TagValues",
column: "RTagId");
migrationBuilder.CreateIndex(
name: "IX_VerifyQueueItems_Secret",
table: "VerifyQueueItems",
column: "Secret",
unique: true,
filter: "[Secret] IS NOT NULL");
migrationBuilder.AddForeignKey(
name: "FK_ImportJobs_Artifacts_ArtifactId",
table: "ImportJobs",
column: "ArtifactId",
principalTable: "Artifacts",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "FK_Items_Artifacts_RArtifactMasterRecordId",
table: "Items",
column: "RArtifactMasterRecordId",
principalTable: "Artifacts",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Artifacts_GeneralImages_CoverImageId",
table: "Artifacts");
migrationBuilder.DropTable(
name: "AspNetRoleClaims");
migrationBuilder.DropTable(
name: "AspNetUserClaims");
migrationBuilder.DropTable(
name: "AspNetUserLogins");
migrationBuilder.DropTable(
name: "AspNetUserRoles");
migrationBuilder.DropTable(
name: "AspNetUserTokens");
migrationBuilder.DropTable(
name: "CaptchaImages");
migrationBuilder.DropTable(
name: "ImportJobs");
migrationBuilder.DropTable(
name: "Permissions");
migrationBuilder.DropTable(
name: "Sessions");
migrationBuilder.DropTable(
name: "TagValues");
migrationBuilder.DropTable(
name: "VerifyQueueItems");
migrationBuilder.DropTable(
name: "AspNetRoles");
migrationBuilder.DropTable(
name: "AspNetUsers");
migrationBuilder.DropTable(
name: "Tags");
migrationBuilder.DropTable(
name: "GeneralImages");
migrationBuilder.DropTable(
name: "Items");
migrationBuilder.DropTable(
name: "Artifacts");
}
}
}

View File

@ -0,0 +1,704 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using RMuseum.DbContext;
namespace RMuseum.Migrations
{
[DbContext(typeof(RMuseumDbContext))]
[Migration("20190908153305_Bookmarks")]
partial class Bookmarks
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.4-servicing-10062")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<Guid>("RoleId");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<Guid>("UserId");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.Property<string>("LoginProvider");
b.Property<string>("ProviderKey");
b.Property<string>("ProviderDisplayName");
b.Property<Guid>("UserId");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.Property<Guid>("UserId");
b.Property<Guid>("RoleId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.Property<Guid>("UserId");
b.Property<string>("LoginProvider");
b.Property<string>("Name");
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RArtifactItemRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("CoverImageIndex");
b.Property<string>("Description");
b.Property<string>("DescriptionInEnglish");
b.Property<string>("FriendlyUrl");
b.Property<DateTime>("LastModified");
b.Property<string>("Name");
b.Property<string>("NameInEnglish");
b.Property<int>("Order");
b.Property<Guid>("RArtifactMasterRecordId");
b.HasKey("Id");
b.HasIndex("RArtifactMasterRecordId", "FriendlyUrl")
.IsUnique()
.HasFilter("[FriendlyUrl] IS NOT NULL");
b.HasIndex("RArtifactMasterRecordId", "Order")
.IsUnique();
b.ToTable("Items");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RArtifactMasterRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<Guid>("CoverImageId");
b.Property<int>("CoverItemIndex");
b.Property<DateTime>("DateTime");
b.Property<string>("Description");
b.Property<string>("DescriptionInEnglish");
b.Property<string>("FriendlyUrl");
b.Property<int>("ItemCount");
b.Property<DateTime>("LastModified");
b.Property<string>("Name");
b.Property<string>("NameInEnglish");
b.Property<int>("Status");
b.HasKey("Id");
b.HasIndex("CoverImageId");
b.HasIndex("FriendlyUrl")
.IsUnique()
.HasFilter("[FriendlyUrl] IS NOT NULL");
b.ToTable("Artifacts");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RTag", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("FriendlyUrl");
b.Property<bool>("GlobalValue");
b.Property<string>("Name");
b.Property<string>("NameInEnglish");
b.Property<int>("Order");
b.Property<string>("PluralName");
b.Property<string>("PluralNameInEnglish");
b.Property<int>("Status");
b.Property<int>("TagType");
b.HasKey("Id");
b.HasIndex("FriendlyUrl");
b.ToTable("Tags");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RTagValue", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("FriendlyUrl");
b.Property<int>("Order");
b.Property<Guid?>("RArtifactItemRecordId");
b.Property<Guid?>("RArtifactMasterRecordId");
b.Property<Guid>("RTagId");
b.Property<int>("Status");
b.Property<string>("Value");
b.Property<string>("ValueInEnglish");
b.Property<string>("ValueSupplement");
b.HasKey("Id");
b.HasIndex("FriendlyUrl");
b.HasIndex("RArtifactItemRecordId");
b.HasIndex("RArtifactMasterRecordId");
b.HasIndex("RTagId");
b.ToTable("TagValues");
});
modelBuilder.Entity("RMuseum.Models.Bookmark.RUserBookmark", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("DateTime");
b.Property<string>("Note");
b.Property<Guid>("RAppUserId");
b.Property<Guid?>("RArtifactItemRecordId");
b.Property<Guid?>("RArtifactMasterRecordId");
b.Property<int>("RBookmarkType");
b.HasKey("Id");
b.HasIndex("RAppUserId");
b.HasIndex("RArtifactItemRecordId");
b.HasIndex("RArtifactMasterRecordId");
b.ToTable("UserBookmarks");
});
modelBuilder.Entity("RMuseum.Models.ImportJob.ImportJob", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<Guid?>("ArtifactId");
b.Property<DateTime>("EndTime");
b.Property<string>("Exception");
b.Property<string>("FriendlyUrl");
b.Property<int>("JobType");
b.Property<decimal>("ProgressPercent");
b.Property<DateTime>("QueueTime");
b.Property<string>("ResourceNumber");
b.Property<string>("SrcContent");
b.Property<string>("SrcUrl");
b.Property<DateTime>("StartTime");
b.Property<int>("Status");
b.HasKey("Id");
b.HasIndex("ArtifactId");
b.ToTable("ImportJobs");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RAppRole", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Description");
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RAppUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<DateTime>("CreateDate");
b.Property<string>("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed");
b.Property<string>("FirstName");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<Guid?>("RImageId");
b.Property<string>("SecurityStamp");
b.Property<int>("Status");
b.Property<string>("SureName");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.HasIndex("RImageId");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RCaptchaImage", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("DateTime");
b.Property<Guid>("RImageId");
b.Property<string>("Value");
b.HasKey("Id");
b.HasIndex("RImageId");
b.ToTable("CaptchaImages");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RPermission", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("OperationShortName");
b.Property<Guid?>("RAppRoleId");
b.Property<string>("SecurableItemShortName");
b.HasKey("Id");
b.HasIndex("RAppRoleId");
b.ToTable("Permissions");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RTemporaryUserSession", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClientAppName");
b.Property<string>("ClientIPAddress");
b.Property<string>("Language");
b.Property<DateTime>("LastRenewal");
b.Property<DateTime>("LoginTime");
b.Property<Guid>("RAppUserId");
b.Property<string>("Token");
b.Property<DateTime>("ValidUntil");
b.HasKey("Id");
b.HasIndex("RAppUserId");
b.ToTable("Sessions");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RVerifyQueueItem", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClientAppName");
b.Property<string>("ClientIPAddress");
b.Property<DateTime>("DateTime");
b.Property<string>("Email");
b.Property<string>("Language");
b.Property<string>("PhoneNumber");
b.Property<int>("QueueType");
b.Property<string>("Secret");
b.HasKey("Id");
b.HasIndex("Secret")
.IsUnique()
.HasFilter("[Secret] IS NOT NULL");
b.ToTable("VerifyQueueItems");
});
modelBuilder.Entity("RSecurityBackend.Models.Image.RImage", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ContentType");
b.Property<DateTime>("DataTime");
b.Property<string>("Discriminator")
.IsRequired();
b.Property<long>("FileSizeInBytes");
b.Property<string>("FolderName");
b.Property<int>("ImageHeight");
b.Property<int>("ImageWidth");
b.Property<DateTime>("LastModified");
b.Property<string>("OriginalFileName");
b.Property<string>("StoredFileName");
b.HasKey("Id");
b.ToTable("GeneralImages");
b.HasDiscriminator<string>("Discriminator").HasValue("RImage");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RPictureFile", b =>
{
b.HasBaseType("RSecurityBackend.Models.Image.RImage");
b.Property<string>("Description");
b.Property<string>("DescriptionInEnglish");
b.Property<DateTime>("LastModifiedMeta");
b.Property<int>("NormalSizeImageHeight");
b.Property<string>("NormalSizeImageStoredFileName");
b.Property<int>("NormalSizeImageWidth");
b.Property<int>("Order");
b.Property<Guid?>("RArtifactItemRecordId");
b.Property<string>("SrcUrl");
b.Property<int>("Status");
b.Property<int>("ThumbnailImageHeight");
b.Property<string>("ThumbnailImageStoredFileName");
b.Property<int>("ThumbnailImageWidth");
b.Property<string>("Title");
b.Property<string>("TitleInEnglish");
b.HasIndex("RArtifactItemRecordId");
b.HasDiscriminator().HasValue("RPictureFile");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Artifact.RArtifactItemRecord", b =>
{
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord")
.WithMany("Items")
.HasForeignKey("RArtifactMasterRecordId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Artifact.RArtifactMasterRecord", b =>
{
b.HasOne("RMuseum.Models.Artifact.RPictureFile", "CoverImage")
.WithMany()
.HasForeignKey("CoverImageId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Artifact.RTagValue", b =>
{
b.HasOne("RMuseum.Models.Artifact.RArtifactItemRecord")
.WithMany("Tags")
.HasForeignKey("RArtifactItemRecordId");
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord")
.WithMany("Tags")
.HasForeignKey("RArtifactMasterRecordId");
b.HasOne("RMuseum.Models.Artifact.RTag", "RTag")
.WithMany()
.HasForeignKey("RTagId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Bookmark.RUserBookmark", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser", "RAppUser")
.WithMany()
.HasForeignKey("RAppUserId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("RMuseum.Models.Artifact.RArtifactItemRecord", "RArtifactItemRecord")
.WithMany()
.HasForeignKey("RArtifactItemRecordId");
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord", "RArtifactMasterRecord")
.WithMany()
.HasForeignKey("RArtifactMasterRecordId");
});
modelBuilder.Entity("RMuseum.Models.ImportJob.ImportJob", b =>
{
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord", "Artifact")
.WithMany()
.HasForeignKey("ArtifactId");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RAppUser", b =>
{
b.HasOne("RSecurityBackend.Models.Image.RImage", "RImage")
.WithMany()
.HasForeignKey("RImageId");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RCaptchaImage", b =>
{
b.HasOne("RSecurityBackend.Models.Image.RImage", "RImage")
.WithMany()
.HasForeignKey("RImageId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RPermission", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppRole")
.WithMany("Permissions")
.HasForeignKey("RAppRoleId");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RTemporaryUserSession", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser", "RAppUser")
.WithMany()
.HasForeignKey("RAppUserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Artifact.RPictureFile", b =>
{
b.HasOne("RMuseum.Models.Artifact.RArtifactItemRecord")
.WithMany("Images")
.HasForeignKey("RArtifactItemRecordId");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,67 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace RMuseum.Migrations
{
public partial class Bookmarks : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "UserBookmarks",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
RAppUserId = table.Column<Guid>(nullable: false),
RArtifactMasterRecordId = table.Column<Guid>(nullable: true),
RArtifactItemRecordId = table.Column<Guid>(nullable: true),
DateTime = table.Column<DateTime>(nullable: false),
RBookmarkType = table.Column<int>(nullable: false),
Note = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_UserBookmarks", x => x.Id);
table.ForeignKey(
name: "FK_UserBookmarks_AspNetUsers_RAppUserId",
column: x => x.RAppUserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_UserBookmarks_Items_RArtifactItemRecordId",
column: x => x.RArtifactItemRecordId,
principalTable: "Items",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_UserBookmarks_Artifacts_RArtifactMasterRecordId",
column: x => x.RArtifactMasterRecordId,
principalTable: "Artifacts",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_UserBookmarks_RAppUserId",
table: "UserBookmarks",
column: "RAppUserId");
migrationBuilder.CreateIndex(
name: "IX_UserBookmarks_RArtifactItemRecordId",
table: "UserBookmarks",
column: "RArtifactItemRecordId");
migrationBuilder.CreateIndex(
name: "IX_UserBookmarks_RArtifactMasterRecordId",
table: "UserBookmarks",
column: "RArtifactMasterRecordId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "UserBookmarks");
}
}
}

View File

@ -0,0 +1,762 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using RMuseum.DbContext;
namespace RMuseum.Migrations
{
[DbContext(typeof(RMuseumDbContext))]
[Migration("20190909123824_UserNotes")]
partial class UserNotes
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.4-servicing-10062")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<Guid>("RoleId");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<Guid>("UserId");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.Property<string>("LoginProvider");
b.Property<string>("ProviderKey");
b.Property<string>("ProviderDisplayName");
b.Property<Guid>("UserId");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.Property<Guid>("UserId");
b.Property<Guid>("RoleId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.Property<Guid>("UserId");
b.Property<string>("LoginProvider");
b.Property<string>("Name");
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RArtifactItemRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("CoverImageIndex");
b.Property<string>("Description");
b.Property<string>("DescriptionInEnglish");
b.Property<string>("FriendlyUrl");
b.Property<DateTime>("LastModified");
b.Property<string>("Name");
b.Property<string>("NameInEnglish");
b.Property<int>("Order");
b.Property<Guid>("RArtifactMasterRecordId");
b.HasKey("Id");
b.HasIndex("RArtifactMasterRecordId", "FriendlyUrl")
.IsUnique()
.HasFilter("[FriendlyUrl] IS NOT NULL");
b.HasIndex("RArtifactMasterRecordId", "Order")
.IsUnique();
b.ToTable("Items");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RArtifactMasterRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<Guid>("CoverImageId");
b.Property<int>("CoverItemIndex");
b.Property<DateTime>("DateTime");
b.Property<string>("Description");
b.Property<string>("DescriptionInEnglish");
b.Property<string>("FriendlyUrl");
b.Property<int>("ItemCount");
b.Property<DateTime>("LastModified");
b.Property<string>("Name");
b.Property<string>("NameInEnglish");
b.Property<int>("Status");
b.HasKey("Id");
b.HasIndex("CoverImageId");
b.HasIndex("FriendlyUrl")
.IsUnique()
.HasFilter("[FriendlyUrl] IS NOT NULL");
b.ToTable("Artifacts");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RTag", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("FriendlyUrl");
b.Property<bool>("GlobalValue");
b.Property<string>("Name");
b.Property<string>("NameInEnglish");
b.Property<int>("Order");
b.Property<string>("PluralName");
b.Property<string>("PluralNameInEnglish");
b.Property<int>("Status");
b.Property<int>("TagType");
b.HasKey("Id");
b.HasIndex("FriendlyUrl");
b.ToTable("Tags");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RTagValue", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("FriendlyUrl");
b.Property<int>("Order");
b.Property<Guid?>("RArtifactItemRecordId");
b.Property<Guid?>("RArtifactMasterRecordId");
b.Property<Guid>("RTagId");
b.Property<int>("Status");
b.Property<string>("Value");
b.Property<string>("ValueInEnglish");
b.Property<string>("ValueSupplement");
b.HasKey("Id");
b.HasIndex("FriendlyUrl");
b.HasIndex("RArtifactItemRecordId");
b.HasIndex("RArtifactMasterRecordId");
b.HasIndex("RTagId");
b.ToTable("TagValues");
});
modelBuilder.Entity("RMuseum.Models.Bookmark.RUserBookmark", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("DateTime");
b.Property<string>("Note");
b.Property<Guid>("RAppUserId");
b.Property<Guid?>("RArtifactItemRecordId");
b.Property<Guid?>("RArtifactMasterRecordId");
b.Property<int>("RBookmarkType");
b.HasKey("Id");
b.HasIndex("RAppUserId");
b.HasIndex("RArtifactItemRecordId");
b.HasIndex("RArtifactMasterRecordId");
b.ToTable("UserBookmarks");
});
modelBuilder.Entity("RMuseum.Models.ImportJob.ImportJob", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<Guid?>("ArtifactId");
b.Property<DateTime>("EndTime");
b.Property<string>("Exception");
b.Property<string>("FriendlyUrl");
b.Property<int>("JobType");
b.Property<decimal>("ProgressPercent");
b.Property<DateTime>("QueueTime");
b.Property<string>("ResourceNumber");
b.Property<string>("SrcContent");
b.Property<string>("SrcUrl");
b.Property<DateTime>("StartTime");
b.Property<int>("Status");
b.HasKey("Id");
b.HasIndex("ArtifactId");
b.ToTable("ImportJobs");
});
modelBuilder.Entity("RMuseum.Models.Note.RUserNote", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("DateTime");
b.Property<string>("HtmlContent");
b.Property<DateTime>("LastModified");
b.Property<bool>("Modified");
b.Property<int>("NoteType");
b.Property<Guid>("RAppUserId");
b.Property<Guid?>("RArtifactItemRecordId");
b.Property<Guid?>("RArtifactMasterRecordId");
b.Property<Guid?>("ReferenceNoteId");
b.Property<int>("Status");
b.HasKey("Id");
b.HasIndex("RAppUserId");
b.HasIndex("RArtifactItemRecordId");
b.HasIndex("RArtifactMasterRecordId");
b.HasIndex("ReferenceNoteId");
b.ToTable("UserNotes");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RAppRole", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Description");
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RAppUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<DateTime>("CreateDate");
b.Property<string>("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed");
b.Property<string>("FirstName");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<Guid?>("RImageId");
b.Property<string>("SecurityStamp");
b.Property<int>("Status");
b.Property<string>("SureName");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.HasIndex("RImageId");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RCaptchaImage", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("DateTime");
b.Property<Guid>("RImageId");
b.Property<string>("Value");
b.HasKey("Id");
b.HasIndex("RImageId");
b.ToTable("CaptchaImages");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RPermission", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("OperationShortName");
b.Property<Guid?>("RAppRoleId");
b.Property<string>("SecurableItemShortName");
b.HasKey("Id");
b.HasIndex("RAppRoleId");
b.ToTable("Permissions");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RTemporaryUserSession", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClientAppName");
b.Property<string>("ClientIPAddress");
b.Property<string>("Language");
b.Property<DateTime>("LastRenewal");
b.Property<DateTime>("LoginTime");
b.Property<Guid>("RAppUserId");
b.Property<string>("Token");
b.Property<DateTime>("ValidUntil");
b.HasKey("Id");
b.HasIndex("RAppUserId");
b.ToTable("Sessions");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RVerifyQueueItem", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClientAppName");
b.Property<string>("ClientIPAddress");
b.Property<DateTime>("DateTime");
b.Property<string>("Email");
b.Property<string>("Language");
b.Property<string>("PhoneNumber");
b.Property<int>("QueueType");
b.Property<string>("Secret");
b.HasKey("Id");
b.HasIndex("Secret")
.IsUnique()
.HasFilter("[Secret] IS NOT NULL");
b.ToTable("VerifyQueueItems");
});
modelBuilder.Entity("RSecurityBackend.Models.Image.RImage", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ContentType");
b.Property<DateTime>("DataTime");
b.Property<string>("Discriminator")
.IsRequired();
b.Property<long>("FileSizeInBytes");
b.Property<string>("FolderName");
b.Property<int>("ImageHeight");
b.Property<int>("ImageWidth");
b.Property<DateTime>("LastModified");
b.Property<string>("OriginalFileName");
b.Property<string>("StoredFileName");
b.HasKey("Id");
b.ToTable("GeneralImages");
b.HasDiscriminator<string>("Discriminator").HasValue("RImage");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RPictureFile", b =>
{
b.HasBaseType("RSecurityBackend.Models.Image.RImage");
b.Property<string>("Description");
b.Property<string>("DescriptionInEnglish");
b.Property<DateTime>("LastModifiedMeta");
b.Property<int>("NormalSizeImageHeight");
b.Property<string>("NormalSizeImageStoredFileName");
b.Property<int>("NormalSizeImageWidth");
b.Property<int>("Order");
b.Property<Guid?>("RArtifactItemRecordId");
b.Property<string>("SrcUrl");
b.Property<int>("Status");
b.Property<int>("ThumbnailImageHeight");
b.Property<string>("ThumbnailImageStoredFileName");
b.Property<int>("ThumbnailImageWidth");
b.Property<string>("Title");
b.Property<string>("TitleInEnglish");
b.HasIndex("RArtifactItemRecordId");
b.HasDiscriminator().HasValue("RPictureFile");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Artifact.RArtifactItemRecord", b =>
{
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord")
.WithMany("Items")
.HasForeignKey("RArtifactMasterRecordId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Artifact.RArtifactMasterRecord", b =>
{
b.HasOne("RMuseum.Models.Artifact.RPictureFile", "CoverImage")
.WithMany()
.HasForeignKey("CoverImageId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Artifact.RTagValue", b =>
{
b.HasOne("RMuseum.Models.Artifact.RArtifactItemRecord")
.WithMany("Tags")
.HasForeignKey("RArtifactItemRecordId");
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord")
.WithMany("Tags")
.HasForeignKey("RArtifactMasterRecordId");
b.HasOne("RMuseum.Models.Artifact.RTag", "RTag")
.WithMany()
.HasForeignKey("RTagId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Bookmark.RUserBookmark", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser", "RAppUser")
.WithMany()
.HasForeignKey("RAppUserId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("RMuseum.Models.Artifact.RArtifactItemRecord", "RArtifactItemRecord")
.WithMany()
.HasForeignKey("RArtifactItemRecordId");
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord", "RArtifactMasterRecord")
.WithMany()
.HasForeignKey("RArtifactMasterRecordId");
});
modelBuilder.Entity("RMuseum.Models.ImportJob.ImportJob", b =>
{
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord", "Artifact")
.WithMany()
.HasForeignKey("ArtifactId");
});
modelBuilder.Entity("RMuseum.Models.Note.RUserNote", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser", "RAppUser")
.WithMany()
.HasForeignKey("RAppUserId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("RMuseum.Models.Artifact.RArtifactItemRecord", "RArtifactItemRecord")
.WithMany()
.HasForeignKey("RArtifactItemRecordId");
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord", "RArtifactMasterRecord")
.WithMany()
.HasForeignKey("RArtifactMasterRecordId");
b.HasOne("RMuseum.Models.Note.RUserNote", "ReferenceNote")
.WithMany()
.HasForeignKey("ReferenceNoteId");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RAppUser", b =>
{
b.HasOne("RSecurityBackend.Models.Image.RImage", "RImage")
.WithMany()
.HasForeignKey("RImageId");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RCaptchaImage", b =>
{
b.HasOne("RSecurityBackend.Models.Image.RImage", "RImage")
.WithMany()
.HasForeignKey("RImageId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RPermission", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppRole")
.WithMany("Permissions")
.HasForeignKey("RAppRoleId");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RTemporaryUserSession", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser", "RAppUser")
.WithMany()
.HasForeignKey("RAppUserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Artifact.RPictureFile", b =>
{
b.HasOne("RMuseum.Models.Artifact.RArtifactItemRecord")
.WithMany("Images")
.HasForeignKey("RArtifactItemRecordId");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,82 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace RMuseum.Migrations
{
public partial class UserNotes : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "UserNotes",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
RAppUserId = table.Column<Guid>(nullable: false),
RArtifactMasterRecordId = table.Column<Guid>(nullable: true),
RArtifactItemRecordId = table.Column<Guid>(nullable: true),
DateTime = table.Column<DateTime>(nullable: false),
Modified = table.Column<bool>(nullable: false),
LastModified = table.Column<DateTime>(nullable: false),
NoteType = table.Column<int>(nullable: false),
HtmlContent = table.Column<string>(nullable: true),
ReferenceNoteId = table.Column<Guid>(nullable: true),
Status = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_UserNotes", x => x.Id);
table.ForeignKey(
name: "FK_UserNotes_AspNetUsers_RAppUserId",
column: x => x.RAppUserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_UserNotes_Items_RArtifactItemRecordId",
column: x => x.RArtifactItemRecordId,
principalTable: "Items",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_UserNotes_Artifacts_RArtifactMasterRecordId",
column: x => x.RArtifactMasterRecordId,
principalTable: "Artifacts",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_UserNotes_UserNotes_ReferenceNoteId",
column: x => x.ReferenceNoteId,
principalTable: "UserNotes",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_UserNotes_RAppUserId",
table: "UserNotes",
column: "RAppUserId");
migrationBuilder.CreateIndex(
name: "IX_UserNotes_RArtifactItemRecordId",
table: "UserNotes",
column: "RArtifactItemRecordId");
migrationBuilder.CreateIndex(
name: "IX_UserNotes_RArtifactMasterRecordId",
table: "UserNotes",
column: "RArtifactMasterRecordId");
migrationBuilder.CreateIndex(
name: "IX_UserNotes_ReferenceNoteId",
table: "UserNotes",
column: "ReferenceNoteId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "UserNotes");
}
}
}

View File

@ -0,0 +1,823 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using RMuseum.DbContext;
namespace RMuseum.Migrations
{
[DbContext(typeof(RMuseumDbContext))]
[Migration("20190916070256_GanjoorLink")]
partial class GanjoorLink
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.4-servicing-10062")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<Guid>("RoleId");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<Guid>("UserId");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.Property<string>("LoginProvider");
b.Property<string>("ProviderKey");
b.Property<string>("ProviderDisplayName");
b.Property<Guid>("UserId");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.Property<Guid>("UserId");
b.Property<Guid>("RoleId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.Property<Guid>("UserId");
b.Property<string>("LoginProvider");
b.Property<string>("Name");
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RArtifactItemRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("CoverImageIndex");
b.Property<string>("Description");
b.Property<string>("DescriptionInEnglish");
b.Property<string>("FriendlyUrl");
b.Property<DateTime>("LastModified");
b.Property<string>("Name");
b.Property<string>("NameInEnglish");
b.Property<int>("Order");
b.Property<Guid>("RArtifactMasterRecordId");
b.HasKey("Id");
b.HasIndex("RArtifactMasterRecordId", "FriendlyUrl")
.IsUnique()
.HasFilter("[FriendlyUrl] IS NOT NULL");
b.HasIndex("RArtifactMasterRecordId", "Order")
.IsUnique();
b.ToTable("Items");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RArtifactMasterRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<Guid>("CoverImageId");
b.Property<int>("CoverItemIndex");
b.Property<DateTime>("DateTime");
b.Property<string>("Description");
b.Property<string>("DescriptionInEnglish");
b.Property<string>("FriendlyUrl");
b.Property<int>("ItemCount");
b.Property<DateTime>("LastModified");
b.Property<string>("Name");
b.Property<string>("NameInEnglish");
b.Property<int>("Status");
b.HasKey("Id");
b.HasIndex("CoverImageId");
b.HasIndex("FriendlyUrl")
.IsUnique()
.HasFilter("[FriendlyUrl] IS NOT NULL");
b.ToTable("Artifacts");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RTag", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("FriendlyUrl");
b.Property<bool>("GlobalValue");
b.Property<string>("Name");
b.Property<string>("NameInEnglish");
b.Property<int>("Order");
b.Property<string>("PluralName");
b.Property<string>("PluralNameInEnglish");
b.Property<int>("Status");
b.Property<int>("TagType");
b.HasKey("Id");
b.HasIndex("FriendlyUrl");
b.ToTable("Tags");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RTagValue", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("FriendlyUrl");
b.Property<int>("Order");
b.Property<Guid?>("RArtifactItemRecordId");
b.Property<Guid?>("RArtifactMasterRecordId");
b.Property<Guid>("RTagId");
b.Property<int>("Status");
b.Property<string>("Value");
b.Property<string>("ValueInEnglish");
b.Property<string>("ValueSupplement");
b.HasKey("Id");
b.HasIndex("FriendlyUrl");
b.HasIndex("RArtifactItemRecordId");
b.HasIndex("RArtifactMasterRecordId");
b.HasIndex("RTagId");
b.ToTable("TagValues");
});
modelBuilder.Entity("RMuseum.Models.Bookmark.RUserBookmark", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("DateTime");
b.Property<string>("Note");
b.Property<Guid>("RAppUserId");
b.Property<Guid?>("RArtifactItemRecordId");
b.Property<Guid?>("RArtifactMasterRecordId");
b.Property<int>("RBookmarkType");
b.HasKey("Id");
b.HasIndex("RAppUserId");
b.HasIndex("RArtifactItemRecordId");
b.HasIndex("RArtifactMasterRecordId");
b.ToTable("UserBookmarks");
});
modelBuilder.Entity("RMuseum.Models.GanjoorIntegration.GanjoorLink", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<Guid>("ArtifactId");
b.Property<int>("GanjoorPostId");
b.Property<string>("GanjoorTitle");
b.Property<string>("GanjoorUrl");
b.Property<Guid?>("ItemId");
b.Property<DateTime>("ReviewDate");
b.Property<int>("ReviewResult");
b.Property<Guid?>("ReviewerId");
b.Property<Guid>("SuggestedById");
b.Property<DateTime>("SuggestionDate");
b.Property<bool>("Synchronized");
b.HasKey("Id");
b.HasIndex("ArtifactId");
b.HasIndex("ItemId");
b.HasIndex("ReviewerId");
b.HasIndex("SuggestedById");
b.ToTable("GanjoorLinks");
});
modelBuilder.Entity("RMuseum.Models.ImportJob.ImportJob", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<Guid?>("ArtifactId");
b.Property<DateTime>("EndTime");
b.Property<string>("Exception");
b.Property<string>("FriendlyUrl");
b.Property<int>("JobType");
b.Property<decimal>("ProgressPercent");
b.Property<DateTime>("QueueTime");
b.Property<string>("ResourceNumber");
b.Property<string>("SrcContent");
b.Property<string>("SrcUrl");
b.Property<DateTime>("StartTime");
b.Property<int>("Status");
b.HasKey("Id");
b.HasIndex("ArtifactId");
b.ToTable("ImportJobs");
});
modelBuilder.Entity("RMuseum.Models.Note.RUserNote", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("DateTime");
b.Property<string>("HtmlContent");
b.Property<DateTime>("LastModified");
b.Property<bool>("Modified");
b.Property<int>("NoteType");
b.Property<Guid>("RAppUserId");
b.Property<Guid?>("RArtifactItemRecordId");
b.Property<Guid?>("RArtifactMasterRecordId");
b.Property<Guid?>("ReferenceNoteId");
b.Property<int>("Status");
b.HasKey("Id");
b.HasIndex("RAppUserId");
b.HasIndex("RArtifactItemRecordId");
b.HasIndex("RArtifactMasterRecordId");
b.HasIndex("ReferenceNoteId");
b.ToTable("UserNotes");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RAppRole", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Description");
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RAppUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<DateTime>("CreateDate");
b.Property<string>("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed");
b.Property<string>("FirstName");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<Guid?>("RImageId");
b.Property<string>("SecurityStamp");
b.Property<int>("Status");
b.Property<string>("SureName");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.HasIndex("RImageId");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RCaptchaImage", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("DateTime");
b.Property<Guid>("RImageId");
b.Property<string>("Value");
b.HasKey("Id");
b.HasIndex("RImageId");
b.ToTable("CaptchaImages");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RPermission", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("OperationShortName");
b.Property<Guid?>("RAppRoleId");
b.Property<string>("SecurableItemShortName");
b.HasKey("Id");
b.HasIndex("RAppRoleId");
b.ToTable("Permissions");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RTemporaryUserSession", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClientAppName");
b.Property<string>("ClientIPAddress");
b.Property<string>("Language");
b.Property<DateTime>("LastRenewal");
b.Property<DateTime>("LoginTime");
b.Property<Guid>("RAppUserId");
b.Property<string>("Token");
b.Property<DateTime>("ValidUntil");
b.HasKey("Id");
b.HasIndex("RAppUserId");
b.ToTable("Sessions");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RVerifyQueueItem", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClientAppName");
b.Property<string>("ClientIPAddress");
b.Property<DateTime>("DateTime");
b.Property<string>("Email");
b.Property<string>("Language");
b.Property<string>("PhoneNumber");
b.Property<int>("QueueType");
b.Property<string>("Secret");
b.HasKey("Id");
b.HasIndex("Secret")
.IsUnique()
.HasFilter("[Secret] IS NOT NULL");
b.ToTable("VerifyQueueItems");
});
modelBuilder.Entity("RSecurityBackend.Models.Image.RImage", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ContentType");
b.Property<DateTime>("DataTime");
b.Property<string>("Discriminator")
.IsRequired();
b.Property<long>("FileSizeInBytes");
b.Property<string>("FolderName");
b.Property<int>("ImageHeight");
b.Property<int>("ImageWidth");
b.Property<DateTime>("LastModified");
b.Property<string>("OriginalFileName");
b.Property<string>("StoredFileName");
b.HasKey("Id");
b.ToTable("GeneralImages");
b.HasDiscriminator<string>("Discriminator").HasValue("RImage");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RPictureFile", b =>
{
b.HasBaseType("RSecurityBackend.Models.Image.RImage");
b.Property<string>("Description");
b.Property<string>("DescriptionInEnglish");
b.Property<DateTime>("LastModifiedMeta");
b.Property<int>("NormalSizeImageHeight");
b.Property<string>("NormalSizeImageStoredFileName");
b.Property<int>("NormalSizeImageWidth");
b.Property<int>("Order");
b.Property<Guid?>("RArtifactItemRecordId");
b.Property<string>("SrcUrl");
b.Property<int>("Status");
b.Property<int>("ThumbnailImageHeight");
b.Property<string>("ThumbnailImageStoredFileName");
b.Property<int>("ThumbnailImageWidth");
b.Property<string>("Title");
b.Property<string>("TitleInEnglish");
b.HasIndex("RArtifactItemRecordId");
b.HasDiscriminator().HasValue("RPictureFile");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Artifact.RArtifactItemRecord", b =>
{
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord")
.WithMany("Items")
.HasForeignKey("RArtifactMasterRecordId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Artifact.RArtifactMasterRecord", b =>
{
b.HasOne("RMuseum.Models.Artifact.RPictureFile", "CoverImage")
.WithMany()
.HasForeignKey("CoverImageId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Artifact.RTagValue", b =>
{
b.HasOne("RMuseum.Models.Artifact.RArtifactItemRecord")
.WithMany("Tags")
.HasForeignKey("RArtifactItemRecordId");
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord")
.WithMany("Tags")
.HasForeignKey("RArtifactMasterRecordId");
b.HasOne("RMuseum.Models.Artifact.RTag", "RTag")
.WithMany()
.HasForeignKey("RTagId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Bookmark.RUserBookmark", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser", "RAppUser")
.WithMany()
.HasForeignKey("RAppUserId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("RMuseum.Models.Artifact.RArtifactItemRecord", "RArtifactItemRecord")
.WithMany()
.HasForeignKey("RArtifactItemRecordId");
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord", "RArtifactMasterRecord")
.WithMany()
.HasForeignKey("RArtifactMasterRecordId");
});
modelBuilder.Entity("RMuseum.Models.GanjoorIntegration.GanjoorLink", b =>
{
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord", "Artifact")
.WithMany()
.HasForeignKey("ArtifactId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("RMuseum.Models.Artifact.RArtifactItemRecord", "Item")
.WithMany()
.HasForeignKey("ItemId");
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser", "Reviewer")
.WithMany()
.HasForeignKey("ReviewerId");
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser", "SuggestedBy")
.WithMany()
.HasForeignKey("SuggestedById")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.ImportJob.ImportJob", b =>
{
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord", "Artifact")
.WithMany()
.HasForeignKey("ArtifactId");
});
modelBuilder.Entity("RMuseum.Models.Note.RUserNote", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser", "RAppUser")
.WithMany()
.HasForeignKey("RAppUserId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("RMuseum.Models.Artifact.RArtifactItemRecord", "RArtifactItemRecord")
.WithMany()
.HasForeignKey("RArtifactItemRecordId");
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord", "RArtifactMasterRecord")
.WithMany()
.HasForeignKey("RArtifactMasterRecordId");
b.HasOne("RMuseum.Models.Note.RUserNote", "ReferenceNote")
.WithMany()
.HasForeignKey("ReferenceNoteId");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RAppUser", b =>
{
b.HasOne("RSecurityBackend.Models.Image.RImage", "RImage")
.WithMany()
.HasForeignKey("RImageId");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RCaptchaImage", b =>
{
b.HasOne("RSecurityBackend.Models.Image.RImage", "RImage")
.WithMany()
.HasForeignKey("RImageId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RPermission", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppRole")
.WithMany("Permissions")
.HasForeignKey("RAppRoleId");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RTemporaryUserSession", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser", "RAppUser")
.WithMany()
.HasForeignKey("RAppUserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Artifact.RPictureFile", b =>
{
b.HasOne("RMuseum.Models.Artifact.RArtifactItemRecord")
.WithMany("Images")
.HasForeignKey("RArtifactItemRecordId");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,83 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace RMuseum.Migrations
{
public partial class GanjoorLink : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "GanjoorLinks",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
GanjoorPostId = table.Column<int>(nullable: false),
GanjoorUrl = table.Column<string>(nullable: true),
GanjoorTitle = table.Column<string>(nullable: true),
ArtifactId = table.Column<Guid>(nullable: false),
ItemId = table.Column<Guid>(nullable: true),
SuggestedById = table.Column<Guid>(nullable: false),
SuggestionDate = table.Column<DateTime>(nullable: false),
ReviewerId = table.Column<Guid>(nullable: true),
ReviewDate = table.Column<DateTime>(nullable: false),
ReviewResult = table.Column<int>(nullable: false),
Synchronized = table.Column<bool>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_GanjoorLinks", x => x.Id);
table.ForeignKey(
name: "FK_GanjoorLinks_Artifacts_ArtifactId",
column: x => x.ArtifactId,
principalTable: "Artifacts",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_GanjoorLinks_Items_ItemId",
column: x => x.ItemId,
principalTable: "Items",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_GanjoorLinks_AspNetUsers_ReviewerId",
column: x => x.ReviewerId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_GanjoorLinks_AspNetUsers_SuggestedById",
column: x => x.SuggestedById,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_GanjoorLinks_ArtifactId",
table: "GanjoorLinks",
column: "ArtifactId");
migrationBuilder.CreateIndex(
name: "IX_GanjoorLinks_ItemId",
table: "GanjoorLinks",
column: "ItemId");
migrationBuilder.CreateIndex(
name: "IX_GanjoorLinks_ReviewerId",
table: "GanjoorLinks",
column: "ReviewerId");
migrationBuilder.CreateIndex(
name: "IX_GanjoorLinks_SuggestedById",
table: "GanjoorLinks",
column: "SuggestedById");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "GanjoorLinks");
}
}
}

View File

@ -0,0 +1,853 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using RMuseum.DbContext;
namespace RMuseum.Migrations
{
[DbContext(typeof(RMuseumDbContext))]
[Migration("20190918162149_Notifications")]
partial class Notifications
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.4-servicing-10062")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<Guid>("RoleId");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<Guid>("UserId");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.Property<string>("LoginProvider");
b.Property<string>("ProviderKey");
b.Property<string>("ProviderDisplayName");
b.Property<Guid>("UserId");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.Property<Guid>("UserId");
b.Property<Guid>("RoleId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.Property<Guid>("UserId");
b.Property<string>("LoginProvider");
b.Property<string>("Name");
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RArtifactItemRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("CoverImageIndex");
b.Property<string>("Description");
b.Property<string>("DescriptionInEnglish");
b.Property<string>("FriendlyUrl");
b.Property<DateTime>("LastModified");
b.Property<string>("Name");
b.Property<string>("NameInEnglish");
b.Property<int>("Order");
b.Property<Guid>("RArtifactMasterRecordId");
b.HasKey("Id");
b.HasIndex("RArtifactMasterRecordId", "FriendlyUrl")
.IsUnique()
.HasFilter("[FriendlyUrl] IS NOT NULL");
b.HasIndex("RArtifactMasterRecordId", "Order")
.IsUnique();
b.ToTable("Items");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RArtifactMasterRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<Guid>("CoverImageId");
b.Property<int>("CoverItemIndex");
b.Property<DateTime>("DateTime");
b.Property<string>("Description");
b.Property<string>("DescriptionInEnglish");
b.Property<string>("FriendlyUrl");
b.Property<int>("ItemCount");
b.Property<DateTime>("LastModified");
b.Property<string>("Name");
b.Property<string>("NameInEnglish");
b.Property<int>("Status");
b.HasKey("Id");
b.HasIndex("CoverImageId");
b.HasIndex("FriendlyUrl")
.IsUnique()
.HasFilter("[FriendlyUrl] IS NOT NULL");
b.ToTable("Artifacts");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RTag", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("FriendlyUrl");
b.Property<bool>("GlobalValue");
b.Property<string>("Name");
b.Property<string>("NameInEnglish");
b.Property<int>("Order");
b.Property<string>("PluralName");
b.Property<string>("PluralNameInEnglish");
b.Property<int>("Status");
b.Property<int>("TagType");
b.HasKey("Id");
b.HasIndex("FriendlyUrl");
b.ToTable("Tags");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RTagValue", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("FriendlyUrl");
b.Property<int>("Order");
b.Property<Guid?>("RArtifactItemRecordId");
b.Property<Guid?>("RArtifactMasterRecordId");
b.Property<Guid>("RTagId");
b.Property<int>("Status");
b.Property<string>("Value");
b.Property<string>("ValueInEnglish");
b.Property<string>("ValueSupplement");
b.HasKey("Id");
b.HasIndex("FriendlyUrl");
b.HasIndex("RArtifactItemRecordId");
b.HasIndex("RArtifactMasterRecordId");
b.HasIndex("RTagId");
b.ToTable("TagValues");
});
modelBuilder.Entity("RMuseum.Models.Bookmark.RUserBookmark", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("DateTime");
b.Property<string>("Note");
b.Property<Guid>("RAppUserId");
b.Property<Guid?>("RArtifactItemRecordId");
b.Property<Guid?>("RArtifactMasterRecordId");
b.Property<int>("RBookmarkType");
b.HasKey("Id");
b.HasIndex("RAppUserId");
b.HasIndex("RArtifactItemRecordId");
b.HasIndex("RArtifactMasterRecordId");
b.ToTable("UserBookmarks");
});
modelBuilder.Entity("RMuseum.Models.GanjoorIntegration.GanjoorLink", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<Guid>("ArtifactId");
b.Property<int>("GanjoorPostId");
b.Property<string>("GanjoorTitle");
b.Property<string>("GanjoorUrl");
b.Property<Guid?>("ItemId");
b.Property<DateTime>("ReviewDate");
b.Property<int>("ReviewResult");
b.Property<Guid?>("ReviewerId");
b.Property<Guid>("SuggestedById");
b.Property<DateTime>("SuggestionDate");
b.Property<bool>("Synchronized");
b.HasKey("Id");
b.HasIndex("ArtifactId");
b.HasIndex("ItemId");
b.HasIndex("ReviewerId");
b.HasIndex("SuggestedById");
b.ToTable("GanjoorLinks");
});
modelBuilder.Entity("RMuseum.Models.ImportJob.ImportJob", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<Guid?>("ArtifactId");
b.Property<DateTime>("EndTime");
b.Property<string>("Exception");
b.Property<string>("FriendlyUrl");
b.Property<int>("JobType");
b.Property<decimal>("ProgressPercent");
b.Property<DateTime>("QueueTime");
b.Property<string>("ResourceNumber");
b.Property<string>("SrcContent");
b.Property<string>("SrcUrl");
b.Property<DateTime>("StartTime");
b.Property<int>("Status");
b.HasKey("Id");
b.HasIndex("ArtifactId");
b.ToTable("ImportJobs");
});
modelBuilder.Entity("RMuseum.Models.Note.RUserNote", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("DateTime");
b.Property<string>("HtmlContent");
b.Property<DateTime>("LastModified");
b.Property<bool>("Modified");
b.Property<int>("NoteType");
b.Property<Guid>("RAppUserId");
b.Property<Guid?>("RArtifactItemRecordId");
b.Property<Guid?>("RArtifactMasterRecordId");
b.Property<Guid?>("ReferenceNoteId");
b.Property<int>("Status");
b.HasKey("Id");
b.HasIndex("RAppUserId");
b.HasIndex("RArtifactItemRecordId");
b.HasIndex("RArtifactMasterRecordId");
b.HasIndex("ReferenceNoteId");
b.ToTable("UserNotes");
});
modelBuilder.Entity("RMuseum.Models.Notification.RUserNotification", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("DateTime");
b.Property<string>("HtmlText");
b.Property<int>("Status");
b.Property<string>("Subject");
b.Property<Guid>("UserId");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Notifications");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RAppRole", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Description");
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RAppUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<DateTime>("CreateDate");
b.Property<string>("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed");
b.Property<string>("FirstName");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<Guid?>("RImageId");
b.Property<string>("SecurityStamp");
b.Property<int>("Status");
b.Property<string>("SureName");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.HasIndex("RImageId");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RCaptchaImage", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("DateTime");
b.Property<Guid>("RImageId");
b.Property<string>("Value");
b.HasKey("Id");
b.HasIndex("RImageId");
b.ToTable("CaptchaImages");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RPermission", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("OperationShortName");
b.Property<Guid?>("RAppRoleId");
b.Property<string>("SecurableItemShortName");
b.HasKey("Id");
b.HasIndex("RAppRoleId");
b.ToTable("Permissions");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RTemporaryUserSession", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClientAppName");
b.Property<string>("ClientIPAddress");
b.Property<string>("Language");
b.Property<DateTime>("LastRenewal");
b.Property<DateTime>("LoginTime");
b.Property<Guid>("RAppUserId");
b.Property<string>("Token");
b.Property<DateTime>("ValidUntil");
b.HasKey("Id");
b.HasIndex("RAppUserId");
b.ToTable("Sessions");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RVerifyQueueItem", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClientAppName");
b.Property<string>("ClientIPAddress");
b.Property<DateTime>("DateTime");
b.Property<string>("Email");
b.Property<string>("Language");
b.Property<string>("PhoneNumber");
b.Property<int>("QueueType");
b.Property<string>("Secret");
b.HasKey("Id");
b.HasIndex("Secret")
.IsUnique()
.HasFilter("[Secret] IS NOT NULL");
b.ToTable("VerifyQueueItems");
});
modelBuilder.Entity("RSecurityBackend.Models.Image.RImage", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ContentType");
b.Property<DateTime>("DataTime");
b.Property<string>("Discriminator")
.IsRequired();
b.Property<long>("FileSizeInBytes");
b.Property<string>("FolderName");
b.Property<int>("ImageHeight");
b.Property<int>("ImageWidth");
b.Property<DateTime>("LastModified");
b.Property<string>("OriginalFileName");
b.Property<string>("StoredFileName");
b.HasKey("Id");
b.ToTable("GeneralImages");
b.HasDiscriminator<string>("Discriminator").HasValue("RImage");
});
modelBuilder.Entity("RMuseum.Models.Artifact.RPictureFile", b =>
{
b.HasBaseType("RSecurityBackend.Models.Image.RImage");
b.Property<string>("Description");
b.Property<string>("DescriptionInEnglish");
b.Property<DateTime>("LastModifiedMeta");
b.Property<int>("NormalSizeImageHeight");
b.Property<string>("NormalSizeImageStoredFileName");
b.Property<int>("NormalSizeImageWidth");
b.Property<int>("Order");
b.Property<Guid?>("RArtifactItemRecordId");
b.Property<string>("SrcUrl");
b.Property<int>("Status");
b.Property<int>("ThumbnailImageHeight");
b.Property<string>("ThumbnailImageStoredFileName");
b.Property<int>("ThumbnailImageWidth");
b.Property<string>("Title");
b.Property<string>("TitleInEnglish");
b.HasIndex("RArtifactItemRecordId");
b.HasDiscriminator().HasValue("RPictureFile");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Artifact.RArtifactItemRecord", b =>
{
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord")
.WithMany("Items")
.HasForeignKey("RArtifactMasterRecordId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Artifact.RArtifactMasterRecord", b =>
{
b.HasOne("RMuseum.Models.Artifact.RPictureFile", "CoverImage")
.WithMany()
.HasForeignKey("CoverImageId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Artifact.RTagValue", b =>
{
b.HasOne("RMuseum.Models.Artifact.RArtifactItemRecord")
.WithMany("Tags")
.HasForeignKey("RArtifactItemRecordId");
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord")
.WithMany("Tags")
.HasForeignKey("RArtifactMasterRecordId");
b.HasOne("RMuseum.Models.Artifact.RTag", "RTag")
.WithMany()
.HasForeignKey("RTagId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Bookmark.RUserBookmark", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser", "RAppUser")
.WithMany()
.HasForeignKey("RAppUserId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("RMuseum.Models.Artifact.RArtifactItemRecord", "RArtifactItemRecord")
.WithMany()
.HasForeignKey("RArtifactItemRecordId");
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord", "RArtifactMasterRecord")
.WithMany()
.HasForeignKey("RArtifactMasterRecordId");
});
modelBuilder.Entity("RMuseum.Models.GanjoorIntegration.GanjoorLink", b =>
{
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord", "Artifact")
.WithMany()
.HasForeignKey("ArtifactId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("RMuseum.Models.Artifact.RArtifactItemRecord", "Item")
.WithMany()
.HasForeignKey("ItemId");
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser", "Reviewer")
.WithMany()
.HasForeignKey("ReviewerId");
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser", "SuggestedBy")
.WithMany()
.HasForeignKey("SuggestedById")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.ImportJob.ImportJob", b =>
{
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord", "Artifact")
.WithMany()
.HasForeignKey("ArtifactId");
});
modelBuilder.Entity("RMuseum.Models.Note.RUserNote", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser", "RAppUser")
.WithMany()
.HasForeignKey("RAppUserId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("RMuseum.Models.Artifact.RArtifactItemRecord", "RArtifactItemRecord")
.WithMany()
.HasForeignKey("RArtifactItemRecordId");
b.HasOne("RMuseum.Models.Artifact.RArtifactMasterRecord", "RArtifactMasterRecord")
.WithMany()
.HasForeignKey("RArtifactMasterRecordId");
b.HasOne("RMuseum.Models.Note.RUserNote", "ReferenceNote")
.WithMany()
.HasForeignKey("ReferenceNoteId");
});
modelBuilder.Entity("RMuseum.Models.Notification.RUserNotification", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RAppUser", b =>
{
b.HasOne("RSecurityBackend.Models.Image.RImage", "RImage")
.WithMany()
.HasForeignKey("RImageId");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RCaptchaImage", b =>
{
b.HasOne("RSecurityBackend.Models.Image.RImage", "RImage")
.WithMany()
.HasForeignKey("RImageId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RPermission", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppRole")
.WithMany("Permissions")
.HasForeignKey("RAppRoleId");
});
modelBuilder.Entity("RSecurityBackend.Models.Auth.Db.RTemporaryUserSession", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser", "RAppUser")
.WithMany()
.HasForeignKey("RAppUserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("RMuseum.Models.Artifact.RPictureFile", b =>
{
b.HasOne("RMuseum.Models.Artifact.RArtifactItemRecord")
.WithMany("Images")
.HasForeignKey("RArtifactItemRecordId");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,44 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace RMuseum.Migrations
{
public partial class Notifications : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Notifications",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
UserId = table.Column<Guid>(nullable: false),
DateTime = table.Column<DateTime>(nullable: false),
Status = table.Column<int>(nullable: false),
Subject = table.Column<string>(nullable: true),
HtmlText = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Notifications", x => x.Id);
table.ForeignKey(
name: "FK_Notifications_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Notifications_UserId",
table: "Notifications",
column: "UserId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Notifications");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,37 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace RMuseum.Migrations
{
public partial class AuditLogs : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AuditLogs",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
EventType = table.Column<string>(nullable: true),
StartDate = table.Column<DateTime>(nullable: false),
EndDate = table.Column<DateTime>(nullable: true),
Duration = table.Column<int>(nullable: false),
UserName = table.Column<string>(nullable: true),
IpAddress = table.Column<string>(nullable: true),
ResponseStatusCode = table.Column<int>(nullable: false),
RequestUrl = table.Column<string>(nullable: true),
JsonData = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AuditLogs", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AuditLogs");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace RMuseum.Migrations
{
public partial class PinterestLinks : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "PinterestLinks",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
GanjoorPostId = table.Column<int>(nullable: false),
GanjoorUrl = table.Column<string>(nullable: true),
GanjoorTitle = table.Column<string>(nullable: true),
AltText = table.Column<string>(nullable: true),
LinkType = table.Column<int>(nullable: false),
PinterestUrl = table.Column<string>(nullable: true),
PinterestImageUrl = table.Column<string>(nullable: true),
SuggestedById = table.Column<Guid>(nullable: true),
SuggestionDate = table.Column<DateTime>(nullable: false),
ReviewerId = table.Column<Guid>(nullable: true),
ReviewDate = table.Column<DateTime>(nullable: false),
ReviewResult = table.Column<int>(nullable: false),
ReviewDesc = table.Column<string>(nullable: true),
ArtifactId = table.Column<Guid>(nullable: true),
ItemId = table.Column<Guid>(nullable: true),
Synchronized = table.Column<bool>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_PinterestLinks", x => x.Id);
table.ForeignKey(
name: "FK_PinterestLinks_Artifacts_ArtifactId",
column: x => x.ArtifactId,
principalTable: "Artifacts",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_PinterestLinks_Items_ItemId",
column: x => x.ItemId,
principalTable: "Items",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_PinterestLinks_AspNetUsers_ReviewerId",
column: x => x.ReviewerId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_PinterestLinks_AspNetUsers_SuggestedById",
column: x => x.SuggestedById,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_PinterestLinks_ArtifactId",
table: "PinterestLinks",
column: "ArtifactId");
migrationBuilder.CreateIndex(
name: "IX_PinterestLinks_ItemId",
table: "PinterestLinks",
column: "ItemId");
migrationBuilder.CreateIndex(
name: "IX_PinterestLinks_ReviewerId",
table: "PinterestLinks",
column: "ReviewerId");
migrationBuilder.CreateIndex(
name: "IX_PinterestLinks_SuggestedById",
table: "PinterestLinks",
column: "SuggestedById");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "PinterestLinks");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
namespace RMuseum.Models.Artifact
{
/// <summary>
/// Items or pictures status
/// </summary>
public enum PublishStatus
{
Draft = 0,
Awaiting = 1,
Refused = 2,
Published = 4,
Restricted = 8,
Hidden = 16,
Deleted = 32
}
}

View File

@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
namespace RMuseum.Models.Artifact
{
public class RArtifactItemRecord
{
/// <summary>
/// Id
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// parent id
/// </summary>
public Guid RArtifactMasterRecordId { get; set; }
/// <summary>
/// Order in Collections
/// </summary>
public int Order { get; set; }
/// <summary>
/// Friendly Url
/// </summary>
public string FriendlyUrl { get; set; }
/// <summary>
/// Name
/// </summary>
public string Name { get; set; }
/// <summary>
/// Name In English
/// </summary>
public string NameInEnglish { get; set; }
/// <summary>
/// Description
/// </summary>
public string Description { get; set; }
/// <summary>
/// Description In English
/// </summary>
public string DescriptionInEnglish { get; set; }
/// <summary>
/// Main Image
/// </summary>
public int CoverImageIndex { get; set; }
/// <summary>
/// Last Modified for caching purposes
/// </summary>
public DateTime LastModified { get; set; }
/// <summary>
/// All Images
/// </summary>
public ICollection<RPictureFile> Images { get; set; }
/// <summary>
/// Attributes
/// </summary>
public ICollection<RTagValue> Tags { get; set; }
}
}

View File

@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
namespace RMuseum.Models.Artifact
{
/// <summary>
/// Museum Master Item
/// </summary>
public class RArtifactMasterRecord
{
public RArtifactMasterRecord() { }
public RArtifactMasterRecord(string name, string description)
{
Name = NameInEnglish = name;
Description = DescriptionInEnglish = description;
}
/// <summary>
/// Id
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Friendly Url
/// </summary>
public string FriendlyUrl { get; set; }
/// <summary>
/// Publish Status
/// </summary>
public PublishStatus Status { get; set; }
/// <summary>
/// Name
/// </summary>
public string Name { get; set; }
/// <summary>
/// Name In English
/// </summary>
public string NameInEnglish { get; set; }
/// <summary>
/// Description
/// </summary>
public string Description { get; set; }
/// <summary>
/// Description In English
/// </summary>
public string DescriptionInEnglish { get; set; }
/// <summary>
/// Date/Time
/// </summary>
public DateTime DateTime { get; set; }
/// <summary>
/// Last Modified for caching purposes
/// </summary>
public DateTime LastModified { get; set; }
/// <summary>
/// Cover Item Index
/// </summary>
public int CoverItemIndex { get; set; }
/// <summary>
/// Cover Image
/// </summary>
public RPictureFile CoverImage { get; set; }
/// <summary>
/// Cover Image Id
/// </summary>
public Guid CoverImageId { get; set; }
/// <summary>
/// Parts of this item
/// </summary>
public ICollection<RArtifactItemRecord> Items { get; set; }
/// <summary>
/// Item Count (for lists and queries)
/// </summary>
public int ItemCount { get; set; }
/// <summary>
/// Attributes
/// </summary>
public ICollection<RTagValue> Tags { get; set; }
}
}

View File

@ -0,0 +1,131 @@
using RSecurityBackend.Models.Image;
using System;
namespace RMuseum.Models.Artifact
{
/// <summary>
/// Artifact Picture Files
/// </summary>
public class RPictureFile : RImage
{
/// <summary>
/// Title
/// </summary>
public string Title { get; set; }
/// <summary>
/// Title in English
/// </summary>
public string TitleInEnglish { set; get; }
/// <summary>
/// Description
/// </summary>
public string Description { get; set; }
/// <summary>
/// Description in English
/// </summary>
public string DescriptionInEnglish { get; set; }
/// <summary>
/// Publish status
/// </summary>
public PublishStatus Status { get; set; }
/// <summary>
/// order in the collection it belongs to it
/// </summary>
public int Order { get; set; }
/// <summary>
/// نام فایل ذخیره شده با بالاترین کیفیت
/// </summary>
/// <example>
/// e0d7050a-7e30-4a2f-b181-0daa4a1e1068.jpg
/// </example>
public string NormalSizeImageStoredFileName { get; set; }
/// <summary>
/// نام فایل ذخیره شده با بالاترین کیفیت
/// </summary>
/// <example>
/// fbe9cc8e-12fa-4cb9-8f09-bbf353333383.jpg
/// </example>
public string ThumbnailImageStoredFileName { get; set; }
/// <summary>
/// عرض تصویر مناسب نمایش در صفحهٔ اختصاصی
/// </summary>
public int NormalSizeImageWidth { get; set; }
/// <summary>
/// طول تصویر مناسب نمایش در صفحهٔ اختصاصی
/// </summary>
public int NormalSizeImageHeight { get; set; }
/// <summary>
///عرض تصویر با اندازه مناسب نمایش در صفحات لیستی
/// </summary>
public int ThumbnailImageWidth { get; set; }
/// <summary>
///طول تصویر با اندازه مناسب نمایش در صفحات لیستی
/// </summary>
public int ThumbnailImageHeight { get; set; }
/// <summary>
/// source url
/// </summary>
public string SrcUrl { get; set; }
/// <summary>
/// Last Modified for caching purposes
/// </summary>
public DateTime LastModifiedMeta { get; set; }
/// <summary>
/// duplicated a picture record (Id is missing so you should store this to get a new Id)
/// </summary>
/// <param name="src"></param>
/// <returns></returns>
public static RPictureFile Duplicate(RPictureFile src)
{
if (src == null)
return null;
return
new RPictureFile()
{
Title = src.Title,
TitleInEnglish = src.TitleInEnglish,
Description = src.Description,
DescriptionInEnglish = src.DescriptionInEnglish,
FolderName = src.FolderName,
DataTime = src.DataTime,
LastModified = src.LastModified,
LastModifiedMeta = src.LastModifiedMeta,
NormalSizeImageHeight = src.NormalSizeImageHeight,
NormalSizeImageStoredFileName = src.NormalSizeImageStoredFileName,
NormalSizeImageWidth = src.NormalSizeImageWidth,
Order = src.Order,
OriginalFileName = src.OriginalFileName,
ContentType = src.ContentType,
FileSizeInBytes = src.FileSizeInBytes,
ImageHeight = src.ImageHeight,
StoredFileName = src.StoredFileName,
ImageWidth = src.ImageWidth,
SrcUrl = src.SrcUrl,
Status = src.Status,
ThumbnailImageHeight = src.ThumbnailImageHeight,
ThumbnailImageStoredFileName = src.ThumbnailImageStoredFileName,
ThumbnailImageWidth = src.ThumbnailImageWidth
};
}
}
}

View File

@ -0,0 +1,61 @@
using System;
namespace RMuseum.Models.Artifact
{
/// <summary>
/// Item Attribute
/// </summary>
public class RTag
{
/// <summary>
/// Id
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Order
/// </summary>
public int Order { get; set; }
/// <summary>
/// tag type
/// </summary>
public RTagType TagType { get; set; }
/// <summary>
/// Friendly Url
/// </summary>
public string FriendlyUrl { get; set; }
/// <summary>
/// Publish Status
/// </summary>
public PublishStatus Status { get; set; }
/// <summary>
/// Name
/// </summary>
public string Name { get; set; }
/// <summary>
/// Name In English
/// </summary>
public string NameInEnglish { get; set; }
/// <summary>
/// Plural Name
/// </summary>
public string PluralName { get; set; }
/// <summary>
/// Plural Name In English
/// </summary>
public string PluralNameInEnglish { get; set; }
/// <summary>
/// Value is normally changed globally
/// </summary>
public bool GlobalValue { get; set; }
}
}

View File

@ -0,0 +1,37 @@
namespace RMuseum.Models.Artifact
{
/// <summary>
/// نوع ویژگی
/// </summary>
public enum RTagType
{
/// <summary>
/// عادی
/// </summary>
Ordinary = 0,
/// <summary>
/// پیوند
/// </summary>
Link = 1,
/// <summary>
/// جستجو
/// </summary>
Search = 2,
/// <summary>
/// پیوند و جستجو
/// </summary>
LinkSearch = 3,
/// <summary>
/// متن چپ به راست
/// </summary>
LeftToRightText = 4,
/// <summary>
/// بدون مقدار
/// </summary>
Binary = 5,
/// <summary>
/// عنوان در فهرست
/// </summary>
TitleInContents = 6,
}
}

View File

@ -0,0 +1,46 @@
using System;
namespace RMuseum.Models.Artifact
{
/// <summary>
/// Attribute Value
/// </summary>
public class RTagValue
{
/// <summary>
/// Id
/// </summary>
public Guid Id { get; set; }
public Guid RTagId { get; set; }
public RTag RTag { get; set; }
public int Order { get; set; }
/// <summary>
/// Friendly Url
/// </summary>
public string FriendlyUrl { get; set; }
/// <summary>
/// Publish Status
/// </summary>
public PublishStatus Status { get; set; }
/// <summary>
/// Value
/// </summary>
public string Value { get; set; }
/// <summary>
/// Values In English
/// </summary>
public string ValueInEnglish { get; set; }
/// <summary>
/// link or ....
/// </summary>
public string ValueSupplement { get; set; }
}
}

View File

@ -0,0 +1,27 @@
using Microsoft.AspNetCore.Http;
namespace RMuseum.Models.Artifact.ViewModels
{
/// <summary>
/// new artifact view model
/// </summary>
public class NewArtifact
{
public string Name { get; set; }
public string Description { get; set; }
public string SrcUrl { get; set; }
public string PicTitle { get; set; }
public string PicDescription { get; set; }
public IFormFile File { get; set; }
public string PicSrcUrl { get; set; }
}
}

View File

@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace RMuseum.Models.Artifact.ViewModels
{
/// <summary>
/// RArtifactItemRecord with additional info
/// </summary>
public class RArtifactItemRecordViewModel
{
private RArtifactItemRecord _Item;
/// <summary>
/// main item info
/// </summary>
public RArtifactItemRecord Item
{
get
{
return _Item;
}
set
{
_Item = value;
List<RArtifactTagViewModel> rItemTags = new List<RArtifactTagViewModel>();
if (_Item != null && _Item.Tags != null)
{
foreach (RTagValue tag in _Item.Tags)
{
RArtifactTagViewModel related = rItemTags.Where(t => t.Id == tag.RTagId).SingleOrDefault();
List<RTagValue> values = (related == null) ? new List<RTagValue>() : new List<RTagValue>(related.Values);
if (related == null)
{
related = new RArtifactTagViewModel(tag.RTag);
rItemTags.Add(related);
}
values.Add(tag);
values.Sort((a, b) => a.Order - b.Order);
related.Values = values;
}
rItemTags.Sort((a, b) => a.Order - b.Order);
}
FormattedTags = rItemTags;
}
}
/// <summary>
/// parent
/// </summary>
public string ParentFriendlyUrl { get; set; }
/// <summary>
/// parent name
/// </summary>
public string ParentName { get; set; }
/// <summary>
/// parent image
/// </summary>
public Guid ParentImageId { get; set; }
/// <summary>
/// parent item count
/// </summary>
public int ParentItemCount { get; set; }
/// <summary>
/// empty or null means this is last item
/// </summary>
public string NextItemFriendlyUrl { get; set; }
/// <summary>
/// next image
/// </summary>
public Guid? NextItemImageId { get; set; }
/// <summary>
/// empty or null means this is first item
/// </summary>
public string PreviousItemFriendlyUrl { get; set; }
/// <summary>
/// prev image
/// </summary>
public Guid? PrevItemImageId { get; set; }
/// <summary>
/// Formatted Tags
/// </summary>
public ICollection<RArtifactTagViewModel> FormattedTags { get; set; }
}
}

View File

@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace RMuseum.Models.Artifact.ViewModels
{
/// <summary>
/// Museum Master Item View Model
/// </summary>
public class RArtifactMasterRecordViewModel : RArtifactMasterRecord
{
/// <summary>
/// constructor
/// </summary>
/// <param name="src"></param>
public RArtifactMasterRecordViewModel(RArtifactMasterRecord src)
{
Id = src.Id;
FriendlyUrl = src.FriendlyUrl;
Status = src.Status;
Name = src.Name;
NameInEnglish = src.NameInEnglish;
Description = src.Description;
DescriptionInEnglish = src.DescriptionInEnglish;
DateTime = src.DateTime;
LastModified = src.LastModified;
CoverItemIndex = src.CoverItemIndex;
CoverImage = src.CoverImage;
CoverImageId = src.CoverImageId;
ItemCount = src.ItemCount;
Tags = null;
List<RArtifactItemRecord> items = new List<RArtifactItemRecord>();
List<RTagSum> sums = new List<RTagSum>();
List<RTitleInContents> contents = new List<RTitleInContents>();
int orderOfContents = 0;
if(src.Items != null)
foreach(RArtifactItemRecord item in src.Items)
{
if(item.Tags != null)
{
var OrderedTags = item.Tags.OrderBy(t => t.RTag.Order).ThenBy(t => t.Order);
foreach (RTagValue value in OrderedTags)
{
if (value.RTag != null)
{
switch(value.RTag.TagType)
{
case RTagType.Binary:
{
RTagSum sum = sums.Where(s => s.TagFriendlyUrl == value.RTag.FriendlyUrl).SingleOrDefault();
if (sum != null)
{
sum.ItemCount++;
}
else
{
sums.Add(new RTagSum()
{
TagName = value.RTag.Name,
TagFriendlyUrl = value.RTag.FriendlyUrl,
ItemCount = 1
});
}
}
break;
case RTagType.TitleInContents:
{
if(!int.TryParse(value.ValueSupplement, out int level))
{
level = 1;
}
if(contents.Where(c => c.Title == value.Value).SingleOrDefault() == null)
{
contents.Add
(
new RTitleInContents()
{
Title = value.Value,
Order = ++orderOfContents,
Level = level,
ItemFriendlyUrl = item.FriendlyUrl
}
);
}
}
break;
}
}
}
}
item.Tags = null;
items.Add(item);
}
Items = items;
RTagSums = sums;
Contents = contents;
List<RArtifactTagViewModel> rArtifactTags = new List<RArtifactTagViewModel>();
if(src.Tags != null)
{
foreach (RTagValue tag in src.Tags)
{
RArtifactTagViewModel related = rArtifactTags.Where(t => t.Id == tag.RTagId).SingleOrDefault();
List<RTagValue> values = (related == null) ? new List<RTagValue>() : new List<RTagValue>(related.Values);
if (related == null)
{
related = new RArtifactTagViewModel(tag.RTag);
rArtifactTags.Add(related);
}
values.Add(tag);
values.Sort((a, b) => a.Order - b.Order);
related.Values = values;
}
rArtifactTags.Sort((a, b) => a.Order - b.Order);
}
ArtifactTags = rArtifactTags;
}
/// <summary>
/// Tags
/// </summary>
public ICollection<RArtifactTagViewModel> ArtifactTags { get; set; }
/// <summary>
/// Binary Tagged Items
/// </summary>
public ICollection<RTagSum> RTagSums { get; set; }
/// <summary>
/// Titles of Items in Contents
/// </summary>
public ICollection<RTitleInContents> Contents { get; set; }
}
}

View File

@ -0,0 +1,26 @@
using System.Collections.Generic;
namespace RMuseum.Models.Artifact.ViewModels
{
/// <summary>
/// Artifact Tag (RTag with grouped related RTagValues of Artifact)
/// </summary>
public class RArtifactTagViewModel : RTag
{
public RArtifactTagViewModel(RTag src)
{
Id = src.Id;
Order = src.Order;
TagType = src.TagType;
FriendlyUrl = src.FriendlyUrl;
Status = src.Status;
Name = src.Name;
NameInEnglish = src.NameInEnglish;
GlobalValue = src.GlobalValue;
PluralName = src.PluralName;
PluralNameInEnglish = src.PluralNameInEnglish;
}
public ICollection<RTagValue> Values { get; set; }
}
}

View File

@ -0,0 +1,31 @@
using System;
namespace RMuseum.Models.Artifact.ViewModels
{
/// <summary>
/// Tag Bundle View Model
/// </summary>
public class RTagBundleValueViewModel
{
/// <summary>
/// Item Title
/// </summary>
public string Name { get; set; }
/// <summary>
/// Item Friendly Url
/// </summary>
public string FriendlyUrl { get; set; }
/// <summary>
/// Items Count
/// </summary>
public int Count { get; set; }
/// <summary>
/// Item Image Id
/// </summary>
public Guid ImageId { get; set; }
}
}

View File

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace RMuseum.Models.Artifact.ViewModels
{
/// <summary>
/// Tag Bundle View Model
/// </summary>
public class RTagBundleViewModel
{
/// <summary>
/// Id
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Friendly Url
/// </summary>
public string FriendlyUrl { get; set; }
/// <summary>
/// Name
/// </summary>
public string Name { get; set; }
/// <summary>
/// Plural Name
/// </summary>
public string PluralName { get; set; }
/// <summary>
/// Values
/// </summary>
public RTagBundleValueViewModel[] Values { get; set; }
}
}

View File

@ -0,0 +1,23 @@
namespace RMuseum.Models.Artifact.ViewModels
{
/// <summary>
/// Artifact Binary Tagged items Summation
/// </summary>
public class RTagSum
{
/// <summary>
/// Tag Name
/// </summary>
public string TagName { get; set; }
/// <summary>
/// Tag Friendly Url
/// </summary>
public string TagFriendlyUrl{ get; set; }
/// <summary>
/// Item Count
/// </summary>
public int ItemCount { get; set; }
}
}

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace RMuseum.Models.Artifact.ViewModels
{
/// <summary>
/// Title of an Item in Artifact Contents
/// </summary>
public class RTitleInContents
{
/// <summary>
/// Title
/// </summary>
public string Title { get; set; }
/// <summary>
/// Order
/// </summary>
public int Order { get; set; }
/// <summary>
/// Level
/// </summary>
public int Level { get; set; }
/// <summary>
/// Item Friendly Url
/// </summary>
public string ItemFriendlyUrl { get; set; }
}
}

View File

@ -0,0 +1,115 @@
using RSecurityBackend.Models.Auth.Memory;
using System.Collections.Generic;
namespace RMuseum.Models.Auth.Memory
{
/// <summary>
/// specific forms and permission
/// </summary>
public class RMuseumSecurableItem : SecurableItem
{
/// <summary>
/// artifact
/// </summary>
public const string ArtifactEntityShortName = "artifact";
/// <summary>
/// tag
/// </summary>
public const string TagEntityShortName = "tag";
/// <summary>
/// note
/// </summary>
public const string NoteEntityShortName = "note";
///<summary>
/// view drafts
/// </summary>
public const string ViewDraftOperationShortName = "viewdraft";
///<summary>
/// edittag
/// </summary>
public const string EditTagValueOperationShortName = "edittag";
///<summary>
/// awaiting
/// </summary>
public const string ToAwaitingStatusOperationShortName = "awaiting";
///<summary>
/// publish
/// </summary>
public const string PublishOperationShortName = "publish";
///<summary>
/// import
/// </summary>
public const string ImportOperationShortName = "import";
///<summary>
/// moderate
/// </summary>
public const string ModerateCommentsOperationShortName = "moderate";
///<summary>
/// review suggested ganjoor links
/// </summary>
public const string ReviewGanjoorLinksOperationShortName = "ganjoor";
/// <summary>
/// list of forms and their permissions
/// </summary>
public new static SecurableItem[] Items
{
get
{
List<SecurableItem> lst = new List<SecurableItem>(SecurableItem.Items);
lst.AddRange(
new SecurableItem[]
{
new SecurableItem()
{
ShortName = ArtifactEntityShortName,
Description = "اشیاء گنجینه",
Operations = new SecurableItemOperation[]
{
new SecurableItemOperation(AddOperationShortName, "ایجاد", false),
new SecurableItemOperation(ImportOperationShortName, "ورود اطلاعات", false),
new SecurableItemOperation(ModifyOperationShortName, "اصلاح", false),
new SecurableItemOperation(DeleteOperationShortName, "حذف", false),
new SecurableItemOperation(ViewDraftOperationShortName, "مشاهدهٔ پیش‌نویس‌ها", false),
new SecurableItemOperation(EditTagValueOperationShortName, "اصلاح مقدار ویژگی", false),
new SecurableItemOperation(ToAwaitingStatusOperationShortName, "درخواست بازبینی", false),
new SecurableItemOperation(PublishOperationShortName, "انتشار", false),
new SecurableItemOperation(ReviewGanjoorLinksOperationShortName, "بررسی شعرهای پیشنهادی گنجور", false),
}
},
new SecurableItem()
{
ShortName = TagEntityShortName,
Description = "انواع ویژگیها",
Operations = new SecurableItemOperation[]
{
new SecurableItemOperation(AddOperationShortName, "ایجاد", false),
new SecurableItemOperation(ModifyOperationShortName, "اصلاح", false),
new SecurableItemOperation(DeleteOperationShortName, "حذف", false)
}
},
new SecurableItem()
{
ShortName = NoteEntityShortName,
Description = "یادداشتها",
Operations = new SecurableItemOperation[]
{
new SecurableItemOperation(ModerateCommentsOperationShortName, "بررسی", false)
}
},
});
return lst.ToArray();
}
}
}
}

View File

@ -0,0 +1,17 @@
namespace RMuseum.Models.Bookmark
{
/// <summary>
/// Bookmark Type
/// </summary>
public enum RBookmarkType
{
/// <summary>
/// Private Bookmark
/// </summary>
Bookmark = 0,
/// <summary>
/// Public Favorite
/// </summary>
Favorite = 1
}
}

View File

@ -0,0 +1,63 @@
using RMuseum.Models.Artifact;
using RSecurityBackend.Models.Auth.Db;
using System;
namespace RMuseum.Models.Bookmark
{
/// <summary>
/// User Bookmarks
/// </summary>
public class RUserBookmark
{
/// <summary>
/// Id
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// User Id
/// </summary>
public Guid RAppUserId { get; set; }
/// <summary>
/// User
/// </summary>
public RAppUser RAppUser { get; set; }
/// <summary>
/// Master Record Id
/// </summary>
public Guid? RArtifactMasterRecordId { get; set; }
/// <summary>
/// Master Record
/// </summary>
public virtual RArtifactMasterRecord RArtifactMasterRecord { get; set; }
/// <summary>
/// Item Record Id
/// </summary>
public Guid? RArtifactItemRecordId { get; set; }
/// <summary>
/// Item Record
/// </summary>
public virtual RArtifactItemRecord RArtifactItemRecord { get; set; }
/// <summary>
/// DateTime
/// </summary>
public DateTime DateTime { get; set; }
/// <summary>
/// Type
/// </summary>
public RBookmarkType RBookmarkType { get; set; }
/// <summary>
/// User Note
/// </summary>
public string Note { get; set; }
}
}

View File

@ -0,0 +1,56 @@
using RMuseum.Models.Artifact;
using RMuseum.Models.Artifact.ViewModels;
using System;
namespace RMuseum.Models.Bookmark.ViewModels
{
public class RUserBookmarkViewModel
{
public RUserBookmarkViewModel(RUserBookmark src)
{
Id = src.Id;
RAppUserId = src.RAppUserId;
RArtifactMasterRecord = src.RArtifactMasterRecord;
RArtifactItemRecord = null;//this should be filled by an external call
DateTime = src.DateTime;
RBookmarkType = src.RBookmarkType;
Note = src.Note;
}
/// <summary>
/// Id
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// User Id
/// </summary>
public Guid RAppUserId { get; set; }
/// <summary>
/// Master Record
/// </summary>
public virtual RArtifactMasterRecord RArtifactMasterRecord { get; set; }
/// <summary>
/// Item Record
/// </summary>
public virtual RArtifactItemRecordViewModel RArtifactItemRecord { get; set; }
/// <summary>
/// DateTime
/// </summary>
public DateTime DateTime { get; set; }
/// <summary>
/// Type
/// </summary>
public RBookmarkType RBookmarkType { get; set; }
/// <summary>
/// User Note
/// </summary>
public string Note { get; set; }
}
}

View File

@ -0,0 +1,92 @@
using RMuseum.Models.Artifact;
using RSecurityBackend.Models.Auth.Db;
using System;
namespace RMuseum.Models.GanjoorIntegration
{
/// <summary>
/// ganjoor link for artifacts and items
/// </summary>
public class GanjoorLink
{
/// <summary>
/// Id
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Ganjoor Post Id
/// </summary>
public int GanjoorPostId { get; set; }
/// <summary>
/// ganjoor url
/// </summary>
public string GanjoorUrl { get; set; }
/// <summary>
/// ganjoor title
/// </summary>
public string GanjoorTitle { get; set; }
/// <summary>
/// Artifact Id
/// </summary>
public Guid ArtifactId { get; set; }
/// <summary>
/// artifact
/// </summary>
public RArtifactMasterRecord Artifact { get; set; }
/// <summary>
/// Artifact Item Id
/// </summary>
public Guid? ItemId { get; set; }
/// <summary>
/// Artifact Item
/// </summary>
public virtual RArtifactItemRecord Item { get; set; }
/// <summary>
/// User Id who suggested the link
/// </summary>
public Guid SuggestedById { get; set; }
/// <summary>
/// Suggestion Date
/// </summary>
public DateTime SuggestionDate { get; set; }
/// <summary>
/// User who suggested the link
/// </summary>
public RAppUser SuggestedBy { get; set; }
/// <summary>
/// User id who reviewed the link
/// </summary>
public Guid? ReviewerId { get; set; }
/// <summary>
/// User who reviewed the link
/// </summary>
public virtual RAppUser Reviewer { get; set; }
/// <summary>
/// Review Date
/// </summary>
public DateTime ReviewDate { get; set; }
/// <summary>
/// review result
/// </summary>
public ReviewResult ReviewResult { get; set; }
/// <summary>
/// Synchronized with ganjoor
/// </summary>
public bool Synchronized { get; set; }
}
}

View File

@ -0,0 +1,25 @@
namespace RMuseum.Models.GanjoorIntegration
{
/// <summary>
/// Link Type
/// </summary>
public enum LinkType
{
/// <summary>
/// Custom
/// </summary>
Custom = 0,
/// <summary>
/// Museum
/// </summary>
Museum = 1,
/// <summary>
/// Pinterest
/// </summary>
Pinterest = 2,
/// <summary>
/// Instagram
/// </summary>
Instagram = 3
}
}

View File

@ -0,0 +1,118 @@
using RMuseum.Models.Artifact;
using RSecurityBackend.Models.Auth.Db;
using System;
namespace RMuseum.Models.GanjoorIntegration
{
/// <summary>
/// pinterest link
/// </summary>
public class PinterestLink
{
/// <summary>
/// Id
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Ganjoor Post Id
/// </summary>
public int GanjoorPostId { get; set; }
/// <summary>
/// ganjoor url
/// </summary>
public string GanjoorUrl { get; set; }
/// <summary>
/// ganjoor title
/// </summary>
public string GanjoorTitle { get; set; }
/// <summary>
/// alt text
/// </summary>
public string AltText { get; set; }
/// <summary>
/// link type
/// </summary>
public LinkType LinkType { get; set; }
/// <summary>
/// pinterest url
/// </summary>
public string PinterestUrl { get; set; }
/// <summary>
/// pinterest image url
/// </summary>
public string PinterestImageUrl { get; set; }
/// <summary>
/// User Id who suggested the link (this would always be null for anonyous suggestions, kept it so that someday it would be used to integerate GanjoorLink into this class)
/// </summary>
public Guid? SuggestedById { get; set; }
/// <summary>
/// Suggestion Date
/// </summary>
public DateTime SuggestionDate { get; set; }
/// <summary>
/// User who suggested the link
/// </summary>
public virtual RAppUser SuggestedBy { get; set; }
/// <summary>
/// User id who reviewed the link
/// </summary>
public Guid? ReviewerId { get; set; }
/// <summary>
/// User who reviewed the link
/// </summary>
public virtual RAppUser Reviewer { get; set; }
/// <summary>
/// Review Date
/// </summary>
public DateTime ReviewDate { get; set; }
/// <summary>
/// review result
/// </summary>
public ReviewResult ReviewResult { get; set; }
/// <summary>
/// review description
/// </summary>
public string ReviewDesc { get; set; }
/// <summary>
/// Artifact Id
/// </summary>
public Guid? ArtifactId { get; set; }
/// <summary>
/// artifact
/// </summary>
public virtual RArtifactMasterRecord Artifact { get; set; }
/// <summary>
/// Artifact Item Id
/// </summary>
public Guid? ItemId { get; set; }
/// <summary>
/// Artifact Item
/// </summary>
public virtual RArtifactItemRecord Item { get; set; }
/// <summary>
/// Synchronized with ganjoor
/// </summary>
public bool Synchronized { get; set; }
}
}

View File

@ -0,0 +1,21 @@
namespace RMuseum.Models.GanjoorIntegration
{
/// <summary>
/// Review Result
/// </summary>
public enum ReviewResult
{
/// <summary>
/// Awaiting (default)
/// </summary>
Awaiting = 0,
/// <summary>
/// Approved
/// </summary>
Approved = 1,
/// <summary>
/// Rejected
/// </summary>
Rejected = 2
}
}

View File

@ -0,0 +1,75 @@
using RSecurityBackend.Models.Auth.ViewModels;
using System;
namespace RMuseum.Models.GanjoorIntegration.ViewModels
{
/// <summary>
/// Ganjoor Link View Model
/// </summary>
public class GanjoorLinkViewModel
{
public GanjoorLinkViewModel(GanjoorLink src, string entityName, string entityFriendlyUrl, Guid entityImageId)
{
Id = src.Id;
GanjoorPostId = src.GanjoorPostId;
GanjoorUrl = src.GanjoorUrl;
GanjoorTitle = src.GanjoorTitle;
EntityName = entityName;
EntityFriendlyUrl = entityFriendlyUrl;
EntityImageId = entityImageId;
ReviewResult = src.ReviewResult;
Synchronized = src.Synchronized;
SuggestedBy = new PublicRAppUser(src.SuggestedBy);
}
/// <summary>
/// Id
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Ganjoor Post Id
/// </summary>
public int GanjoorPostId { get; set; }
/// <summary>
/// ganjoor url
/// </summary>
public string GanjoorUrl { get; set; }
/// <summary>
/// ganjoor title
/// </summary>
public string GanjoorTitle { get; set; }
/// <summary>
/// entity name
/// </summary>
public string EntityName { get; set; }
/// <summary>
/// entity friendly url
/// </summary>
public string EntityFriendlyUrl { get; set; }
/// <summary>
/// entity image id
/// </summary>
public Guid EntityImageId { get; set; }
/// <summary>
/// review result
/// </summary>
public ReviewResult ReviewResult { get; set; }
/// <summary>
/// Synchronized with ganjoor
/// </summary>
public bool Synchronized { get; set; }
/// <summary>
/// suggested by
/// </summary>
public PublicRAppUser SuggestedBy { get; set; }
}
}

View File

@ -0,0 +1,35 @@
using System;
namespace RMuseum.Models.GanjoorIntegration.ViewModels
{
/// <summary>
/// Link Suggestion
/// </summary>
public class LinkSuggestion
{
/// <summary>
/// Ganjoor Post Id
/// </summary>
public int GanjoorPostId { get; set; }
/// <summary>
/// ganjoor url
/// </summary>
public string GanjoorUrl { get; set; }
/// <summary>
/// ganjoor title
/// </summary>
public string GanjoorTitle { get; set; }
/// <summary>
/// Artifact Friendly Url
/// </summary>
public string ArtifactFriendlyUrl { get; set; }
/// <summary>
/// Artifact Item Id
/// </summary>
public Guid? ItemId { get; set; }
}
}

View File

@ -0,0 +1,142 @@
using System;
namespace RMuseum.Models.GanjoorIntegration.ViewModels
{
/// <summary>
/// Safe pinterest link view model
/// </summary>
public class PinterestLinkViewModel
{
/// <summary>
/// cosntructor
/// </summary>
/// <param name="src"></param>
/// <param name="entityFriendlyUrl"></param>
/// <param name="entityImageId"></param>
/// <param name="entityName"></param>
public PinterestLinkViewModel(PinterestLink src, string entityName, string entityFriendlyUrl, Guid entityImageId)
{
Id = src.Id;
GanjoorPostId = src.GanjoorPostId;
GanjoorUrl = src.GanjoorUrl;
GanjoorTitle = src.GanjoorTitle;
AltText = src.AltText;
LinkType = src.LinkType;
PinterestImageUrl = src.PinterestImageUrl;
PinterestUrl = src.PinterestUrl;
SuggestionDate = src.SuggestionDate;
ReviewerId = src.ReviewerId;
ReviewDate = src.ReviewDate;
ReviewResult = src.ReviewResult;
ReviewDesc = src.ReviewDesc;
ArtifactId = src.ArtifactId;
ItemId = src.ItemId;
Synchronized = src.Synchronized;
EntityName = entityName;
EntityFriendlyUrl = entityFriendlyUrl;
EntityImageId = entityImageId;
}
/// <summary>
/// Id
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Ganjoor Post Id
/// </summary>
public int GanjoorPostId { get; set; }
/// <summary>
/// ganjoor url
/// </summary>
public string GanjoorUrl { get; set; }
/// <summary>
/// ganjoor title
/// </summary>
public string GanjoorTitle { get; set; }
/// <summary>
/// alt text
/// </summary>
public string AltText { get; set; }
/// <summary>
/// link type
/// </summary>
public LinkType LinkType { get; set; }
/// <summary>
/// pinterest url
/// </summary>
public string PinterestUrl { get; set; }
/// <summary>
/// pinterest image url
/// </summary>
public string PinterestImageUrl { get; set; }
/// <summary>
/// Suggestion Date
/// </summary>
public DateTime SuggestionDate { get; set; }
/// <summary>
/// User id who reviewed the link
/// </summary>
public Guid? ReviewerId { get; set; }
/// <summary>
/// Review Date
/// </summary>
public DateTime ReviewDate { get; set; }
/// <summary>
/// review result
/// </summary>
public ReviewResult ReviewResult { get; set; }
/// <summary>
/// review description
/// </summary>
public string ReviewDesc { get; set; }
/// <summary>
/// Artifact Id
/// </summary>
public Guid? ArtifactId { get; set; }
/// <summary>
/// Artifact Item Id
/// </summary>
public Guid? ItemId { get; set; }
/// <summary>
/// Synchronized with ganjoor
/// </summary>
public bool Synchronized { get; set; }
/// <summary>
/// entity name
/// </summary>
public string EntityName { get; set; }
/// <summary>
/// entity friendly url
/// </summary>
public string EntityFriendlyUrl { get; set; }
/// <summary>
/// entity image id
/// </summary>
public Guid EntityImageId { get; set; }
}
}

View File

@ -0,0 +1,55 @@
using System;
namespace RMuseum.Models.GanjoorIntegration.ViewModels
{
/// <summary>
/// pinterest / ganjoor link suggestion view model
/// </summary>
public class PinterestSuggestion
{
/// <summary>
/// Ganjoor Post Id
/// </summary>
public int GanjoorPostId { get; set; }
/// <summary>
/// ganjoor url
/// </summary>
public string GanjoorUrl { get; set; }
/// <summary>
/// ganjoor title
/// </summary>
public string GanjoorTitle { get; set; }
/// <summary>
/// alt text
/// </summary>
public string AltText { get; set; }
/// <summary>
/// link type
/// </summary>
public LinkType LinkType { get; set; }
/// <summary>
/// pinterest url
/// </summary>
public string PinterestUrl { get; set; }
/// <summary>
/// pinterest image url
/// </summary>
public string PinterestImageUrl { get; set; }
/// <summary>
/// Captcha Image Id
/// </summary>
public Guid CaptchaImageId { get; set; }
/// <summary>
/// Captcha Value
/// </summary>
public string CaptchaValue { get; set; }
}
}

View File

@ -0,0 +1,39 @@
using RMuseum.Models.Artifact;
using System;
namespace RMuseum.Models.ImportJob
{
/// <summary>
/// Import Job
/// </summary>
public class ImportJob
{
public Guid Id { get; set; }
public JobType JobType { get; set; }
public string ResourceNumber { get; set; }
public string FriendlyUrl { get; set; }
public string SrcUrl { get; set; }
public DateTime QueueTime { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public ImportJobStatus Status { get; set; }
public decimal ProgressPercent { get; set; }
public string Exception { get; set; }
public virtual RArtifactMasterRecord Artifact { get; set; }
public Guid? ArtifactId { get; set; }
public string SrcContent { get; set; }
}
}

View File

@ -0,0 +1,13 @@

namespace RMuseum.Models.ImportJob
{
public enum ImportJobStatus
{
NotStarted = 0,
Running = 1,
Succeeded =2,
Failed = 3,
Aborted = 4
}
}

View File

@ -0,0 +1,21 @@
namespace RMuseum.Models.ImportJob
{
/// <summary>
/// Job Type
/// </summary>
public enum JobType
{
Pdf = 0,
Loc = 1,
Princeton = 2,
Harvard = 3,
HarvardDirect = 4,
HathiTrust = 5,
PennLibraries = 6,
Cambridge = 7,
BritishLibrary = 8,
ServerFolder = 9,
Walters = 10,
ChesterBeatty = 11
}
}

View File

@ -0,0 +1,17 @@
namespace RMuseum.Models.Note
{
/// <summary>
/// Note Type
/// </summary>
public enum RNoteType
{
/// <summary>
/// private
/// </summary>
Private = 0,
/// <summary>
/// public
/// </summary>
Public = 1
}
}

View File

@ -0,0 +1,87 @@
using RMuseum.Models.Artifact;
using RSecurityBackend.Models.Auth.Db;
using System;
namespace RMuseum.Models.Note
{
/// <summary>
/// User Note
/// </summary>
public class RUserNote
{
/// <summary>
/// Id
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// User Id
/// </summary>
public Guid RAppUserId { get; set; }
/// <summary>
/// User
/// </summary>
public RAppUser RAppUser { get; set; }
/// <summary>
/// Master Record Id
/// </summary>
public Guid? RArtifactMasterRecordId { get; set; }
/// <summary>
/// Master Record
/// </summary>
public virtual RArtifactMasterRecord RArtifactMasterRecord { get; set; }
/// <summary>
/// Item Record Id
/// </summary>
public Guid? RArtifactItemRecordId { get; set; }
/// <summary>
/// Item Record
/// </summary>
public virtual RArtifactItemRecord RArtifactItemRecord { get; set; }
/// <summary>
/// DateTime
/// </summary>
public DateTime DateTime { get; set; }
/// <summary>
/// Is Updated by User
/// </summary>
public bool Modified { get; set; }
/// <summary>
/// Last Modified
/// </summary>
public DateTime LastModified { get; set; }
/// <summary>
/// Private / Public
/// </summary>
public RNoteType NoteType { get; set; }
/// <summary>
/// content
/// </summary>
public string HtmlContent { get; set; }
/// <summary>
/// In Reply to Other Note
/// </summary>
public virtual RUserNote ReferenceNote { get; set; }
/// <summary>
/// Reference Note Id
/// </summary>
public Guid? ReferenceNoteId { get; set; }
/// <summary>
/// Status
/// </summary>
public PublishStatus Status { get; set; }
}
}

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace RMuseum.Models.Note.ViewModels
{
/// <summary>
/// New User Note For Artifact
/// </summary>
public class PostUserNote
{
/// <summary>
/// Artifact / Item Id
/// </summary>
public Guid EntityId { get; set; }
/// <summary>
/// Note Type
/// </summary>
public RNoteType NoteType { get; set; }
/// <summary>
/// Contents
/// </summary>
public string Contents { get; set; }
/// <summary>
/// Reference Note Id
/// </summary>
public Guid? ReferenceNoteId { get; set; }
}
}

View File

@ -0,0 +1,134 @@
using RMuseum.Models.Artifact;
using RSecurityBackend.Models.Auth.ViewModels;
using System;
using System.Globalization;
namespace RMuseum.Models.Note.ViewModels
{
/// <summary>
/// Safe User Note View Model
/// </summary>
public class RUserNoteViewModel
{
/// <summary>
/// Constructor
/// </summary>
/// <param name="src"></param>
/// <param name="user"></param>
public RUserNoteViewModel(RUserNote src, PublicRAppUser user)
{
Id = src.Id;
RAppUserId = src.RAppUserId;
UserName = (user.FirstName + " " + user.SureName);
RUserImageId = user.RImageId;
Modified = src.Modified;
NoteType = src.NoteType;
HtmlContent = src.HtmlContent;
ReferenceNoteId = src.ReferenceNoteId;
Status = src.Status;
Notes = new RUserNoteViewModel[] { };
PersianCalendar pc = new PersianCalendar();
DateTime = $"{pc.GetYear(src.DateTime)}/{pc.GetMonth(src.DateTime)}/{pc.GetDayOfMonth(src.DateTime)}&nbsp;{pc.GetHour(src.DateTime)}:{pc.GetMinute(src.DateTime)}";
LastModified = $"{pc.GetYear(src.LastModified)}/{pc.GetMonth(src.LastModified)}/{pc.GetDayOfMonth(src.LastModified)}&nbsp;{pc.GetHour(src.LastModified)}:{pc.GetMinute(src.LastModified)}";
}
/// <summary>
/// Id
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// User Id
/// </summary>
public Guid RAppUserId { get; set; }
/// <summary>
/// User Name (FirstName + SureName)
/// </summary>
public string UserName { get; set; }
/// <summary>
/// user image id
/// </summary>
public Guid? RUserImageId { get; set; }
/// <summary>
/// DateTime
/// </summary>
public string DateTime { get; set; }
/// <summary>
/// Is Updated by User
/// </summary>
public bool Modified { get; set; }
/// <summary>
/// Last Modified
/// </summary>
public string LastModified { get; set; }
/// <summary>
/// Private / Public
/// </summary>
public RNoteType NoteType { get; set; }
/// <summary>
/// content
/// </summary>
public string HtmlContent { get; set; }
/// <summary>
/// Reference Note Id
/// </summary>
public Guid? ReferenceNoteId { get; set; }
/// <summary>
/// Status
/// </summary>
public PublishStatus Status { get; set; }
/// <summary>
/// Related Entity (Artifact/Item) Name
/// </summary>
/// <remarks>
/// It is set only in cummulative api calls
/// </remarks>
public string RelatedEntityName { get; set; }
/// <summary>
/// Realated entity Image Id
/// </summary>
/// <remarks>
/// It is set only in cummulative api calls
/// </remarks>
public Guid RelatedEntityImageId { get; set; }
/// <summary>
/// Realated entity Friendly Url (for artifact items it would be ParentFiendlyUrl/ItemFriendlyUrl )
/// </summary>
/// <remarks>
/// It is set only in cummulative api calls
/// </remarks>
public string RelatedEntityFriendlyUrl { get; set; }
/// <summary>
/// Related Item Artifact Name
/// </summary>
/// <remarks>
/// It is set only in cummulative api calls
/// </remarks>
public string RelatedItemParentName { get; set; }
/// <summary>
/// child notes
/// </summary>
/// <remarks>
/// It os set only in certain cases
/// </remarks>
public RUserNoteViewModel[] Notes { get; set; }
}
}

View File

@ -0,0 +1,13 @@
namespace RMuseum.Models.Notification
{
/// <summary>
/// Notifaction Status
/// </summary>
public enum NotificationStatus
{
Unread = 0,
Read = 1,
Archived = 2,
Deleted = 3
}
}

View File

@ -0,0 +1,46 @@
using RSecurityBackend.Models.Auth.Db;
using System;
namespace RMuseum.Models.Notification
{
/// <summary>
/// User Notification
/// </summary>
public class RUserNotification
{
/// <summary>
/// Id
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// User Id
/// </summary>
public Guid UserId { get; set; }
/// <summary>
/// User
/// </summary>
public RAppUser User { get; set; }
/// <summary>
/// DateTime
/// </summary>
public DateTime DateTime { get; set; }
/// <summary>
/// Status
/// </summary>
public NotificationStatus Status { get; set; }
/// <summary>
/// Subject
/// </summary>
public string Subject { get; set; }
/// <summary>
/// Text
/// </summary>
public string HtmlText { get; set; }
}
}

20
RMuseum/Program.cs Normal file
View File

@ -0,0 +1,20 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace RMuseum
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is used by the publish/package process of your Web project. You can customize the behavior of this process
by editing this MSBuild file. In order to learn more about this please visit https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WebPublishMethod>FileSystem</WebPublishMethod>
<PublishProvider>FileSystem</PublishProvider>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish />
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
<ExcludeApp_Data>False</ExcludeApp_Data>
<TargetFramework>netcoreapp3.1</TargetFramework>
<ProjectGuid>089caebe-24cf-438a-80f9-7e457a6e77d2</ProjectGuid>
<SelfContained>false</SelfContained>
<publishUrl>bin\Release\netcoreapp3.1\publish\</publishUrl>
<DeleteExistingFiles>True</DeleteExistingFiles>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,29 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:3439",
"sslPort": 0
}
},
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"RMuseum": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "api/values",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:5000"
}
}
}

40
RMuseum/RMuseum.csproj Normal file
View File

@ -0,0 +1,40 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>RMuseum.xml</DocumentationFile>
<NoWarn>1701;1702;1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Audit.WebApi.Core" Version="15.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.2.1" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="5.2.1" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="5.0.2" />
</ItemGroup>
<ItemGroup>
<Folder Include="Swashbuckle\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\RSecurityBackend\RSecurityBackend.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="RMuseum.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

3797
RMuseum/RMuseum.xml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,640 @@
using Microsoft.AspNetCore.Http;
using RMuseum.Models.Artifact;
using RMuseum.Models.Artifact.ViewModels;
using RMuseum.Models.Bookmark;
using RMuseum.Models.Bookmark.ViewModels;
using RMuseum.Models.GanjoorIntegration;
using RMuseum.Models.GanjoorIntegration.ViewModels;
using RMuseum.Models.ImportJob;
using RMuseum.Models.Note;
using RMuseum.Models.Note.ViewModels;
using RMuseum.Models.Notification;
using RSecurityBackend.Models.Generic;
using System;
using System.IO;
using System.Threading.Tasks;
namespace RMuseum.Services
{
/// <summary>
/// Artifact Item Service
/// </summary>
public interface IArtifactService
{
/// <summary>
/// get all artifacts (including CoverImage info but not items or attributes info)
/// </summary>
/// <param name="paging"></param>
/// <param name="statusArray"></param>
/// <returns></returns>
Task<RServiceResult<(PaginationMetadata PagingMeta, RArtifactMasterRecord[] Items)>> GetAll(PagingParameterModel paging, PublishStatus[] statusArray);
/// <summary>
/// get tagged publish artifacts (including CoverImage info but not items or tagibutes info)
/// </summary>
/// <param name="tagUrl"></param>
/// <param name="valueUrl"></param>
/// <param name="statusArray"></param>
/// <returns></returns>
Task<RServiceResult<RArtifactMasterRecord[]>> GetByTagValue(string tagUrl, string valueUrl, PublishStatus[] statusArray);
/// <summary>
/// gets specified artifact info (including CoverImage + images + attributes)
/// </summary>
/// <param name="friendlyUrl"></param>
/// <param name="statusArray"></param>
/// <returns></returns>
Task<RServiceResult<RArtifactMasterRecordViewModel>> GetByFriendlyUrl(string friendlyUrl, PublishStatus[] statusArray);
/// <summary>
/// gets specified artifact info (including CoverImage + images + tagibutes)
/// </summary>
/// <param name="friendlyUrl"></param>
/// <param name="statusArray"></param>
/// <param name="tagFriendlyUrl"></param>
/// <param name="tagValueFriendlyUrl"></param>
/// <returns></returns>
Task<RServiceResult<RArtifactMasterRecordViewModel>> GetByFriendlyUrlFilterItemsByTag(string friendlyUrl, PublishStatus[] statusArray, string tagFriendlyUrl, string tagValueFriendlyUrl);
/// <summary>
/// edit master record
/// </summary>
/// <param name="edited"></param>
/// <param name="canChangeStatusToAwaiting"></param>
/// <param name="canPublish"></param>
/// <returns></returns>
Task<RServiceResult<RArtifactMasterRecord>> EditMasterRecord(RArtifactMasterRecord edited, bool canChangeStatusToAwaiting, bool canPublish);
/// <summary>
/// Set Artifact Cover Item Index
/// </summary>
/// <param name="artifactId"></param>
/// <param name="itemIndex"></param>
/// <returns></returns>
Task<RServiceResult<bool>> SetArtifactCoverItemIndex(Guid artifactId, int itemIndex);
/// <summary>
/// get tag bundle by frindly url
/// </summary>
/// <param name="friendlyUrl"></param>
/// <returns></returns>
Task<RServiceResult<RTagBundleViewModel>> GetTagBundleByFiendlyUrl(string friendlyUrl);
/// <summary>
/// get max lastmodified artifact date for caching purposes
/// </summary>
/// <returns></returns>
Task<RServiceResult<DateTime>> GetMaxArtifactLastModified();
/// <summary>
/// get all tags
/// </summary>
/// <param name="paging"></param>
/// <returns></returns>
Task<RServiceResult<(PaginationMetadata PagingMeta, RTag[] Items)>> GetAllTags(PagingParameterModel paging);
/// <summary>
/// get tag bu friendly url
/// </summary>
/// <param name="friendlyUrl"></param>
/// <returns></returns>
Task<RServiceResult<RTag>> GetTagByFriendlyUrl(string friendlyUrl);
/// <summary>
/// add tag
/// </summary>
/// <returns></returns>
Task<RServiceResult<RTag>> AddTag(string tagName);
/// <summary>
/// edit tag
/// </summary>
/// <param name="edited"></param>
/// <returns></returns>
Task<RServiceResult<RTag>> EditTag(RTag edited);
/// <summary>
/// changes order of tags based on their position in artifacts
/// </summary>
/// <param name="tagId"></param>
/// <param name="artifactId"></param>
/// <param name="up">true => up, false => down</param>
/// <returns>the other tag which its Order has been changed</returns>
Task<RServiceResult<Guid?>> EditTagOrderBasedOnArtifact(Guid tagId, Guid artifactId, bool up);
/// <summary>
/// changes order of tags based on their position in artifact items
/// </summary>
/// <param name="tagId"></param>
/// <param name="itemId"></param>
/// <param name="up">true => up, false => down</param>
/// <returns>the other tag which its Order has been changed</returns>
Task<RServiceResult<Guid?>> EditTagOrderBasedOnItem(Guid tagId, Guid itemId, bool up);
/// <summary>
/// add artifact tag value
/// </summary>
/// <param name="artifactId"></param>
/// <param name="tag"></param>
/// <returns></returns>
Task<RServiceResult<RTagValue>> TagArtifact(Guid artifactId, RTag tag);
/// <summary>
/// changes order of tag values based on their position in an artifact
/// </summary>
/// <param name="artifactId"></param>
/// <param name="tagId"></param>
/// <param name="valueId"></param>
/// <param name="up">true => up, false => down</param>
/// <returns>the other tag value which its Order has been changed</returns>
Task<RServiceResult<Guid?>> EditTagValueOrder(Guid artifactId, Guid tagId, Guid valueId, bool up);
/// <summary>
/// changes order of tag values based on their position in an item
/// </summary>
/// <param name="itemId"></param>
/// <param name="tagId"></param>
/// <param name="valueId"></param>
/// <param name="up">true => up, false => down</param>
/// <returns>the other tag value which its Order has been changed</returns>
Task<RServiceResult<Guid?>> EditItemTagValueOrder(Guid itemId, Guid tagId, Guid valueId, bool up);
/// <summary>
/// get tag value bundle by frindly url
/// </summary>
/// <param name="friendlyUrl"></param>
/// <param name="valueUrl"></param>
/// <returns></returns>
Task<RServiceResult<RArtifactTagViewModel>> GetTagValueBundleByFiendlyUrl(string friendlyUrl, string valueUrl);
/// <summary>
/// get tag value by frindly url
/// </summary>
/// <param name="tagId"></param>
/// <param name="friendlyUrl"></param>
/// <returns></returns>
Task<RServiceResult<RTagValue>> GetTagValueByFriendlyUrl(Guid tagId, string friendlyUrl);
/// <summary>
/// edit artifact tagibute value
/// </summary>
/// <param name="artifactId"></param>
/// <param name="edited"></param>
/// <param name="global">apply on all same value tags</param>
/// <returns></returns>
Task<RServiceResult<RTagValue>> EditTagValue(Guid artifactId, RTagValue edited, bool global);
/// <summary>
/// remove artfiact tag value
/// </summary>
/// <param name="artifactId"></param>
/// <param name="tagValueId"></param>
/// <returns></returns>
Task<RServiceResult<bool>> UnTagArtifact(Guid artifactId, Guid tagValueId);
/// <summary>
/// add item tag value
/// </summary>
/// <param name="itemId"></param>
/// <param name="tag"></param>
/// <returns></returns>
Task<RServiceResult<RTagValue>> TagItem(Guid itemId, RTag tag);
/// <summary>
/// edit item tag value
/// </summary>
/// <param name="itemId"></param>
/// <param name="edited"></param>
/// <param name="global">apply on all same value tags</param>
/// <returns></returns>
Task<RServiceResult<RTagValue>> EditItemTagValue(Guid itemId, RTagValue edited, bool global);
/// <summary>
/// remove item tag value
/// </summary>
/// <param name="itemId"></param>
/// <param name="tagValueId"></param>
/// <returns></returns>
Task<RServiceResult<bool>> UnTagItem(Guid itemId, Guid tagValueId);
/// <summary>
/// gets specified artifact item info (including images + attributes)
/// </summary>
/// <param name="artifactUrl"></param>
/// <param name="itemUrl"></param>
/// <param name="statusArray"></param>
/// <returns></returns>
Task<RServiceResult<RArtifactItemRecordViewModel>> GetArtifactItemByFrienlyUrl(string artifactUrl, string itemUrl, PublishStatus[] statusArray);
/// <summary>
/// add new artifact
/// </summary>
/// <param name="name"></param>
/// <param name="description"></param>
/// <param name="srcurl"></param>
/// <param name="pictitle"></param>
/// <param name="picdescription"></param>
/// <param name="file"></param>
/// <param name="picsrcurl"></param>
/// <param name="stream"></param>
/// <returns></returns>
Task<RServiceResult<RArtifactMasterRecord>> Add
(
string name, string description, string srcurl,
string pictitle, string picdescription, IFormFile file, string picsrcurl, Stream stream
);
/// <summary>
/// for now it only supports https://www.loc.gov
/// </summary>
/// <param name="resourceNumber">
/// <example>
/// m084
/// </example>
/// </param>
/// <param name="fiendlyUrl">
/// <example>
/// boostan1207
/// </example>
/// </param>
/// <param name="resourcePrefix"></param>
/// <example>
/// plmp
/// </example>
/// <returns></returns>
Task<RServiceResult<bool>> StartImportingFromTheLibraryOfCongress(string resourceNumber, string fiendlyUrl, string resourcePrefix);
/// <summary>
/// from http://pudl.princeton.edu/
/// </summary>
/// <param name="resourceNumber">dj52w476m</param>
/// <param name="friendlyUrl"></param>
/// <returns></returns>
Task<RServiceResult<bool>> StartImportingFromPrinceton(string resourceNumber, string friendlyUrl);
/// <summary>
/// from https://curiosity.lib.harvard.edu
/// </summary>
/// <param name="url">example: https://curiosity.lib.harvard.edu/islamic-heritage-project/catalog/40-990114893240203941</param>
/// <param name="friendlyUrl"></param>
/// <returns></returns>
Task<RServiceResult<bool>> StartImportingFromHarvard(string url, string friendlyUrl);
/// <summary>
/// import from http://www.qajarwomen.org
/// </summary>
/// <param name="hardvardResourceNumber">43117279</param>
/// <param name="friendlyUrl">atame</param>
/// <param name="srcUrl">http://www.qajarwomen.org/fa/items/1018A10.html</param>
/// <returns></returns>
Task<RServiceResult<bool>> StartImportingFromHarvardDirectly(string hardvardResourceNumber, string friendlyUrl, string srcUrl);
/// <summary>
/// from https://catalog.hathitrust.org
/// </summary>
/// <param name="resourceNumber">006814127</param>
/// <param name="friendlyUrl"></param>
/// <returns></returns>
Task<RServiceResult<bool>> StartImportingFromHathiTrust(string resourceNumber, string friendlyUrl);
/// <summary>
/// from http://www.library.upenn.edu/
/// </summary>
/// <param name="resourceNumber">MEDREN_9949222153503681</param>
/// <param name="friendlyUrl"></param>
/// <returns></returns>
Task<RServiceResult<bool>> StartImportingFromPenLibraries(string resourceNumber, string friendlyUrl);
/// <summary>
/// from http://cudl.lib.cam.ac.uk
/// </summary>
/// <param name="resourceNumber">MS-RAS-00258</param>
/// <param name="friendlyUrl"></param>
/// <returns></returns>
Task<RServiceResult<bool>> StartImportingFromCambridge(string resourceNumber, string friendlyUrl);
/// <summary>
/// from http://www.bl.uk
/// </summary>
/// <param name="resourceNumber">grenville_xli_f001r</param>
/// <param name="friendlyUrl"></param>
/// <returns></returns>
Task<RServiceResult<bool>> StartImportingFromBritishLibrary(string resourceNumber, string friendlyUrl);
/// <summary>
/// import from server folder
/// </summary>
/// <param name="folderPath">C:\Tools\batches\florence</param>
/// <param name="friendlyUrl">shahname-florence</param>
/// <param name="srcUrl">https://t.me/dr_khatibi_abolfazl/888</param>
/// <returns></returns>
Task<RServiceResult<bool>> StartImportingFromServerFolder(string folderPath, string friendlyUrl, string srcUrl);
/// <summary>
/// from http://www.thedigitalwalters.org/01_ACCESS_WALTERS_MANUSCRIPTS.html
/// </summary>
/// <param name="resourceNumber">W619</param>
/// <param name="friendlyUrl">golestan-walters-01</param>
/// <returns></returns>
Task<RServiceResult<bool>> StartImportingFromWalters(string resourceNumber, string friendlyUrl);
/// <summary>
/// import from https://viewer.cbl.ie
/// </summary>
/// <param name="resourceNumber">119</param>
/// <param name="friendlyUrl">golestan-baysonghori</param>
/// <returns></returns>
Task<RServiceResult<bool>> StartImportingFromChesterBeatty(string resourceNumber, string friendlyUrl);
/// <summary>
/// reschedule jobs
/// </summary>
/// <returns></returns>
Task<RServiceResult<bool>> RescheduleJobs();
/// <summary>
/// due to a bug in loc json outputs some artifacts with more than 1000 pages were downloaded incompletely
/// </summary>
/// <returns></returns>
Task<RServiceResult<string[]>> ReExamineLocDownloads();
/// <summary>
/// import jobs
/// </summary>
/// <param name="paging"></param>
/// <returns></returns>
Task<RServiceResult<(PaginationMetadata PagingMeta, ImportJob[] Jobs)>> GetImportJobs(PagingParameterModel paging);
/// <summary>
/// Bookmark Artifact
/// </summary>
/// <param name="artifactId"></param>
/// <param name="userId"></param>
/// <param name="type"></param>
/// <returns></returns>
Task<RServiceResult<RUserBookmark>> BookmarkArtifact(Guid artifactId, Guid userId, RBookmarkType type);
/// <summary>
/// get artifact user bookmarks
/// </summary>
/// <param name="artifactId"></param>
/// <param name="userId"></param>
/// <returns></returns>
Task<RServiceResult<RUserBookmark[]>> GetArtifactUserBookmarks(Guid artifactId, Guid userId);
/// <summary>
/// Bookmark Item
/// </summary>
/// <param name="itemId"></param>
/// <param name="userId"></param>
/// <param name="type"></param>
/// <returns></returns>
Task<RServiceResult<RUserBookmark>> BookmarkItem(Guid itemId, Guid userId, RBookmarkType type);
/// <summary>
/// get item user bookmarks
/// </summary>
/// <param name="itemId"></param>
/// <param name="userId"></param>
/// <returns></returns>
Task<RServiceResult<RUserBookmark[]>> GeItemUserBookmarks(Guid itemId, Guid userId);
/// <summary>
/// update bookmark note
/// </summary>
/// <param name="bookmarkId"></param>
/// <param name="note"></param>
/// <returns></returns>
Task<RServiceResult<bool>> UpdateUserBookmark(Guid bookmarkId, string note);
/// <summary>
/// delete user bookmark
/// /// </summary>
/// <param name="bookmarkId"></param>
/// <returns></returns>
Task<RServiceResult<bool>> DeleteUserBookmark(Guid bookmarkId);
/// <summary>
/// get user bookmarks (artifacts and items)
/// </summary>
/// <param name="paging"></param>
/// <param name="userId"></param>
/// <param name="type"></param>
/// <param name="statusArray"></param>
/// <returns></returns>
Task<RServiceResult<(PaginationMetadata PagingMeta, RUserBookmarkViewModel[] Bookmarks)>> GetBookmarks(PagingParameterModel paging, Guid userId, RBookmarkType type, PublishStatus[] statusArray);
/// <summary>
/// Add Note to Artifact
/// </summary>
/// <param name="artifactId"></param>
/// <param name="userId"></param>
/// <param name="type"></param>
/// <param name="noteContents"></param>
/// <param name="referenceNoteId"></param>
/// <returns></returns>
Task<RServiceResult<RUserNoteViewModel>> AddUserNoteToArtifact(Guid artifactId, Guid userId, RNoteType type, string noteContents, Guid? referenceNoteId);
/// <summary>
/// Add Note to Artifact Item
/// </summary>
/// <param name="itemId"></param>
/// <param name="userId"></param>
/// <param name="type"></param>
/// <param name="noteContents"></param>
/// <param name="referenceNoteId"></param>
/// <returns></returns>
Task<RServiceResult<RUserNoteViewModel>> AddUserNoteToArtifactItem(Guid itemId, Guid userId, RNoteType type, string noteContents, Guid? referenceNoteId);
/// <summary>
/// Edit Note
/// </summary>
/// <param name="noteId"></param>
/// <param name="userId">sending null here means user is a moderator</param>
/// <param name="noteContents"></param>
/// <returns></returns>
Task<RServiceResult<RUserNoteViewModel>> EditUserNote(Guid noteId, Guid? userId, string noteContents);
/// <summary>
/// delete note
/// </summary>
/// <param name="noteId"></param>
/// <param name="userId">sending null here means user is a moderator or the note is being deleted in a recursive delete of referenced notes</param>
/// <returns>list of notes deleted</returns>
Task<RServiceResult<Guid[]>> DeleteUserNote(Guid noteId, Guid? userId);
/// <summary>
/// get artifact private user notes
/// </summary>
/// <param name="artifactId"></param>
/// <param name="userId"></param>
/// <returns></returns>
Task<RServiceResult<RUserNoteViewModel[]>> GetArtifactUserNotes(Guid artifactId, Guid userId);
/// <summary>
/// get artifact public user notes
/// </summary>
/// <param name="artifactId"></param>
/// <returns></returns>
Task<RServiceResult<RUserNoteViewModel[]>> GetArtifactPublicNotes(Guid artifactId);
/// <summary>
/// get item artifact private user notes
/// </summary>
/// <param name="itemId"></param>
/// <param name="userId"></param>
/// <returns></returns>
Task<RServiceResult<RUserNoteViewModel[]>> GetArtifactItemUserNotes(Guid itemId, Guid userId);
/// <summary>
/// get item artifact item public user notes
/// </summary>
/// <param name="itemId"></param>
/// <returns></returns>
Task<RServiceResult<RUserNoteViewModel[]>> GetArtifactItemPublicNotes(Guid itemId);
/// <summary>
/// Get All USer Notes
/// </summary>
/// <param name="userId"></param>
/// <param name="paging"></param>
/// <param name="type"></param>
/// <param name="statusArray"></param>
/// <returns></returns>
Task<RServiceResult<(PaginationMetadata PagingMeta, RUserNoteViewModel[] Notes)>> GetUserNotes(Guid userId, PagingParameterModel paging, RNoteType type, PublishStatus[] statusArray);
/// <summary>
/// Get All Public Notes
/// </summary>
/// <param name="paging"></param>
/// <returns></returns>
Task<RServiceResult<(PaginationMetadata PagingMeta, RUserNoteViewModel[] Notes)>> GetAllPublicNotes(PagingParameterModel paging);
/// <summary>
/// suggest ganjoor link
/// </summary>
/// <param name="userId"></param>
/// <param name="link"></param>
/// <returns></returns>
Task<RServiceResult<GanjoorLinkViewModel>> SuggestGanjoorLink(Guid userId, LinkSuggestion link);
/// <summary>
/// get suggested ganjoor links
/// </summary>
/// <param name="status"></param>
/// <param name="notSynced"></param>
/// <returns></returns>
Task<RServiceResult<GanjoorLinkViewModel[]>> GetSuggestedLinks(ReviewResult status, bool notSynced);
/// <summary>
/// Review Suggested Link
/// </summary>
/// <param name="linkId"></param>
/// <param name="userId"></param>
/// <param name="result"></param>
/// <returns></returns>
Task<RServiceResult<bool>> ReviewSuggestedLink(Guid linkId, Guid userId, ReviewResult result);
/// <summary>
/// Temporary api
/// </summary>
/// <returns></returns>
Task<RServiceResult<string[]>> AddTOCForSuggestedLinks();
/// <summary>
/// Synchronize suggested link
/// </summary>
/// <param name="linkId"></param>
/// <returns></returns>
Task<RServiceResult<bool>> SynchronizeSuggestedLink(Guid linkId);
/// <summary>
/// get suggested pinterest links
/// </summary>
/// <param name="status"></param>
/// <param name="notSynced"></param>
/// <returns></returns>
Task<RServiceResult<PinterestLinkViewModel[]>> GetSuggestedPinterestLinks(ReviewResult status, bool notSynced);
/// <summary>
/// suggest pinterest link
/// </summary>
/// <param name="suggestion"></param>
/// <returns></returns>
Task<RServiceResult<PinterestLinkViewModel>> SuggestPinterestLink(PinterestSuggestion suggestion);
/// <summary>
/// Review Suggested Pinterest Link
/// </summary>
/// <param name="linkId"></param>
/// <param name="userId"></param>
/// <param name="altText"></param>
/// <param name="result"></param>
/// <param name="reviewDesc"></param>
/// <param name="imageUrl"></param>
/// <returns></returns>
Task<RServiceResult<bool>> ReviewSuggestedPinterestLink(Guid linkId, Guid userId, string altText, ReviewResult result, string reviewDesc, string imageUrl);
/// <summary>
/// Synchronize suggested pinterest link
/// </summary>
/// <param name="linkId"></param>
/// <returns></returns>
Task<RServiceResult<bool>> SynchronizeSuggestedPinterestLink(Guid linkId);
/// <summary>
/// Add Notification
/// </summary>
/// <param name="userId"></param>
/// <param name="subject"></param>
/// <param name="htmlText"></param>
/// <returns></returns>
Task<RServiceResult<RUserNotification>> PushNotification(Guid userId, string subject, string htmlText);
/// <summary>
/// Get User Notifications
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
Task<RServiceResult<RUserNotification[]>> GetUserNotifications(Guid userId);
/// <summary>
/// Get Unread User Notifications Count
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
Task<RServiceResult<int>> GetUnreadUserNotificationsCount(Guid userId);
/// <summary>
/// Switch Notification Status
/// </summary>
/// <param name="notificationId"></param>
/// <returns>updated notification object</returns>
Task<RServiceResult<RUserNotification>> SwitchNotificationStatus(Guid notificationId);
/// <summary>
/// Delete Notification
/// </summary>
/// <param name="notificationId"></param>
/// <returns></returns>
Task<RServiceResult<bool>> DeleteNotification(Guid notificationId);
/// <summary>
/// an incomplete prototype for removing artifacts
/// </summary>
/// <param name="artifactId"></param>
/// <param name="checkJobs"></param>
/// <returns></returns>
Task<RServiceResult<bool>> RemoveArtifactHavingNoNoteAndBookmarks(Guid artifactId, bool checkJobs);
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace RMuseum.Services
{
public interface IBackgroundTaskQueue
{
void QueueBackgroundWorkItem(Func<CancellationToken, Task> workItem);
Task<Func<CancellationToken, Task>> DequeueAsync(
CancellationToken cancellationToken);
}
}

View File

@ -0,0 +1,101 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using RMuseum.Models.Artifact;
using RSecurityBackend.Models.Generic;
using RSecurityBackend.Models.Image;
using System;
using System.Drawing.Imaging;
using System.IO;
using System.Threading.Tasks;
namespace RMuseum.Services
{
/// <summary>
/// manipulating picture files
/// </summary>
public interface IPictureFileService
{
/// <summary>
/// add new picture
/// </summary>
/// <param name="title"></param>
/// <param name="description"></param>
/// <param name="order"></param>
/// <param name="file"></param>
/// <param name="srcurl"></param>
/// <param name="stream"></param>
/// <param name="originalFileNameForStreams"></param>
/// <param name="imageFolderName">pass empty if you want a generic date based folder</param>
/// <returns></returns>
Task<RServiceResult<RPictureFile>> Add
(
string title, string description, int order, IFormFile file, string srcurl, Stream stream, string originalFileNameForStreams, string imageFolderName
);
/// <summary>
/// recover from files
/// </summary>
/// <param name="title"></param>
/// <param name="description"></param>
/// <param name="order"></param>
/// <param name="srcurl"></param>
/// <param name="orignalFilePath"></param>
/// <param name="normalFilePath"></param>
/// <param name="thumbFilePath"></param>
/// <param name="originalFileNameForStreams"></param>
/// <param name="imageFolderName"></param>
/// <returns></returns>
Task<RServiceResult<RPictureFile>> RecoverFromeFiles
(
string title, string description, int order, string srcurl, string orignalFilePath, string normalFilePath, string thumbFilePath, string originalFileNameForStreams, string imageFolderName
);
/// <summary>
/// returns image info
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
Task<RServiceResult<RPictureFile>> GetImage(Guid id);
/// <summary>
/// returns image file stream
/// </summary>
/// <param name="image"></param>
/// <param name="sz"></param>
/// <returns></returns>
RServiceResult<string> GetImagePath(RPictureFile image, string sz = "orig");
/// <summary>
/// Rotate Image in 90 deg. multiplicants: 90, 180 or 270
/// </summary>
/// <param name="id"></param>
/// <param name="degIn90mul"></param>
/// <returns></returns>
Task<RServiceResult<RPictureFile>> RotateImage(Guid id, int degIn90mul);
/// <summary>
/// Get Encoder
/// </summary>
/// <param name="format"></param>
/// <returns></returns>
ImageCodecInfo GetEncoder(ImageFormat format);
/// <summary>
/// Generate Cropped Image Based On ThumbnailCoordinates For Notes
/// </summary>
/// <param name="id"></param>
/// <param name="left"></param>
/// <param name="top"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns></returns>
Task<RServiceResult<RImage>> GenerateCroppedImageBasedOnThumbnailCoordinates(Guid id, decimal left, decimal top, decimal width, decimal height);
/// <summary>
/// Image Storage Path
/// </summary>
string ImageStoragePath { get; }
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,258 @@
using RSecurityBackend.Models.Generic;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace RMuseum.Services.Implementation
{
/// <summary>
/// British Library Tile Downloader And Mixer
/// </summary>
public class BLTileMixer
{
/// <summary>
/// Constructor
/// </summary>
public BLTileMixer()
{
}
private Dictionary<(int x, int y), Image> _tiles = null;
public async Task<RServiceResult<Stream>> DownloadMix(string pageUrl, int order)
{
try
{
using (var client = new HttpClient())
{
client.Timeout = TimeSpan.FromMinutes(5);
_tiles = new Dictionary<(int x, int y), Image>();
int max_x = -1;
for (int x = 0; ; x++)
{
string imageUrl = $"http://www.bl.uk/manuscripts/Proxy.ashx?view={pageUrl}_files/13/{x}_0.jpg";
var imageResult = await client.GetAsync(imageUrl);
int _ImportRetryCount = 5;
int _ImportRetryInitialSleep = 500;
int retryCount = 0;
while (retryCount < _ImportRetryCount && !imageResult.IsSuccessStatusCode && imageResult.StatusCode == HttpStatusCode.ServiceUnavailable)
{
imageResult.Dispose();
Thread.Sleep(_ImportRetryInitialSleep * (retryCount + 1));
imageResult = await client.GetAsync(imageUrl);
retryCount++;
}
if (imageResult.IsSuccessStatusCode)
{
using (Stream imageStream = await imageResult.Content.ReadAsStreamAsync())
{
imageStream.Position = 0;
try
{
Image tile = Image.FromStream(imageStream);
_tiles.Add((x, 0), tile);
max_x = x;
}
catch (Exception aexp)
{
if (aexp is ArgumentException)
{
break;
}
throw aexp;
}
}
}
else
{
imageResult.Dispose();
return new RServiceResult<Stream>(null, $"Http result is not ok ({imageResult.StatusCode}) for page {order}, url {imageUrl}");
}
}
int max_y = -1;
for (int y = 1; ; y++)
{
string imageUrl = $"http://www.bl.uk/manuscripts/Proxy.ashx?view={pageUrl}_files/13/0_{y}.jpg";
var imageResult = await client.GetAsync(imageUrl);
int _ImportRetryCount = 5;
int _ImportRetryInitialSleep = 500;
int retryCount = 0;
while (retryCount < _ImportRetryCount && !imageResult.IsSuccessStatusCode && imageResult.StatusCode == HttpStatusCode.ServiceUnavailable)
{
imageResult.Dispose();
Thread.Sleep(_ImportRetryInitialSleep * (retryCount + 1));
imageResult = await client.GetAsync(imageUrl);
retryCount++;
}
if (imageResult.IsSuccessStatusCode)
{
using (Stream imageStream = await imageResult.Content.ReadAsStreamAsync())
{
imageStream.Position = 0;
try
{
Image tile = Image.FromStream(imageStream);
_tiles.Add((0, y), tile);
max_y = y;
}
catch (Exception aexp)
{
if (aexp is ArgumentException)
{
break;
}
throw aexp;
}
}
}
else
{
imageResult.Dispose();
return new RServiceResult<Stream>(null, $"Http result is not ok ({imageResult.StatusCode}) for page {order}, url {imageUrl}");
}
}
List<Thread> threadsList = new List<Thread>();
List<(int x, int y)> unprocessedtiles = new List<(int x, int y)>();
for (int x = 0; x <= max_x; x++)
for (int y = 0; y <= max_y; y++)
if (_tiles.TryGetValue((x, y), out Image tmp) == false)
{
unprocessedtiles.Add((x, y));
}
foreach((int x, int y) unprocessedtile in unprocessedtiles)
{
Thread th = new Thread
(
new ThreadStart
(
async () =>
{
await _ProcessTile(pageUrl, unprocessedtile.x, unprocessedtile.y);
}
)
);
th.Start();
Thread.Sleep(100);
threadsList.Add(th);
}
foreach (Thread th in threadsList)
th.Join();
int tileWidth = _tiles[(0, 0)].Width;
int tileHeight = _tiles[(0, 0)].Height;
int imageWidth = tileWidth * (max_x + 1);
int imageHeight = tileHeight * (max_y + 1);
using (Image image = new Bitmap(imageWidth, imageHeight))
{
using (Graphics g = Graphics.FromImage(image))
{
for (int x = 0; x <= max_x; x++)
for (int y = 0; y < max_y; y++)
{
g.DrawImage(_tiles[(x, y)], new Point(x * tileWidth, y * tileHeight));
_tiles[(x, y)].Dispose();
}
Stream imageStream = new MemoryStream();
ImageCodecInfo jpgEncoder = _GetEncoder(ImageFormat.Jpeg);
Encoder myEncoder =
Encoder.Quality;
EncoderParameters jpegParameters = new EncoderParameters(1);
EncoderParameter qualityParameter = new EncoderParameter(myEncoder, 92L);
jpegParameters.Param[0] = qualityParameter;
image.Save(imageStream, jpgEncoder, jpegParameters);
imageStream.Position = 0;
return new RServiceResult<Stream>(imageStream);
}
}
}
}
catch(Exception exp)
{
return new RServiceResult<Stream>(null, exp.ToString());
}
}
private async Task _ProcessTile(string pageUrl, int x, int y)
{
using (var client = new HttpClient())
{
client.Timeout = TimeSpan.FromMinutes(5);
string imageUrl = $"http://www.bl.uk/manuscripts/Proxy.ashx?view={pageUrl}_files/13/{x}_{y}.jpg";
var imageResult = await client.GetAsync(imageUrl);
int _ImportRetryCount = 5;
int _ImportRetryInitialSleep = 500;
int retryCount = 0;
while (retryCount < _ImportRetryCount && !imageResult.IsSuccessStatusCode && imageResult.StatusCode == HttpStatusCode.ServiceUnavailable)
{
imageResult.Dispose();
Thread.Sleep(_ImportRetryInitialSleep * (retryCount + 1));
imageResult = await client.GetAsync(imageUrl);
retryCount++;
}
if (imageResult.IsSuccessStatusCode)
{
using (Stream imageStream = await imageResult.Content.ReadAsStreamAsync())
{
if (imageStream.Length == 0)
return;
imageStream.Position = 0;
_tiles.Add((x, y), Image.FromStream(imageStream));
}
}
else
{
imageResult.Dispose();
}
}
}
/// <summary>
/// find image encodder
/// </summary>
/// <param name="format"></param>
/// <returns></returns>
private ImageCodecInfo _GetEncoder(ImageFormat format)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
}
}

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
namespace RMuseum.Services.Implementation
{
internal class BackgroundTaskQueue : IBackgroundTaskQueue
{
private ConcurrentQueue<Func<CancellationToken, Task>> _workItems =
new ConcurrentQueue<Func<CancellationToken, Task>>();
private SemaphoreSlim _signal = new SemaphoreSlim(0);
public void QueueBackgroundWorkItem(
Func<CancellationToken, Task> workItem)
{
if (workItem == null)
{
throw new ArgumentNullException(nameof(workItem));
}
_workItems.Enqueue(workItem);
_signal.Release();
}
public async Task<Func<CancellationToken, Task>> DequeueAsync(
CancellationToken cancellationToken)
{
await _signal.WaitAsync(cancellationToken);
_workItems.TryDequeue(out var workItem);
return workItem;
}
}
}

View File

@ -0,0 +1,33 @@
using RSecurityBackend.Models.Mail;
using RSecurityBackend.Services.Implementation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace RMuseum.Services.Implementation
{
public class MailKitEmailSender : MailKitEmailSenderBase
{
/// <summary>
/// SmptConfig
/// </summary>
public override SmptConfig SmptConfig
{
get
{
return
new SmptConfig()
{
port = 25,
server = "ganjgah.ir",
smtpUsername = "noreply@ganjgah.ir",
smtpPassword = "sahY%6&$OnD67i0978",
from = "noreply@ganjgah.ir",
useSsl = false
};
}
}
}
}

View File

@ -0,0 +1,588 @@
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using RMuseum.DbContext;
using RMuseum.Models.Artifact;
using RSecurityBackend.Models.Generic;
using RSecurityBackend.Models.Image;
using RSecurityBackend.Services;
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace RMuseum.Services.Implementation
{
/// <summary>
/// manipulating picture files
/// </summary>
public class PictureFileService : IPictureFileService
{
/// <summary>
/// recover from files
/// </summary>
/// <param name="title"></param>
/// <param name="description"></param>
/// <param name="order"></param>
/// <param name="srcurl"></param>
/// <param name="orignalFilePath"></param>
/// <param name="normalFilePath"></param>
/// <param name="thumbFilePath"></param>
/// <param name="originalFileNameForStreams"></param>
/// <param name="imageFolderName"></param>
/// <returns></returns>
public async Task<RServiceResult<RPictureFile>> RecoverFromeFiles
(
string title, string description, int order, string srcurl, string orignalFilePath, string normalFilePath, string thumbFilePath, string originalFileNameForStreams, string imageFolderName
)
{
try
{
RPictureFile pictureFile =
new RPictureFile()
{
Title = title,
TitleInEnglish = title,
Description = description,
DescriptionInEnglish = description,
Order = order,
SrcUrl = srcurl,
Status = PublishStatus.Draft,
DataTime = DateTime.Now,
LastModified = DateTime.Now,
LastModifiedMeta = DateTime.Now,
FolderName = string.IsNullOrEmpty(imageFolderName) ? DateTime.Now.ToString("yyyy-MM") : imageFolderName,
ContentType = "image/jpeg",
OriginalFileName = originalFileNameForStreams,
StoredFileName = orignalFilePath,
NormalSizeImageStoredFileName = normalFilePath,
ThumbnailImageStoredFileName = thumbFilePath,
FileSizeInBytes = (await File.ReadAllBytesAsync(orignalFilePath)).Length
};
using(Image img = Image.FromFile(orignalFilePath))
{
pictureFile.ImageWidth = img.Width;
pictureFile.ImageHeight = img.Height;
}
using (Image img = Image.FromFile(normalFilePath))
{
pictureFile.NormalSizeImageWidth = img.Width;
pictureFile.NormalSizeImageHeight = img.Height;
}
using (Image img = Image.FromFile(thumbFilePath))
{
pictureFile.ThumbnailImageWidth = img.Width;
pictureFile.ThumbnailImageHeight = img.Height;
}
//here is a problem, this method could be called from a background service where _context is disposed, so I need to renew it
/*using(RMuseumDbContext context = new RMuseumDbContext(Configuration))
{
await context.PictureFiles.AddAsync(pictureFile.Result);
await context.SaveChangesAsync();
}*/
return new RServiceResult<RPictureFile>(pictureFile);
}
catch (Exception exp)
{
return new RServiceResult<RPictureFile>(null, exp.ToString());
}
}
/// <summary>
/// add new picture
/// </summary>
/// <param name="title"></param>
/// <param name="description"></param>
/// <param name="order"></param>
/// <param name="file"></param>
/// <param name="srcurl"></param>
/// <param name="stream"></param>
/// <param name="originalFileNameForStreams"></param>
/// <param name="imageFolderName">pass empty if you want a generic date based folder</param>
/// <returns></returns>
public async Task<RServiceResult<RPictureFile>> Add
(
string title, string description, int order, IFormFile file, string srcurl, Stream stream, string originalFileNameForStreams, string imageFolderName
)
{
try
{
RServiceResult<RPictureFile>
pictureFile =
await ProcessImage
(
file,
new RPictureFile()
{
Title = title,
TitleInEnglish = title,
Description = description,
DescriptionInEnglish = description,
Order = order,
SrcUrl = srcurl,
Status = PublishStatus.Draft,
DataTime = DateTime.Now,
LastModified = DateTime.Now,
LastModifiedMeta = DateTime.Now,
FolderName = string.IsNullOrEmpty(imageFolderName) ? DateTime.Now.ToString("yyyy-MM") : imageFolderName
},
stream,
originalFileNameForStreams
);
if (!string.IsNullOrEmpty(pictureFile.ExceptionString))
return new RServiceResult<RPictureFile>(null, pictureFile.ExceptionString);
//here is a problem, this method could be called from a background service where _context is disposed, so I need to renew it
/*using(RMuseumDbContext context = new RMuseumDbContext(Configuration))
{
await context.PictureFiles.AddAsync(pictureFile.Result);
await context.SaveChangesAsync();
}*/
return new RServiceResult<RPictureFile>(pictureFile.Result);
}
catch(Exception exp)
{
return new RServiceResult<RPictureFile>(null, exp.ToString());
}
}
/// <summary>
/// returns image info
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task<RServiceResult<RPictureFile>> GetImage(Guid id)
{
try
{
return new RServiceResult<RPictureFile>(
await _context.PictureFiles
.Where(p => p.Id == id)
.SingleOrDefaultAsync()
);
}
catch (Exception exp)
{
return new RServiceResult<RPictureFile>(null, exp.ToString());
}
}
/// <summary>
/// Rotate Image in 90 deg. multiplicants: 90, 180 or 270
/// </summary>
/// <param name="id"></param>
/// <param name="degIn90mul"></param>
/// <returns></returns>
public async Task<RServiceResult<RPictureFile>> RotateImage(Guid id, int degIn90mul)
{
try
{
if(degIn90mul != 90 && degIn90mul != 180 && degIn90mul != 270)
{
return new RServiceResult<RPictureFile>(null, "degIn90mul could only be equal to these 3 values: 90, 180 and 270");
}
RPictureFile rPictureFile =
await _context.PictureFiles
.Where(p => p.Id == id)
.SingleOrDefaultAsync();
string origPath = GetImagePath(rPictureFile).Result;
using(MemoryStream msRotated = new MemoryStream())
{
using (Image img = Image.FromFile(origPath))
{
img.RotateFlip(degIn90mul == 90 ? RotateFlipType.Rotate90FlipNone : degIn90mul == 180 ? RotateFlipType.Rotate180FlipNone : RotateFlipType.Rotate270FlipNone);
ImageCodecInfo jpgEncoder = GetEncoder(ImageFormat.Jpeg);
Encoder myEncoder =
Encoder.Quality;
EncoderParameters jpegParameters = new EncoderParameters(1);
EncoderParameter qualityParameter = new EncoderParameter(myEncoder, 92L);
jpegParameters.Param[0] = qualityParameter;
img.Save(msRotated, jpgEncoder, jpegParameters);
}
File.Move(GetImagePath(rPictureFile, "thumb").Result, GetImagePath(rPictureFile, "thumb").Result + ".bak");
File.Move(GetImagePath(rPictureFile, "norm").Result, GetImagePath(rPictureFile, "norm").Result + ".bak");
File.Move(origPath, origPath + ".bak");
RServiceResult<RPictureFile>
result =
await ProcessImage
(
null,
rPictureFile,
msRotated,
rPictureFile.OriginalFileName
);
if (!string.IsNullOrEmpty(result.ExceptionString))
return new RServiceResult<RPictureFile>(null, result.ExceptionString);
result.Result.LastModified = DateTime.Now;
_context.PictureFiles.Update(result.Result);
await _context.SaveChangesAsync();
File.Delete(GetImagePath(rPictureFile, "thumb").Result + ".bak");
File.Delete(GetImagePath(rPictureFile, "norm").Result + ".bak");
File.Delete(origPath + ".bak");
return result;
}
}
catch (Exception exp)
{
return new RServiceResult<RPictureFile>(null, exp.ToString());
}
}
/// <summary>
/// find image encodder
/// </summary>
/// <param name="format"></param>
/// <returns></returns>
public ImageCodecInfo GetEncoder(ImageFormat format)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
/// <summary>
/// returns image file stream
/// </summary>
/// <param name="image"></param>
/// <param name="sz"></param>
/// <returns></returns>
public RServiceResult<string> GetImagePath(RPictureFile image, string sz = "orig")
{
try
{
string fileName = sz == "thumb" ? image.ThumbnailImageStoredFileName : sz == "norm" ? image.NormalSizeImageStoredFileName : image.StoredFileName;
return new RServiceResult<string>(Path.Combine(ImageStoragePath, image.FolderName, fileName));
}
catch(Exception exp)
{
return new RServiceResult<string>(null, exp.ToString());
}
}
/// <summary>
/// فرمت تصاویر بندانگشتی و ...
/// </summary>
/// <example>image/jpeg</example>
protected string ResizedImageContentType { get { return $"{Configuration.GetSection("PictureFileService")["ResizedImageContentType"]}"; } }
/// <summary>
/// عرض تصویر با اندازه نرمال - جهت نمایش در صفحات
/// </summary>
protected int NormalImageMaxWidth { get { return int.Parse($"{Configuration.GetSection("PictureFileService")["NormalImageMaxWidth"]}"); } }
/// <summary>
/// /// طول تصویر با اندازه نرمال - جهت نمایش در صفحات
/// </summary>
protected int NormalImageMaxHeight { get { return int.Parse($"{Configuration.GetSection("PictureFileService")["NormalImageMaxHeight"]}"); } }
/// <summary>
/// عرض تصویر بندانگشتی
/// </summary>
protected int ThumbnailImageWidth { get { return int.Parse($"{Configuration.GetSection("PictureFileService")["ThumbnailImageWidth"]}"); } }
/// <summary>
/// طول تصویر بندانگشتی
/// </summary>
protected int ThumbnailImageMaxHeight { get { return int.Parse($"{Configuration.GetSection("PictureFileService")["ThumbnailMaxHeight"]}"); } }
/// <summary>
/// Image Storage Path
/// </summary>
public string ImageStoragePath { get { return $"{Configuration.GetSection("PictureFileService")["StoragePath"]}"; } }
private async Task<RServiceResult<RPictureFile>> ProcessImage(IFormFile uploadedImage, RPictureFile pictureFile, Stream stream, string originalFileNameForStreams)
{
if (uploadedImage == null && stream == null)
{
return new RServiceResult<RPictureFile>(null, "ProcessImage: uploadedImage == null && stream == null");
}
pictureFile.ContentType = uploadedImage == null ? "image/jpeg" : uploadedImage.ContentType;
pictureFile.FileSizeInBytes = uploadedImage == null ? stream.Length : uploadedImage.Length;
pictureFile.OriginalFileName = uploadedImage == null ? originalFileNameForStreams : uploadedImage.FileName;
string fullDirStorePath = Path.Combine(ImageStoragePath, pictureFile.FolderName);
string originalStorePath = Path.Combine(fullDirStorePath, "orig");
string normalStorePath = Path.Combine(fullDirStorePath, "norm");
string thumbStorePath = Path.Combine(fullDirStorePath, "thumb");
foreach(string path in new string[]
{
fullDirStorePath,
originalStorePath,
normalStorePath,
thumbStorePath
})
{
if (!Directory.Exists(path))
{
try
{
Directory.CreateDirectory(path);
}
catch
{
return new RServiceResult<RPictureFile>(null, $"ProcessImage: create dir failed {path}");
}
}
}
string ext = uploadedImage == null ? ".jpg" : Path.GetExtension(uploadedImage.FileName);
pictureFile.StoredFileName = Path.GetFileNameWithoutExtension(pictureFile.OriginalFileName) + ext;
string originalFileStorePath = Path.Combine(originalStorePath, pictureFile.StoredFileName);
while (File.Exists(originalFileStorePath))
{
pictureFile.StoredFileName = Path.GetFileNameWithoutExtension(pictureFile.OriginalFileName)+ "-" + Guid.NewGuid().ToString() + ext;
originalFileStorePath = Path.Combine(originalStorePath, pictureFile.StoredFileName);
}
pictureFile.StoredFileName = $@"orig\{pictureFile.StoredFileName}";
using (FileStream fsMain = new FileStream(originalFileStorePath, FileMode.Create))
{
if (uploadedImage != null)
await uploadedImage.CopyToAsync(fsMain);
else
{
stream.Position = 0;
await stream.CopyToAsync(fsMain);
}
}
using (MemoryStream ms = new MemoryStream())
{
if (uploadedImage != null)
await uploadedImage.CopyToAsync(ms);
else
{
stream.Position = 0;
await stream.CopyToAsync(ms);
}
using (Image img = Bitmap.FromStream(ms))
{
pictureFile.ImageWidth = img.Width;
pictureFile.ImageHeight = img.Height;
if (img.Width <= NormalImageMaxWidth && img.Height <= NormalImageMaxHeight && ext.Equals(".jpg", StringComparison.InvariantCultureIgnoreCase))
{
pictureFile.NormalSizeImageStoredFileName = pictureFile.StoredFileName;
pictureFile.NormalSizeImageWidth = img.Width;
pictureFile.NormalSizeImageHeight = img.Height;
}
else
{
pictureFile.NormalSizeImageWidth = NormalImageMaxWidth;
pictureFile.NormalSizeImageHeight = NormalImageMaxWidth * pictureFile.ImageHeight / pictureFile.ImageWidth;
if (pictureFile.NormalSizeImageHeight > NormalImageMaxHeight)
{
pictureFile.NormalSizeImageHeight = NormalImageMaxHeight;
pictureFile.NormalSizeImageWidth = NormalImageMaxHeight * pictureFile.ImageWidth / pictureFile.ImageHeight;
}
//روی خود img تأثیر می گذارد
Image resized = new Bitmap(pictureFile.NormalSizeImageWidth, pictureFile.NormalSizeImageHeight);
using(Graphics gResized = Graphics.FromImage(resized))
{
gResized.DrawImage(img, 0, 0, pictureFile.NormalSizeImageWidth, pictureFile.NormalSizeImageHeight);
}
using (MemoryStream msNormal = new MemoryStream())
{
resized.Save(msNormal, ImageFormat.Jpeg);
}
pictureFile.NormalSizeImageStoredFileName = Path.GetFileNameWithoutExtension(pictureFile.OriginalFileName) + ".jpg";
string normalFileStorePath = Path.Combine(normalStorePath, pictureFile.NormalSizeImageStoredFileName);
while (File.Exists(normalFileStorePath))
{
pictureFile.NormalSizeImageStoredFileName = Path.GetFileNameWithoutExtension(pictureFile.OriginalFileName) + "-" + Guid.NewGuid().ToString() + ".jpg";
normalFileStorePath = Path.Combine(normalStorePath, pictureFile.NormalSizeImageStoredFileName);
}
pictureFile.NormalSizeImageStoredFileName = $@"norm\{pictureFile.NormalSizeImageStoredFileName}";
using (FileStream fsNormal = new FileStream(normalFileStorePath, FileMode.Create))
{
resized.Save(fsNormal, ImageFormat.Jpeg);
}
}
pictureFile.ThumbnailImageWidth = ThumbnailImageWidth;
pictureFile.ThumbnailImageHeight = ThumbnailImageWidth * pictureFile.ImageHeight / pictureFile.ImageWidth;
if(pictureFile.ThumbnailImageHeight > ThumbnailImageMaxHeight)
{
pictureFile.ThumbnailImageHeight = ThumbnailImageMaxHeight;
pictureFile.ThumbnailImageWidth = pictureFile.ThumbnailImageHeight * pictureFile.ImageWidth / pictureFile.ImageHeight;
}
//روی خود img تأثیر می گذارد
// به احتمال قوی داریم روی تصویر resize کار می‌کنیم
Image thumbnail = new Bitmap(pictureFile.ThumbnailImageWidth, pictureFile.ThumbnailImageHeight);
using (Graphics gThumbnail = Graphics.FromImage(thumbnail))
{
gThumbnail.DrawImage(img, 0, 0, pictureFile.ThumbnailImageWidth, pictureFile.ThumbnailImageHeight);
}
using (MemoryStream msThumbnail = new MemoryStream())
{
thumbnail.Save(msThumbnail, ImageFormat.Jpeg);
}
pictureFile.ThumbnailImageStoredFileName = Path.GetFileNameWithoutExtension(pictureFile.OriginalFileName) + ".jpg";
string thumbFileStorePath = Path.Combine(thumbStorePath, pictureFile.ThumbnailImageStoredFileName);
while (File.Exists(thumbFileStorePath))
{
pictureFile.ThumbnailImageStoredFileName = Path.GetFileNameWithoutExtension(pictureFile.OriginalFileName) + "-" + Guid.NewGuid().ToString() + ".jpg";
thumbFileStorePath = Path.Combine(thumbStorePath, pictureFile.ThumbnailImageStoredFileName);
}
pictureFile.ThumbnailImageStoredFileName = $@"thumb\{pictureFile.ThumbnailImageStoredFileName}";
using (FileStream fsThumb = new FileStream(thumbFileStorePath, FileMode.Create))
{
thumbnail.Save(fsThumb, ImageFormat.Jpeg);
}
}
}
return new RServiceResult<RPictureFile>(pictureFile);
}
/// <summary>
/// Generated Cropped Image Based On ThumbnailCoordinates For Notes
/// </summary>
/// <param name="id"></param>
/// <param name="left"></param>
/// <param name="top"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns></returns>
public async Task<RServiceResult<RImage>> GenerateCroppedImageBasedOnThumbnailCoordinates(Guid id, decimal left, decimal top, decimal width, decimal height)
{
try
{
RPictureFile rPictureFile =
await _context.PictureFiles
.Where(p => p.Id == id)
.SingleOrDefaultAsync();
int adjustedImageWidth = (int)(width * rPictureFile.NormalSizeImageWidth / rPictureFile.ThumbnailImageWidth);
if (adjustedImageWidth > rPictureFile.ThumbnailImageWidth)
adjustedImageWidth = rPictureFile.ThumbnailImageWidth;
int adjustedImageHeight = (int)(height * adjustedImageWidth / width);
int adjusttedLeft = (int)(left * adjustedImageWidth / width);
int adjusttedTop = (int)(top * adjustedImageHeight / height);
string normalImagePath = GetImagePath(rPictureFile, "norm").Result;
using (Image targetImage = new Bitmap(adjustedImageWidth, adjustedImageHeight))
{
using(Graphics g = Graphics.FromImage(targetImage))
{
using (Image img = Image.FromFile(normalImagePath))
{
g.DrawImage(img, new Rectangle(0, 0, adjustedImageWidth, adjustedImageHeight),
(int)(left * rPictureFile.NormalSizeImageWidth / rPictureFile.ThumbnailImageWidth),
(int)(top * rPictureFile.NormalSizeImageHeight / rPictureFile.ThumbnailImageHeight),
(int)(width * rPictureFile.NormalSizeImageWidth / rPictureFile.ThumbnailImageWidth),
(int)(height * rPictureFile.NormalSizeImageHeight / rPictureFile.ThumbnailImageHeight),
GraphicsUnit.Pixel,
new ImageAttributes()
);
}
}
using(MemoryStream ms = new MemoryStream())
{
targetImage.Save(ms, ImageFormat.Jpeg);
ms.Position = 0;
RServiceResult<RImage> res = await _simpleImageStorage.Add(null, ms, $"{Path.GetFileNameWithoutExtension(rPictureFile.OriginalFileName)}-cropped-{left}-{top}-{width}-{height}.jpg", "CroppedImages");
if(string.IsNullOrEmpty(res.ExceptionString))
{
RImage image = res.Result;
_context.GeneralImages.Add(image);
await _context.SaveChangesAsync();
return new RServiceResult<RImage>(image);
}
return res;
}
}
}
catch (Exception exp)
{
return new RServiceResult<RImage>(null, exp.ToString());
}
}
/// <summary>
/// Database Contetxt
/// </summary>
protected readonly RMuseumDbContext _context;
/// <summary>
/// Configuration
/// </summary>
protected IConfiguration Configuration { get; }
/// <summary>
/// Simple Image Storage (not storing multiple image sizes) for creating small cropped images
/// </summary>
protected readonly IImageFileService _simpleImageStorage;
/// <summary>
/// constructor
/// </summary>
/// <param name="context"></param>
/// <param name="configuration"></param>
/// <param name="simpleImageStorage"></param>
public PictureFileService(RMuseumDbContext context, IConfiguration configuration, IImageFileService simpleImageStorage)
{
_context = context;
Configuration = configuration;
_simpleImageStorage = simpleImageStorage;
}
}
}

View File

@ -0,0 +1,38 @@
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace RMuseum.Services.Implementation
{
internal class QueuedHostedService : BackgroundService
{
public QueuedHostedService(IBackgroundTaskQueue taskQueue)
{
TaskQueue = taskQueue;
}
public IBackgroundTaskQueue TaskQueue { get; }
protected async override Task ExecuteAsync(
CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
var workItem = await TaskQueue.DequeueAsync(cancellationToken);
try
{
await workItem(cancellationToken);
}
catch
{
}
}
}
}
}

View File

@ -0,0 +1,35 @@
using Microsoft.AspNetCore.Identity;
using RMuseum.Models.Auth.Memory;
using RSecurityBackend.DbContext;
using RSecurityBackend.Models.Auth.Db;
using RSecurityBackend.Models.Auth.Memory;
using RSecurityBackend.Services.Implementation;
namespace RMuseum.Services.Implementation
{
/// <summary>
/// Role Service Implementation
/// </summary>
public class RoleService : RoleServiceBase
{
/// <summary>
/// gets list of SecurableItem, should be reimplemented in end user applications
/// </summary>
/// <returns></returns>
public override SecurableItem[] GetSecurableItems()
{
return RMuseumSecurableItem.Items;
}
/// <summary>
/// constructor
/// </summary>
/// <param name="roleManager"></param>
public RoleService(
RoleManager<RAppRole> roleManager
) : base(roleManager)
{
}
}
}

View File

@ -0,0 +1,56 @@
using Microsoft.EntityFrameworkCore;
using RMuseum.DbContext;
using RMuseum.Models.Artifact;
using System.Linq;
using System.Threading.Tasks;
namespace RMuseum.Services.Implementation
{
internal class TagHandler
{
public async static Task<RTagValue> PrepareAttribute(RMuseumDbContext db, string aName, string aValue, int order)
{
RTag type = await db.Tags.Where(a => a.Name == aName || a.NameInEnglish == aName).SingleOrDefaultAsync();
if(type == null)
{
int maxOrder = await db.Tags.CountAsync() == 0 ? 0 : await db.Tags.MaxAsync(a => a.Order);
type = new RTag()
{
Name = aName,
NameInEnglish = aName,
PluralName = $"{aName}s",
PluralNameInEnglish = $"{aName}s",
Order = maxOrder + 1,
Status = PublishStatus.Published,
GlobalValue = true
};
await db.Tags.AddAsync(type);
await db.SaveChangesAsync();
}
RTagValue tag =
new RTagValue()
{
Order = order,
Value = aValue,
ValueInEnglish = aValue,
ValueSupplement = "",
RTag = type,
Status = PublishStatus.Published
};
if (type.TagType == RTagType.Search || type.TagType == RTagType.LinkSearch)
{
RTagValue similar = await db.TagValues.Where(v => v.RTagId == type.Id && v.ValueInEnglish == aValue && !string.IsNullOrEmpty(v.FriendlyUrl)).FirstOrDefaultAsync();
if(similar != null)
{
tag.Value = similar.Value;
tag.FriendlyUrl = similar.FriendlyUrl;
}
}
return tag;
}
}
}

301
RMuseum/Startup.cs Normal file
View File

@ -0,0 +1,301 @@
using System;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Audit.Core;
using Audit.WebApi;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using RMuseum.DbContext;
using RMuseum.Models.Auth.Memory;
using RMuseum.Services;
using RMuseum.Services.Implementation;
using RSecurityBackend.Authorization;
using RSecurityBackend.DbContext;
using RSecurityBackend.Models.Auth.Db;
using RSecurityBackend.Models.Auth.Memory;
using RSecurityBackend.Models.Mail;
using RSecurityBackend.Services;
using RSecurityBackend.Services.Implementation;
using RSecurityBackend.Utilities;
using Swashbuckle.AspNetCore.Filters;
namespace RMuseum
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add service and create Policy with options
services.AddCors(options =>
{
options.AddPolicy("GanjoorCorsPolicy",
builder => builder.SetIsOriginAllowed(_ => true)
.AllowAnyMethod()
.AllowAnyHeader()
.WithExposedHeaders("paging-headers")
.AllowCredentials()
);
});
services.AddDbContext<RMuseumDbContext>();
Audit.Core.Configuration.JsonSettings.ContractResolver = AuditNetEnvironmentSkippingContractResolver.Instance;
Audit.Core.Configuration.DataProvider = new RAuditDataProvider(Configuration.GetConnectionString("DefaultConnection"));
services.AddIdentityCore<RAppUser>(
options =>
{
// Password settings.
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequiredLength = 6;
options.Password.RequiredUniqueChars = 1;
// Lockout settings.
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;
// User settings.
options.User.AllowedUserNameCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
options.User.RequireUniqueEmail = false;
}
);
new IdentityBuilder(typeof(RAppUser), typeof(RAppRole), services)
.AddRoleManager<RoleManager<RAppRole>>()
.AddSignInManager<SignInManager<RAppUser>>()
.AddEntityFrameworkStores<RMuseumDbContext>();
;
services.AddMvc
(
mvc =>
mvc.AddAuditFilter(config => config
.LogRequestIf(r => r.Method != "GET")
.WithEventType("{controller}/{action} ({verb})")
.IncludeHeaders(ctx => !ctx.ModelState.IsValid)
.IncludeRequestBody()
.IncludeModelState()
)).SetCompatibilityVersion(CompatibilityVersion.Latest);
services.AddMemoryCache();
services.Configure<FormOptions>(x =>
{
x.ValueLengthLimit = int.MaxValue;
x.MultipartBodyLengthLimit = int.MaxValue; // In case of multipart
});
services.AddAuthentication(options =>
{
options.DefaultScheme = "bearer";
}).AddJwtBearer("bearer", options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidAudience = "Everyone",
ValidateIssuer = true,
ValidIssuer = "Ganjoor",
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes($"{Configuration.GetSection("Security")["Secret"]}")),
ValidateLifetime = true, //validate the expiration and not before values in the token
ClockSkew = TimeSpan.Zero
};
options.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
context.Response.Headers.Add("Token-Expired", "true");
}
return Task.CompletedTask;
}
};
});
services.AddAuthorization(options =>
{
foreach (SecurableItem Item in RMuseumSecurableItem.Items)
{
foreach (SecurableItemOperation Operation in Item.Operations)
{
options.AddPolicy($"{Item.ShortName}:{Operation.ShortName}", policy => policy.Requirements.Add(new UserGroupPermissionRequirement(Item.ShortName, Operation.ShortName)));
}
}
});
// Register the Swagger generator, defining 1 or more Swagger documents
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "RMuseum API",
Version = "v1",
Description = "RMuseum API",
TermsOfService = new Uri("https://ganjoor.net/contact"),
Contact = new OpenApiContact
{
Name = "Ganjoor",
Email = "ganjoor@ganjoor.net",
Url = new Uri("https://ganjoor.net")
}
}
);
c.EnableAnnotations();
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "RSecurityBackend.xml"));
c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme()
{
Description = "format: \"bearer {token}\"",
In = ParameterLocation.Header,
Name = "Authorization",
Type = SecuritySchemeType.ApiKey
});
c.OperationFilter<Swashbuckle.SecurityRequirementsOperationFilter>();
c.OperationFilter<AppendAuthorizeToSummaryOperationFilter>(); // Adds "(Auth)" to the summary so that you can see which endpoints have Authorization
// or use the generic method, e.g. c.OperationFilter<AppendAuthorizeToSummaryOperationFilter<MyCustomAttribute>>();
});
//IHttpContextAccessor
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
//authorization handler
services.AddScoped<IAuthorizationHandler, UserGroupPermissionHandler>();
//security context maps to main db context
services.AddTransient<RSecurityDbContext<RAppUser, RAppRole, Guid>, RMuseumDbContext>();
//captcha service
services.AddTransient<ICaptchaService, CaptchaServiceEF>();
//generic image file sercie
services.AddTransient<IImageFileService, ImageFileServiceEF>();
//app user services
services.AddTransient<IAppUserService, AppUserService>();
//user groups servicess
services.AddTransient<IUserRoleService, RoleService>();
//audit service
services.AddTransient<IAuditLogService, AuditLogServiceEF>();
//user permission checker
services.AddTransient<IUserPermissionChecker, UserPermissionChecker>();
//secret generator
services.AddTransient<ISecretGenerator, SecretGenerator>();
// email service
services.AddTransient<IEmailSender, MailKitEmailSender>();
services.Configure<SmptConfig>(Configuration);
//picture file service
services.AddTransient<IPictureFileService, PictureFileService>();
//artifact service
services.AddTransient<IArtifactService, ArtifactService>();
services.AddHostedService<QueuedHostedService>();
services.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "RMuseum API V1");
c.RoutePrefix = string.Empty;
});
app.UseAuthentication();
// global policy - assign here or on each controller
app.UseCors("GanjoorCorsPolicy");
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

View File

@ -0,0 +1,46 @@
using System.Linq;
using System.Collections.Generic;
using Microsoft.AspNetCore.Authorization;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
using Microsoft.OpenApi.Models;
namespace RMuseum.Swashbuckle
{
/// <summary>
/// //https://mattfrear.com/2018/07/21/add-an-authorization-header-to-your-swagger-ui-with-swashbuckle-revisited/
/// </summary>
public class SecurityRequirementsOperationFilter : IOperationFilter
{
/// <summary>
///
/// </summary>
/// <param name="operation"></param>
/// <param name="context"></param>
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
// Policy names map to scopes
var requiredScopes = context.MethodInfo
.GetCustomAttributes(true)
.OfType<AuthorizeAttribute>()
.Select(attr => attr.Policy)
.Distinct();
if (requiredScopes.Any())
{
var oAuthScheme = new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }
};
operation.Security = new List<OpenApiSecurityRequirement>
{
new OpenApiSecurityRequirement
{
[ oAuthScheme ] = requiredScopes.ToList()
}
};
}
}
}
}

View File

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}

29
RMuseum/appsettings.json Normal file
View File

@ -0,0 +1,29 @@
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=museum;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"PictureFileService": {
"StoragePath": "C:\\museum",
"ResizedImageContentType": "image/jpeg",
"NormalImageMaxWidth": "800",
"NormalImageMaxHeight": "1350",
"ThumbnailImageWidth": "300",
"ThumbnailMaxHeight": "500"
},
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"UserSignupClientCallBack": {
"Url": "https://museum.ganjoor.net/signup"
},
"ForgotPasswordClientCallBack": {
"Url": "https://museum.ganjoor.net/forgot-password"
},
"Security": {
"Secret": "k4Fy7pDsc0LgXQ8PCCfZtwmju5Ed8asc",
"DefaultTokenExpirationInSeconds": "2502000"
},
"AllowedHosts": "*"
}

5
RMuseum/global.json Normal file
View File

@ -0,0 +1,5 @@
{
"sdk": {
"version": "3.1.200"
}
}

View File

@ -0,0 +1,64 @@
using Microsoft.AspNetCore.Authorization;
using RSecurityBackend.Models.Generic;
using System;
using System.Linq;
using System.Threading.Tasks;
using RSecurityBackend.Services;
namespace RSecurityBackend.Authorization
{
/// <summary>
/// UserGroupPermissionHandler
/// </summary>
public class UserGroupPermissionHandler : AuthorizationHandler<UserGroupPermissionRequirement>
{
/// <summary>
/// HandleRequirementAsync
/// </summary>
/// <param name="context"></param>
/// <param name="requirement"></param>
/// <returns></returns>
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, UserGroupPermissionRequirement requirement)
{
if (!context.User.HasClaim(c => c.Type == "UserId") || !context.User.HasClaim(c => c.Type == "SessionId"))
{
context.Fail();
return;
}
RServiceResult<bool> result = await _userPermissionChecker.Check
(
new Guid(context.User.Claims.FirstOrDefault(c => c.Type == "UserId").Value),
new Guid(context.User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value),
requirement.SecurableItemShortName,
requirement.OperationShortName
);
if(result.Result)
{
context.Succeed(requirement);
return;
}
context.Fail();
}
/// <summary>
/// IUserPermissionChecker instance
/// </summary>
private IUserPermissionChecker _userPermissionChecker;
/// <summary>
/// constructor
/// </summary>
/// <param name="userPermissionChecker"></param>
public UserGroupPermissionHandler(IUserPermissionChecker userPermissionChecker) : base()
{
_userPermissionChecker = userPermissionChecker;
}
}
}

View File

@ -0,0 +1,39 @@
using Microsoft.AspNetCore.Authorization;
namespace RSecurityBackend.Authorization
{
/// <summary>
/// UserGroupPermissionRequirement
/// </summary>
public class UserGroupPermissionRequirement : IAuthorizationRequirement
{
/// <summary>
///
/// </summary>
/// <see cref="RSecurityBackend.Models.Auth.Memory.SecurableItem.ShortName"/>
/// <example>
/// job
/// </example>
public string SecurableItemShortName { get; set; }
/// <summary>
///
/// </summary>
/// <see cref="RSecurityBackend.Models.Auth.Memory.SecurableItemOperation.ShortName"/>
/// <example>
/// view
/// </example>
public string OperationShortName { get; set; }
/// <summary>
/// constructor
/// </summary>
/// <param name="securableItemShortName"></param>
/// <param name="operationShortName"></param>
public UserGroupPermissionRequirement(string securableItemShortName, string operationShortName)
{
SecurableItemShortName = securableItemShortName;
OperationShortName = operationShortName;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,66 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using RSecurityBackend.Models.Audit.Db;
using RSecurityBackend.Models.Auth.Memory;
using RSecurityBackend.Models.Generic;
using RSecurityBackend.Services;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
namespace RSecurityBackend.Controllers
{
/// <summary>
/// Audit Log Controller Base
/// </summary>
[Produces("application/json")]
[Route("api/auditlogs")]
public abstract class AuditLogControllerBase : Controller
{
/// <summary>
/// get all audit logs ordered by time
/// </summary>
/// <param name="paging"></param>
/// <param name="userName"></param>
/// <param name="orderByTimeDescending"></param>
/// <returns></returns>
[HttpGet]
[Authorize(Policy = SecurableItem.AuditLogEntityShortName + ":" + SecurableItem.ViewOperationShortName)]
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable<REvent>))]
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
public async Task<IActionResult> Get([FromQuery]PagingParameterModel paging, string userName, bool orderByTimeDescending)
{
RServiceResult<(PaginationMetadata PagingMeta, REvent[] Items)> itemsInfo
= await _auditLogService.GetAll(paging, userName, orderByTimeDescending);
if (!string.IsNullOrEmpty(itemsInfo.ExceptionString))
{
return BadRequest(itemsInfo.ExceptionString);
}
// Paging Header
HttpContext.Response.Headers.Add("paging-headers", JsonConvert.SerializeObject(itemsInfo.Result.PagingMeta));
return Ok(itemsInfo.Result.Items);
}
/// <summary>
/// constructor
/// </summary>
/// <param name="auditLogService">
/// </param>
public AuditLogControllerBase(IAuditLogService auditLogService)
{
_auditLogService = auditLogService;
}
/// <summary>
/// Artifact Service
/// </summary>
protected readonly IAuditLogService _auditLogService;
}
}

View File

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace RBilling.Models.Tenant.Db
{
/// <summary>
/// Chargable Feature For Tenant Type
/// </summary>
public enum RChargableFeatureType
{
/// <summary>
/// yes/no
/// </summary>
Boolean = 0,
/// <summary>
/// 0, ...
/// </summary>
RegularNumber = 1,
/// <summary>
/// 1.222 or large numbers
/// </summary>
FloatNumber
}
}

View File

@ -0,0 +1,122 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using RSecurityBackend.Models.Auth.Memory;
using RSecurityBackend.Models.Generic;
using RSecurityBackend.Models.Image;
using RSecurityBackend.Services;
using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;
namespace RSecurityBackend.Controllers
{
/// <summary>
/// Generic Image Provider
/// </summary>
[Produces("application/json")]
[Route("api/rimages")]
public abstract class RImageControllerBase : Controller
{
/// <summary>
/// returns image stream
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet("{id}.jpg")]
[AllowAnonymous]
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(FileStreamResult))]
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
public async Task<IActionResult> Get(Guid id)
{
RServiceResult<RImage> img =
await _pictureFileService.GetImage(id);
if (!string.IsNullOrEmpty(img.ExceptionString))
return BadRequest(img.ExceptionString);
if (img.Result == null)
return NotFound();
Response.GetTypedHeaders().LastModified = img.Result.LastModified;
var requestHeaders = Request.GetTypedHeaders();
if (requestHeaders.IfModifiedSince.HasValue &&
requestHeaders.IfModifiedSince.Value >= img.Result.LastModified)
{
return StatusCode(StatusCodes.Status304NotModified);
}
RServiceResult<string> imgPath = _pictureFileService.GetImagePath(img.Result);
if (!string.IsNullOrEmpty(imgPath.ExceptionString))
return BadRequest(imgPath.ExceptionString);
return new FileStreamResult(new FileStream(imgPath.Result, FileMode.Open, FileAccess.Read), "image/jpeg");
}
/// <summary>
/// temporary api for uploading temporary images
/// </summary>
/// <returns></returns>
[HttpPost]
[Authorize(Policy = SecurableItem.AuditLogEntityShortName + ":" + SecurableItem.ViewOperationShortName)]
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(Guid))]
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
[ProducesResponseType((int)HttpStatusCode.Unauthorized)]
[ProducesResponseType((int)HttpStatusCode.Forbidden, Type = typeof(string))]
public async Task<IActionResult> UploadImage()
{
try
{
if (!Request.Form.TryGetValue("path", out Microsoft.Extensions.Primitives.StringValues path))
{
return BadRequest("path is null");
}
IFormFile file = Request.Form.Files[0];
RServiceResult<RImage> image = await _pictureFileService.Add(file, null, file.FileName, path);
if (!string.IsNullOrEmpty(image.ExceptionString))
{
return BadRequest(image.ExceptionString);
}
image = await _pictureFileService.Store(image.Result);
if (!string.IsNullOrEmpty(image.ExceptionString))
{
return BadRequest(image.ExceptionString);
}
return Ok(image.Result.Id);
}
catch (Exception exp)
{
return BadRequest(exp.ToString());
}
}
/// <summary>
/// constructor
/// </summary>
/// <param name="pictureFileService">
/// </param>
public RImageControllerBase(IImageFileService pictureFileService)
{
_pictureFileService = pictureFileService;
}
/// <summary>
/// Artifact Service
/// </summary>
protected readonly IImageFileService _pictureFileService;
}
}

View File

@ -0,0 +1,198 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using RSecurityBackend.Models.Auth.Db;
using RSecurityBackend.Models.Auth.Memory;
using RSecurityBackend.Models.Generic;
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using RSecurityBackend.Services;
namespace RSecurityBackend.Controllers
{
/// <summary>
/// User roles
/// </summary>
[Produces("application/json")]
[Route("api/roles")]
public abstract class RoleControllerBase : Controller
{
/// <summary>
/// All Roles Information
/// </summary>
/// <returns>All Roles Information</returns>
[HttpGet]
[Authorize(Policy = SecurableItem.RoleEntityShortName + ":" + SecurableItem.ViewOperationShortName)]
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable<RAppRole>))]
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
public async Task<IActionResult> Get()
{
RServiceResult<RAppRole[]> rolesInfo = await _roleService.GetAllRoles();
if (rolesInfo.Result == null)
{
return BadRequest(rolesInfo.ExceptionString);
}
return Ok(rolesInfo.Result);
}
/// <summary>
/// returns role information
/// </summary>
/// <param name="roleName">role name</param>
/// <returns>role information</returns>
[HttpGet("{roleName}")]
[Authorize(Policy = SecurableItem.RoleEntityShortName + ":" + SecurableItem.ViewOperationShortName)]
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RAppRole))]
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public async Task<IActionResult> Get(string roleName)
{
RServiceResult<RAppRole> roleInfo = await _roleService.GetRoleInformation(roleName);
if (roleInfo.Result == null)
{
if (string.IsNullOrEmpty(roleInfo.ExceptionString))
return NotFound();
return BadRequest(roleInfo.ExceptionString);
}
return Ok(roleInfo.Result);
}
/// <summary>
/// add a new role
/// </summary>
/// <param name="newGroupInfo"></param>
/// <returns>id if required could be retrieved from return value</returns>
[HttpPost]
[Authorize(Policy = SecurableItem.RoleEntityShortName + ":" + SecurableItem.AddOperationShortName)]
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RAppRole))]
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
public async Task<IActionResult> Post([FromBody]RAppRole newGroupInfo)
{
RServiceResult<RAppRole> result = await _roleService.AddRole(newGroupInfo);
if (result.Result == null)
return BadRequest(result.ExceptionString);
return Ok(result.Result);
}
/// <summary>
/// update existing role
/// </summary>
/// <param name="roleName">role name</param>
/// <param name="existingGroupInfo">existingGroupInfo.id could be passed empty and it is ignored completely</param>
/// <returns>true if succeeds</returns>
[HttpPut("{roleName}")]
[Authorize(Policy = SecurableItem.RoleEntityShortName + ":" + SecurableItem.ModifyOperationShortName)]
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))]
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
public async Task<IActionResult> Put(string roleName, [FromBody]RAppRole existingGroupInfo)
{
RServiceResult<bool> res = await _roleService.ModifyRole(roleName, existingGroupInfo);
if (!res.Result)
return BadRequest(res.ExceptionString);
return Ok(true);
}
/// <summary>
/// delete role
/// </summary>
/// <param name="roleName">role name</param>
/// <returns>true if succeeds</returns>
[HttpDelete("{roleName}")]
[Authorize(Policy = SecurableItem.RoleEntityShortName + ":" + SecurableItem.DeleteOperationShortName)]
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))]
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
public async Task<IActionResult> Delete(string roleName)
{
RServiceResult<bool> res = await _roleService.DeleteRole(roleName);
if (!res.Result)
{
return BadRequest(res.ExceptionString);
}
return Ok(true);
}
/// <summary>
/// lists role permissions
/// </summary>
/// <returns></returns>
[HttpGet]
[Authorize(Policy = SecurableItem.RoleEntityShortName + ":" + SecurableItem.ViewOperationShortName)]
[Route("permissions/{roleName}")]
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable<SecurableItem[]>))]
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
public async Task<IActionResult> GetRoleSecurableItemsStatus(string roleName)
{
RServiceResult<SecurableItem[]> res = await _roleService.GetRoleSecurableItemsStatus(roleName);
if (res.Result == null)
{
return BadRequest(res.ExceptionString);
}
return Ok(res.Result);
}
/// <summary>
/// Saves role permissions
/// </summary>
/// <param name="roleName">role name</param>
/// <param name="securableItems"></param>
/// <returns></returns>
[HttpPut]
[Authorize(Policy = SecurableItem.RoleEntityShortName + ":" + SecurableItem.ModifyOperationShortName)]
[Route("permissions/{roleName}")]
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable<SecurableItem[]>))]
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
public async Task<IActionResult> SetRoleSecurableItemsStatus(string roleName, [FromBody]SecurableItem[] securableItems)
{
RServiceResult<bool> res = await _roleService.SetRoleSecurableItemsStatus(roleName, securableItems);
if (!res.Result)
{
return BadRequest(res.ExceptionString);
}
return Ok(res.Result);
}
/// <summary>
/// Get All SecurableItems
/// </summary>
/// <returns>All All SecurableItems</returns>
[HttpGet]
[Route("securableitems")]
[AllowAnonymous]
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable<SecurableItem>))]
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
public IActionResult GetSecurableItems()
{
return Ok(_roleService.GetSecurableItems());
}
/// <summary>
/// IUserRoleService instance
/// </summary>
protected IUserRoleService _roleService;
/// <summary>
/// constructor
/// </summary>
/// <param name="roleService"></param>
public RoleControllerBase(IUserRoleService roleService)
{
_roleService = roleService;
}
}
}

View File

@ -0,0 +1,92 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using RSecurityBackend.Models.Generic;
using RSecurityBackend.Services;
namespace RSecurityBackend.Controllers
{
/// <summary>
/// base class for controllers needing UserScopeCheck
/// </summary>
public abstract class UserScopeCheckEnabledControllerBase : Controller
{
/// <summary>
/// UserScopeCheck method EntityShortName;
/// </summary>
/// <example>
/// RBillingSecurableItem.TenantEntityShortName
/// </example>
protected abstract string UserScopeCheckEntityShortName { get; }
/// <summary>
/// UserScopeCheck method OperationShortName
/// </summary>
/// <example>
/// SecurableItem.ViewOperationShortName
/// </example>
protected abstract string UserScopeCheckOperationShortName { get; }
/// <summary>
/// returns user id if user is a guest, and empty if he/she has access to view all users information
/// </summary>
/// <returns></returns>
protected async Task<RServiceResult<Guid>> GetUserIdUnlessUserHavePermissionToReadAllUsersDataWhichReturnEmptyUserId()
{
Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value);
RServiceResult<bool> canReadAllUsersData =
await _userPermissionChecker.Check
(
userId,
new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value),
UserScopeCheckEntityShortName,
UserScopeCheckOperationShortName
);
if (!string.IsNullOrEmpty(canReadAllUsersData.ExceptionString))
return new RServiceResult<Guid>(userId, canReadAllUsersData.ExceptionString);
if (canReadAllUsersData.Result)
{
userId = Guid.Empty;
}
return new RServiceResult<Guid>(userId);
}
/// <summary>
/// can current user read this userId daya
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
protected async Task<RServiceResult<bool>> CanReadUserData(Guid userId)
{
RServiceResult<Guid> scopeForUserId = await GetUserIdUnlessUserHavePermissionToReadAllUsersDataWhichReturnEmptyUserId();
if(!string.IsNullOrEmpty(scopeForUserId.ExceptionString))
{
return new RServiceResult<bool>(false, scopeForUserId.ExceptionString);
}
return new RServiceResult<bool>(scopeForUserId.Result == Guid.Empty || scopeForUserId.Result == userId);
}
/// <summary>
/// Permission Checker Service
/// </summary>
protected readonly IUserPermissionChecker _userPermissionChecker;
/// <summary>
/// constructor
/// </summary>
/// <param name="userPermissionChecker"></param>
public UserScopeCheckEnabledControllerBase(IUserPermissionChecker userPermissionChecker)
{
_userPermissionChecker = userPermissionChecker;
}
}
}

View File

@ -0,0 +1,86 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using RSecurityBackend.Models.Audit.Db;
using RSecurityBackend.Models.Auth.Db;
using RSecurityBackend.Models.Image;
using System;
namespace RSecurityBackend.DbContext
{
/// <summary>
/// Security EF DbContext
/// </summary>
public class RSecurityDbContext<TUser, TRole, TKey> : IdentityDbContext<TUser, TRole, TKey>
where TUser : IdentityUser<TKey>
where TRole : IdentityRole<TKey>
where TKey : IEquatable<TKey>
{
/// <summary>
/// parameterless constructor
/// </summary>
public RSecurityDbContext()
: base()
{
}
/// <summary>
/// delete db
/// </summary>
public void DeleteDb()
{
Database.EnsureDeleted();
}
/// <summary>
///
/// </summary>
/// <param name="builder"></param>
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder); //https://stackoverflow.com/a/34013431/66657
builder.Entity<RVerifyQueueItem>()
.HasIndex(u => u.Secret)
.IsUnique();
}
#region Auth
/// <summary>
/// Permissions
/// </summary>
public DbSet<RPermission> Permissions { get; set; }
/// <summary>
/// User Sessions
/// </summary>
public DbSet<RTemporaryUserSession> Sessions { get; set; }
/// <summary>
/// Signup Queue Items
/// </summary>
public DbSet<RVerifyQueueItem> VerifyQueueItems { get; set; }
/// <summary>
/// General Images
/// </summary>
public DbSet<RImage> GeneralImages { get; set; }
/// <summary>
/// Captcha Images
/// </summary>
public DbSet<RCaptchaImage> CaptchaImages { get; set; }
#endregion
#region Audit
/// <summary>
/// Audit Logs Events
/// </summary>
public DbSet<REvent> AuditLogs { get; set; }
#endregion
}
}

View File

@ -0,0 +1,61 @@
using System;
namespace RSecurityBackend.Models.Audit.Db
{
/// <summary>
/// Audit Event
/// </summary>
public class REvent
{
/// <summary>
/// Id
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Event Type
/// </summary>
public string EventType { get; set; }
/// <summary>
/// Start Date
/// </summary>
public DateTime StartDate { get; set; }
/// <summary>
/// Last Updated Date
/// </summary>
public DateTime? EndDate { get; set; }
/// <summary>
/// Duration
/// </summary>
public int Duration { get; set; }
/// <summary>
/// User Name
/// </summary>
public string UserName { get; set; }
/// <summary>
/// IP Address
/// </summary>
public string IpAddress { get; set; }
/// <summary>
/// Response Code
/// </summary>
public int ResponseStatusCode { get; set; }
/// <summary>
/// Requset Url
/// </summary>
public string RequestUrl { get; set; }
/// <summary>
/// Jason Data
/// </summary>
public string JsonData { get; set; }
}
}

View File

@ -0,0 +1,38 @@
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
namespace RSecurityBackend.Models.Auth.Db
{
/// <summary>
/// Role Permissions (a set of defined permision policies which could be assigned to users)
/// </summary>
public class RAppRole : IdentityRole<Guid>
{
/// <summary>
/// default constructor
/// </summary>
public RAppRole() : base()
{
}
/// <summary>
/// constructor
/// </summary>
/// <param name="roleName"></param>
public RAppRole(string roleName) : base(roleName)
{
}
/// <summary>
/// Description
/// </summary>
public string Description { get; set; }
/// <summary>
/// Permissions
/// </summary>
public ICollection<RPermission> Permissions { get; set; }
}
}

View File

@ -0,0 +1,47 @@
using System;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Identity;
using RSecurityBackend.Models.Image;
namespace RSecurityBackend.Models.Auth.Db
{
/// <summary>
/// Ganjoor Application User
/// </summary>
public class RAppUser : IdentityUser<Guid>
{
/// <summary>
/// First Name
/// </summary>
[MinLength(1)]
public string FirstName { get; set; }
/// <summary>
/// Sure Name
/// </summary>
[MinLength(1)]
public string SureName { get; set; }
/// <summary>
/// User Creation Date
/// </summary>
[Required]
public DateTime CreateDate { get; set; }
/// <summary>
/// user status
/// </summary>
[Required]
public RAppUserStatus Status { get; set; }
/// <summary>
/// user image
/// </summary>
public virtual RImage RImage { get; set; }
/// <summary>
/// user image id
/// </summary>
public Guid? RImageId { get; set; }
}
}

View File

@ -0,0 +1,17 @@
namespace RSecurityBackend.Models.Auth.Db
{
/// <summary>
/// User Status
/// </summary>
public enum RAppUserStatus
{
/// <summary>
/// Inactive
/// </summary>
Inactive = 0,
/// <summary>
/// Active
/// </summary>
Active = 1
}
}

View File

@ -0,0 +1,36 @@
using RSecurityBackend.Models.Image;
using System;
namespace RSecurityBackend.Models.Auth.Db
{
/// <summary>
/// Captcha Image
/// </summary>
public class RCaptchaImage
{
/// <summary>
/// Id
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Captcha Image
/// </summary>
public RImage RImage { get; set; }
/// <summary>
/// Captcha Image Id
/// </summary>
public Guid RImageId { get; set; }
/// <summary>
/// Captcha Text
/// </summary>
public string Value { get; set; }
/// <summary>
/// DateTime
/// </summary>
public DateTime DateTime { get; set; }
}
}

View File

@ -0,0 +1,32 @@
using System;
namespace RSecurityBackend.Models.Auth.Db
{
/// <summary>
/// Permission line
/// </summary>
/// <see cref="RSecurityBackend.Models.Auth.Memory.SecurableItem"/>
public class RPermission
{
/// <summary>
/// Id
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// SecurableItem short name
/// </summary>
/// <example>
/// job
/// </example>
public string SecurableItemShortName { get; set; }
/// <summary>
/// Operation short name
/// </summary>
/// <example>
/// view
/// </example>
public string OperationShortName { get; set; }
}
}

View File

@ -0,0 +1,72 @@
using System;
namespace RSecurityBackend.Models.Auth.Db
{
/// <summary>
/// Ganjoor User Session (Temporary)
/// </summary>
/// <remarks>
/// Warning: Instances are supposed to delete when user logs out, so do not link anything serious to it
/// </remarks>
public class RTemporaryUserSession
{
/// <summary>
/// Session Id
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// User Id
/// </summary>
public Guid RAppUserId { get; set; }
/// <summary>
/// User
/// </summary>
public RAppUser RAppUser { get; set; }
/// <summary>
/// Client IP Address
/// </summary>
public string ClientIPAddress { get; set; }
/// <summary>
/// Client Application Name
/// </summary>
/// <example>
/// Ganjoor Angular Client
/// </example>
public string ClientAppName { get; set; }
/// <summary>
/// Client Language
/// </summary>
/// <example>
/// fa-IR
/// </example>
public string Language { get; set; }
/// <summary>
/// Login Date
/// </summary>
public DateTime LoginTime { get; set; }
/// <summary>
/// Last Renewal
/// </summary>
public DateTime LastRenewal { get; set; }
/// <summary>
/// Expiraton Time
/// </summary>
public DateTime ValidUntil { get; set; }
/// <summary>
/// User Token
/// </summary>
public string Token { get; set; }
}
}

View File

@ -0,0 +1,58 @@
using System;
namespace RSecurityBackend.Models.Auth.Db
{
/// <summary>
/// User SignUp Queue
/// </summary>
public class RVerifyQueueItem
{
/// <summary>
/// Id
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// queue type
/// </summary>
public RVerifyQueueType QueueType { get; set; }
/// <summary>
/// Email
/// </summary>
public string Email { get; set; }
/// <summary>
/// PhoneNumber
/// </summary>
public string PhoneNumber { get; set; }
/// <summary>
/// DateTime
/// </summary>
public DateTime DateTime { get; set; }
/// <summary>
/// Secret
/// </summary>
public string Secret { get; set; }
/// <summary>
/// client IP address
/// </summary>
public string ClientIPAddress { get; set; }
/// <summary>
/// client app name
/// </summary>
public string ClientAppName { get; set; }
/// <summary>
/// Client Language
/// </summary>
/// <example>
/// fa-IR
/// </example>
public string Language { get; set; }
}
}

View File

@ -0,0 +1,17 @@
namespace RSecurityBackend.Models.Auth.Db
{
/// <summary>
/// Verify Queue Type
/// </summary>
public enum RVerifyQueueType
{
/// <summary>
/// Sign up by email
/// </summary>
SignUp = 0,
/// <summary>
/// Forgot Password by email
/// </summary>
ForgotPassword = 1
}
}

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