Bladeren bron

- Added ability to edit paste.

- Added session cacheing of paste password.
tags/3.0.0^2
Teknikode 8 maanden geleden
bovenliggende
commit
a4365b2671

+ 5
- 3
Teknik/Areas/API/V1/Models/PasteAPIv1Model.cs Bestand weergeven

@@ -1,4 +1,6 @@
namespace Teknik.Areas.API.V1.Models
using Teknik.Utilities;

namespace Teknik.Areas.API.V1.Models
{
public class PasteAPIv1Model : BaseAPIv1Model
{
@@ -8,7 +10,7 @@

public string syntax { get; set; }

public string expireUnit { get; set; }
public ExpirationUnit expireUnit { get; set; }

public int expireLength { get; set; }

@@ -19,7 +21,7 @@
code = null;
title = string.Empty;
syntax = "text";
expireUnit = "never";
expireUnit = ExpirationUnit.Never;
expireLength = 1;
password = string.Empty;
}

+ 206
- 2
Teknik/Areas/Paste/Controllers/PasteController.cs Bestand weergeven

@@ -20,6 +20,7 @@ using Microsoft.AspNetCore.Http;
using Teknik.Logging;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Diagnostics;

namespace Teknik.Areas.Paste.Controllers
{
@@ -64,6 +65,7 @@ namespace Teknik.Areas.Paste.Controllers
model.Title = paste.Title;
model.Syntax = paste.Syntax;
model.DatePosted = paste.DatePosted;
model.Username = paste.User?.Username;

if (User.Identity.IsAuthenticated && type.ToLower() == "full")
{
@@ -80,6 +82,11 @@ namespace Teknik.Areas.Paste.Controllers
// The paste has a password set
if (!string.IsNullOrEmpty(paste.HashedPassword))
{
if (string.IsNullOrEmpty(password))
{
// Try to get the password from the session
password = GetCachedPassword(url);
}
string hash = string.Empty;
if (!string.IsNullOrEmpty(password))
{
@@ -89,6 +96,7 @@ namespace Teknik.Areas.Paste.Controllers
if (string.IsNullOrEmpty(password) || hash != paste.HashedPassword)
{
PasswordViewModel passModel = new PasswordViewModel();
passModel.ActionUrl = Url.SubRouteUrl("p", "Paste.View");
passModel.Url = url;
passModel.Type = type;

@@ -103,6 +111,9 @@ namespace Teknik.Areas.Paste.Controllers
}
}

// Save the password to the cache
CachePassword(url, password);

// Read in the file
string subDir = paste.FileName[0].ToString();
string filePath = Path.Combine(_config.PasteConfig.PasteDirectory, subDir, paste.FileName);
@@ -158,7 +169,7 @@ namespace Teknik.Areas.Paste.Controllers
{
Models.Paste paste = PasteHelper.CreatePaste(_config, _dbContext, model.Content, model.Title, model.Syntax, model.ExpireUnit, model.ExpireLength ?? 1, model.Password);

if (model.ExpireUnit == "view")
if (model.ExpireUnit == ExpirationUnit.Views)
{
paste.Views = -1;
}
@@ -175,6 +186,9 @@ namespace Teknik.Areas.Paste.Controllers
_dbContext.Pastes.Add(paste);
_dbContext.SaveChanges();

// Cache the password
CachePassword(paste.Url, model.Password);

return Redirect(Url.SubRouteUrl("p", "Paste.View", new { type = "Full", url = paste.Url }));
}
catch (Exception ex)
@@ -186,6 +200,171 @@ namespace Teknik.Areas.Paste.Controllers
}
return View("~/Areas/Paste/Views/Paste/Index.cshtml", model);
}
public async Task<IActionResult> Edit(string url, string password)
{
Models.Paste paste = _dbContext.Pastes.Where(p => p.Url == url).FirstOrDefault();
if (paste != null)
{
if (paste.User?.Username != User.Identity.Name)
return new StatusCodeResult(StatusCodes.Status403Forbidden);

ViewBag.Title = "Edit Paste";
ViewBag.Description = "Edit your paste's content.";

// Check Expiration
if (PasteHelper.CheckExpiration(paste))
{
_dbContext.Pastes.Remove(paste);
_dbContext.SaveChanges();
return new StatusCodeResult(StatusCodes.Status404NotFound);
}

PasteViewModel model = new PasteViewModel();
model.Url = url;
model.Title = paste.Title;
model.Syntax = paste.Syntax;
model.DatePosted = paste.DatePosted;
model.Username = paste.User?.Username;

byte[] ivBytes = Encoding.Unicode.GetBytes(paste.IV);
byte[] keyBytes = AesCounterManaged.CreateKey(paste.Key, ivBytes, paste.KeySize);

// The paste has a password set
if (!string.IsNullOrEmpty(paste.HashedPassword))
{
if (string.IsNullOrEmpty(password))
{
// Try to get the password from the session
password = GetCachedPassword(url);
}
string hash = string.Empty;
if (!string.IsNullOrEmpty(password))
{
hash = PasteHelper.HashPassword(paste.Key, password);
keyBytes = AesCounterManaged.CreateKey(password, ivBytes, paste.KeySize);
}
if (string.IsNullOrEmpty(password) || hash != paste.HashedPassword)
{
PasswordViewModel passModel = new PasswordViewModel();
passModel.ActionUrl = Url.SubRouteUrl("p", "Paste.Edit");
passModel.Url = url;

if (!string.IsNullOrEmpty(password) && hash != paste.HashedPassword)
{
passModel.Error = true;
passModel.ErrorMessage = "Invalid Password";
}

// Redirect them to the password request page
return View("~/Areas/Paste/Views/Paste/PasswordNeeded.cshtml", passModel);
}
}

// Cache the password
CachePassword(url, password);

// Read in the file
string subDir = paste.FileName[0].ToString();
string filePath = Path.Combine(_config.PasteConfig.PasteDirectory, subDir, paste.FileName);
if (!System.IO.File.Exists(filePath))
{
return new StatusCodeResult(StatusCodes.Status404NotFound);
}

using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
using (AesCounterStream cs = new AesCounterStream(fs, false, keyBytes, ivBytes))
using (StreamReader sr = new StreamReader(cs, Encoding.Unicode))
{
model.Content = await sr.ReadToEndAsync();
}

return View("~/Areas/Paste/Views/Paste/Edit.cshtml", model);
}
return new StatusCodeResult(StatusCodes.Status404NotFound);
}

[HttpPost]
[DisableRequestSizeLimit]
public IActionResult EditSubmit([Bind("Content, Title, Syntax, Url")]PasteEditViewModel model)
{
if (_config.PasteConfig.Enabled)
{
try
{
Models.Paste paste = _dbContext.Pastes.Where(p => p.Url == model.Url).FirstOrDefault();
if (paste != null)
{
if (paste.User?.Username != User.Identity.Name)
return new StatusCodeResult(StatusCodes.Status403Forbidden);

string password = null;
// The paste has a password set
if (!string.IsNullOrEmpty(paste.HashedPassword))
{
// Try to get the password from the session
password = GetCachedPassword(model.Url);
string hash = string.Empty;
if (!string.IsNullOrEmpty(password))
{
hash = PasteHelper.HashPassword(paste.Key, password);
}
if (string.IsNullOrEmpty(password) || hash != paste.HashedPassword)
{
PasswordViewModel passModel = new PasswordViewModel();
passModel.ActionUrl = Url.SubRouteUrl("p", "Paste.Edit");
passModel.Url = model.Url;

if (!string.IsNullOrEmpty(password) && hash != paste.HashedPassword)
{
passModel.Error = true;
passModel.ErrorMessage = "Invalid Password";
}

// Redirect them to the password request page
return View("~/Areas/Paste/Views/Paste/PasswordNeeded.cshtml", passModel);
}
}

// Delete the old file
string subDir = paste.FileName[0].ToString();
string filePath = Path.Combine(_config.PasteConfig.PasteDirectory, subDir, paste.FileName);
if (System.IO.File.Exists(filePath))
System.IO.File.Delete(filePath);

// Generate a unique file name that does not currently exist
string newFilePath = FileHelper.GenerateRandomFileName(_config.PasteConfig.PasteDirectory, _config.PasteConfig.FileExtension, 10);
string fileName = Path.GetFileName(newFilePath);

string key = PasteHelper.GenerateKey(_config.PasteConfig.KeySize);
string iv = PasteHelper.GenerateIV(_config.PasteConfig.BlockSize);

PasteHelper.EncryptContents(model.Content, newFilePath, password, key, iv, _config.PasteConfig.KeySize, _config.PasteConfig.ChunkSize);

paste.Key = key;
paste.KeySize = _config.PasteConfig.KeySize;
paste.IV = iv;
paste.BlockSize = _config.PasteConfig.BlockSize;

paste.HashedPassword = PasteHelper.HashPassword(paste.Key, password);
paste.FileName = fileName;
paste.Title = model.Title;
paste.Syntax = model.Syntax;
paste.DateEdited = DateTime.Now;

_dbContext.Entry(paste).State = EntityState.Modified;
_dbContext.SaveChanges();

return Redirect(Url.SubRouteUrl("p", "Paste.View", new { type = "Full", url = paste.Url }));
}
}
catch (Exception ex)
{
return Redirect(Url.SubRouteUrl("error", "Error.500", new { exception = ex }));
}
}
return new StatusCodeResult(StatusCodes.Status403Forbidden);
}

[HttpPost]
public IActionResult Delete(string id)
@@ -206,11 +385,36 @@ namespace Teknik.Areas.Paste.Controllers
System.IO.File.Delete(filePath);
}

return Json(new { result = true });
return Json(new { result = true, redirect = Url.SubRouteUrl("p", "Paste.Index") });
}
return Json(new { error = new { message = "You do not have permission to edit this Paste" } });
}
return Json(new { error = new { message = "This Paste does not exist" } });
}

private void CachePassword(string url, string password)
{
if (HttpContext != null)
{
HttpContext.Session.Set("PastePassword_" + url, password);
}
}

private string GetCachedPassword(string url)
{
if (HttpContext != null)
{
return HttpContext.Session.Get<string>("PastePassword_" + url);
}
return null;
}

private void ClearCachedPassword(string url)
{
if (HttpContext != null)
{
HttpContext.Session.Remove("PastePassword_" + url);
}
}
}
}

+ 2
- 0
Teknik/Areas/Paste/Models/Paste.cs Bestand weergeven

@@ -21,6 +21,8 @@ namespace Teknik.Areas.Paste.Models

public DateTime DatePosted { get; set; }

public DateTime DateEdited { get; set; }

[CaseSensitive]
public string Url { get; set; }


+ 9
- 9
Teknik/Areas/Paste/PasteHelper.cs Bestand weergeven

@@ -14,7 +14,7 @@ namespace Teknik.Areas.Paste
{
public static class PasteHelper
{
public static Models.Paste CreatePaste(Config config, TeknikEntities db, string content, string title = "", string syntax = "text", string expireUnit = "never", int expireLength = 1, string password = "")
public static Models.Paste CreatePaste(Config config, TeknikEntities db, string content, string title = "", string syntax = "text", ExpirationUnit expireUnit = ExpirationUnit.Never, int expireLength = 1, string password = "")
{
Models.Paste paste = new Models.Paste();
paste.DatePosted = DateTime.Now;
@@ -30,26 +30,26 @@ namespace Teknik.Areas.Paste
paste.Url = url;

// Figure out the expire date (null if 'never' or 'visit')
switch (expireUnit.ToLower())
switch (expireUnit)
{
case "never":
case ExpirationUnit.Never:
break;
case "view":
case ExpirationUnit.Views:
paste.MaxViews = expireLength;
break;
case "minute":
case ExpirationUnit.Minutes:
paste.ExpireDate = paste.DatePosted.AddMinutes(expireLength);
break;
case "hour":
case ExpirationUnit.Hours:
paste.ExpireDate = paste.DatePosted.AddHours(expireLength);
break;
case "day":
case ExpirationUnit.Days:
paste.ExpireDate = paste.DatePosted.AddDays(expireLength);
break;
case "month":
case ExpirationUnit.Months:
paste.ExpireDate = paste.DatePosted.AddMonths(expireLength);
break;
case "year":
case ExpirationUnit.Years:
paste.ExpireDate = paste.DatePosted.AddYears(expireLength);
break;
default:

+ 2
- 0
Teknik/Areas/Paste/ViewModels/PasswordViewModel.cs Bestand weergeven

@@ -9,6 +9,8 @@ namespace Teknik.Areas.Paste.ViewModels
{
public class PasswordViewModel : ViewModelBase
{
public string ActionUrl { get; set; }

public string Url { get; set; }

public string Type { get; set; }

+ 2
- 1
Teknik/Areas/Paste/ViewModels/PasteCreateViewModel.cs Bestand weergeven

@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations;
using Teknik.Utilities;
using Teknik.ViewModels;

namespace Teknik.Areas.Paste.ViewModels
@@ -15,7 +16,7 @@ namespace Teknik.Areas.Paste.ViewModels
[Range(1, int.MaxValue)]
public int? ExpireLength { get; set; }

public string ExpireUnit { get; set; }
public ExpirationUnit ExpireUnit { get; set; }
public string Password { get; set; }


+ 16
- 0
Teknik/Areas/Paste/ViewModels/PasteEditViewModel.cs Bestand weergeven

@@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
using Teknik.ViewModels;

namespace Teknik.Areas.Paste.ViewModels
{
public class PasteEditViewModel : ViewModelBase
{
public string Url { get; set; }

public string Content { get; set; }

public string Title { get; set; }

public string Syntax { get; set; }
}
}

+ 1
- 0
Teknik/Areas/Paste/ViewModels/PasteViewModel.cs Bestand weergeven

@@ -13,6 +13,7 @@ namespace Teknik.Areas.Paste.ViewModels
public string Password { get; set; }
public bool Hide { get; set; }
public DateTime DatePosted { get; set; }
public string Username { get; set; }

public List<Vault.Models.Vault> Vaults { get; set; }
}

+ 48
- 0
Teknik/Areas/Paste/Views/Paste/Edit.cshtml Bestand weergeven

@@ -0,0 +1,48 @@
@model Teknik.Areas.Paste.ViewModels.PasteViewModel

<bundle src="css/paste.edit.min.css" append-version="true"></bundle>

<div class="container">
<div class="row">
<div class="col-sm-10 col-sm-offset-1 text-center">
<b>@Html.ValidationSummary(true, "The input is not valid")</b>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<!form class="form-horizontal" name="editor" method="post" action="@Url.SubRouteUrl("p", "Paste.Action", new { action = "EditSubmit", url = Model.Url })">
<input type="hidden" class="form-control" name="Url" id="url" value="@Model.Url">
<div class="form-group">
<div class="col-sm-10 col-sm-offset-1">
<textarea class="form-control" name="Content" id="content" rows="20">@Model.Content</textarea>
</div>
</div>
<div class="form-group">
<label for="title" class="col-sm-2 col-sm-offset-1 control-label">Title</label>
<div class="col-sm-4">
<input type="text" class="form-control" name="Title" id="title" value="@Model.Title">
</div>
<div class="col-sm-1 col-sm-offset-2">
<button type="submit" class="btn btn-primary pull-right" id="pasteSubmit">Save</button>
</div>
</div>
<div class="form-group">
<label for="syntax" class="col-sm-2 col-sm-offset-1 control-label">Syntax</label>
<div class="col-sm-4">
<select class="form-control" name="Syntax" id="syntax">
<!option value=""@((string.IsNullOrEmpty(Model.Syntax)) ? " selected" : string.Empty)>Text</!option>

@foreach (var format in HighlightHelper.Languages.GroupBy(l => l.Value).ToList())
{
<!option value="@(format?.FirstOrDefault().Key)"@((Model.Syntax == format?.FirstOrDefault().Key) ? " selected" : string.Empty)>@(format?.Key)</!option>
}

</select>
</div>
</div>
</!form>
</div>
</div>
</div>

<bundle src="js/paste.edit.min.js" append-version="true"></bundle>

+ 27
- 12
Teknik/Areas/Paste/Views/Paste/Full.cshtml Bestand weergeven

@@ -16,6 +16,7 @@

<script>
var createVaultURL = '@Url.SubRouteUrl("vault", "Vault.NewVaultFromService", new { type = "Paste" })';
var deletePasteURL = '@Url.SubRouteUrl("p", "Paste.Delete")';
</script>

<div class="container">
@@ -27,21 +28,35 @@
<hr />
<div class="row">
<div class="col-sm-12 pull-left">
<div class="btn-group" role="group">
<a role="button" class="btn btn-default" href="@Url.SubRouteUrl("p", "Paste.Simple", new { url = Model.Url })">Simple</a>
<a role="button" class="btn btn-default" href="@Url.SubRouteUrl("p", "Paste.Raw", new { url = Model.Url })">Raw</a>
<a role="button" class="btn btn-default" href="@Url.SubRouteUrl("p", "Paste.Download", new { url = Model.Url })">Download</a>
<button type="button" class="btn btn-default" id="create-vault" data-paste-url="@Model.Url" data-paste-title="@((string.IsNullOrEmpty(Model.Title)) ? "Untitled" : Model.Title)">Create Vault</button>
@if (User.Identity.IsAuthenticated && Model.Vaults != null && Model.Vaults.Any())
<div class="btn-toolbar" role="toolbar">
<div class="btn-group" role="group">
<a role="button" class="btn btn-default" href="@Url.SubRouteUrl("p", "Paste.Simple", new { url = Model.Url })">Simple</a>
<a role="button" class="btn btn-default" href="@Url.SubRouteUrl("p", "Paste.Raw", new { url = Model.Url })">Raw</a>
<a role="button" class="btn btn-default" href="@Url.SubRouteUrl("p", "Paste.Download", new { url = Model.Url })">Download</a>
</div>
@if (User.Identity.IsAuthenticated && User.Identity.Name == Model.Username)
{
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">Add to Vault <span class="caret"></span></button>
<ul class="dropdown-menu pull-right" id="add-to-vault-menu">
@foreach (Vault item in Model.Vaults)
<div class="btn-group pull-right" role="group">
<a role="button" class="btn btn-default" href="@Url.SubRouteUrl("p", "Paste.Edit", new { url = Model.Url })"><span class="text-primary">Edit</span></a>
<button type="button" class="btn btn-default" id="delete-paste" data-paste-url="@Model.Url"><span class="text-danger">Delete</span></button>
</div>
}
<div class="btn-group pull-right" role="group">
<button type="button" class="btn btn-default" id="create-vault" data-paste-url="@Model.Url" data-paste-title="@((string.IsNullOrEmpty(Model.Title)) ? "Untitled" : Model.Title)">Create Vault</button>
@if (User.Identity.IsAuthenticated)
{
@if (Model.Vaults != null && Model.Vaults.Any())
{
<li><a href="#" class="add-to-vault" data-add-to-vault-url="@Url.SubRouteUrl("vault", "Vault.EditVault", new { url = item.Url, type = "Paste" })" data-paste-url="@Model.Url" data-paste-title="@((string.IsNullOrEmpty(Model.Title)) ? "Untitled" : Model.Title)">@item.Title</a></li>
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">Add to Vault <span class="caret"></span></button>
<ul class="dropdown-menu pull-right" id="add-to-vault-menu">
@foreach (Vault item in Model.Vaults)
{
<li><a href="#" class="add-to-vault" data-add-to-vault-url="@Url.SubRouteUrl("vault", "Vault.EditVault", new { url = item.Url, type = "Paste" })" data-paste-url="@Model.Url" data-paste-title="@((string.IsNullOrEmpty(Model.Title)) ? "Untitled" : Model.Title)">@item.Title</a></li>
}
</ul>
}
</ul>
}
}
</div>
</div>
</div>
</div>

+ 4
- 7
Teknik/Areas/Paste/Views/Paste/Index.cshtml Bestand weergeven

@@ -46,13 +46,10 @@
</div>
<div class="col-sm-4" id="unit-div">
<select class="form-control" name="ExpireUnit" id="expireunit">
<!option value="never">Never</!option>
<!option value="view">Views</!option>
<!option value="minute">Minutes</!option>
<!option value="hour">Hours</!option>
<!option value="day">Days</!option>
<!option value="month">Months</!option>
<!option value="year">Years</!option>
@foreach (ExpirationUnit unit in Enum.GetValues(typeof(ExpirationUnit)))
{
<!option value="@unit">@unit.ToString()</!option>
}
</select>
</div>
</div>

+ 1
- 1
Teknik/Areas/Paste/Views/Paste/PasswordNeeded.cshtml Bestand weergeven

@@ -13,7 +13,7 @@
}
<div class="row text-center">
<div class="col-sm-6 col-sm-offset-3">
<!form class="form-inline" method="post" action="@Url.SubRouteUrl("paste", "Paste.View")">
<!form class="form-inline" method="post" action="@Model.ActionUrl">
<h3>This paste is password protected</h3>
<input type="hidden" name="type" value="@Model.Type">
<input type="hidden" name="url" value="@Model.Url">

+ 29
- 0
Teknik/Content/Paste/EditPaste.css Bestand weergeven

@@ -0,0 +1,29 @@
pre {
background-color: #ffffff;
}

pre code,
pre .line-number {
/* Ukuran line-height antara teks di dalam tag <code> dan <span class="line-number"> harus sama! */
font:normal normal 12px/14px "Courier New",Courier,Monospace;
color:black;
display:block;
}

pre .line-number {
float:left;
margin:0 1em 0 -1em;
padding-top: 5px;
border-right:1px solid;
text-align:right;
}

pre .line-number span {
display:block;
padding:0 .5em 0 1em;
}

pre .cl {
display:block;
clear:both;
}

+ 749
- 0
Teknik/Data/Migrations/20190124080711_PasteEditDate.Designer.cs Bestand weergeven

@@ -0,0 +1,749 @@
// <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 Teknik.Data;

namespace Teknik.Data.Migrations
{
[DbContext(typeof(TeknikEntities))]
[Migration("20190124080711_PasteEditDate")]
partial class PasteEditDate
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.0-preview3-35497")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

modelBuilder.Entity("Teknik.Areas.Blog.Models.Blog", b =>
{
b.Property<int>("BlogId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<int>("UserId");

b.HasKey("BlogId");

b.HasIndex("UserId");

b.ToTable("Blogs");
});

modelBuilder.Entity("Teknik.Areas.Blog.Models.BlogPost", b =>
{
b.Property<int>("BlogPostId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("Article");

b.Property<int>("BlogId");

b.Property<DateTime>("DateEdited");

b.Property<DateTime>("DatePosted");

b.Property<DateTime>("DatePublished");

b.Property<bool>("Published");

b.Property<bool>("System");

b.Property<string>("Title");

b.HasKey("BlogPostId");

b.HasIndex("BlogId");

b.ToTable("BlogPosts");
});

modelBuilder.Entity("Teknik.Areas.Blog.Models.BlogPostComment", b =>
{
b.Property<int>("BlogPostCommentId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("Article");

b.Property<int>("BlogPostId");

b.Property<DateTime>("DateEdited");

b.Property<DateTime>("DatePosted");

b.Property<int?>("UserId");

b.HasKey("BlogPostCommentId");

b.HasIndex("BlogPostId");

b.HasIndex("UserId");

b.ToTable("BlogPostComments");
});

modelBuilder.Entity("Teknik.Areas.Blog.Models.BlogPostTag", b =>
{
b.Property<int>("BlogPostTagId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<int>("BlogPostId");

b.Property<string>("Description");

b.Property<string>("Name");

b.HasKey("BlogPostTagId");

b.HasIndex("BlogPostId");

b.ToTable("BlogPostTags");
});

modelBuilder.Entity("Teknik.Areas.Contact.Models.Contact", b =>
{
b.Property<int>("ContactId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<DateTime>("DateAdded");

b.Property<string>("Email");

b.Property<string>("Message");

b.Property<string>("Name");

b.Property<string>("Subject");

b.HasKey("ContactId");

b.ToTable("Contact");
});

modelBuilder.Entity("Teknik.Areas.Paste.Models.Paste", b =>
{
b.Property<int>("PasteId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<int>("BlockSize");

b.Property<string>("Content");

b.Property<DateTime>("DateEdited");

b.Property<DateTime>("DatePosted");

b.Property<string>("DeleteKey")
.HasAnnotation("CaseSensitive", true);

b.Property<DateTime?>("ExpireDate");

b.Property<string>("FileName")
.HasAnnotation("CaseSensitive", true);

b.Property<string>("HashedPassword")
.HasAnnotation("CaseSensitive", true);

b.Property<string>("IV")
.HasAnnotation("CaseSensitive", true);

b.Property<string>("Key")
.HasAnnotation("CaseSensitive", true);

b.Property<int>("KeySize");

b.Property<int>("MaxViews");

b.Property<string>("Syntax");

b.Property<string>("Title");

b.Property<string>("Url")
.HasAnnotation("CaseSensitive", true);

b.Property<int?>("UserId");

b.Property<int>("Views");

b.HasKey("PasteId");

b.HasIndex("UserId");

b.ToTable("Pastes");
});

modelBuilder.Entity("Teknik.Areas.Podcast.Models.Podcast", b =>
{
b.Property<int>("PodcastId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<DateTime>("DateEdited");

b.Property<DateTime>("DatePosted");

b.Property<DateTime>("DatePublished");

b.Property<string>("Description");

b.Property<int>("Episode");

b.Property<bool>("Published");

b.Property<string>("Title");

b.HasKey("PodcastId");

b.ToTable("Podcasts");
});

modelBuilder.Entity("Teknik.Areas.Podcast.Models.PodcastComment", b =>
{
b.Property<int>("PodcastCommentId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("Article");

b.Property<DateTime>("DateEdited");

b.Property<DateTime>("DatePosted");

b.Property<int>("PodcastId");

b.Property<int>("UserId");

b.HasKey("PodcastCommentId");

b.HasIndex("PodcastId");

b.HasIndex("UserId");

b.ToTable("PodcastComments");
});

modelBuilder.Entity("Teknik.Areas.Podcast.Models.PodcastFile", b =>
{
b.Property<int>("PodcastFileId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<long>("ContentLength");

b.Property<string>("ContentType");

b.Property<string>("FileName");

b.Property<string>("Path");

b.Property<int>("PodcastId");

b.Property<int>("Size");

b.HasKey("PodcastFileId");

b.HasIndex("PodcastId");

b.ToTable("PodcastFiles");
});

modelBuilder.Entity("Teknik.Areas.Podcast.Models.PodcastTag", b =>
{
b.Property<int>("PodcastTagId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("Description");

b.Property<string>("Name");

b.Property<int>("PodcastId");

b.HasKey("PodcastTagId");

b.HasIndex("PodcastId");

b.ToTable("PodcastTags");
});

modelBuilder.Entity("Teknik.Areas.Shortener.Models.ShortenedUrl", b =>
{
b.Property<int>("ShortenedUrlId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<DateTime>("DateAdded");

b.Property<string>("OriginalUrl");

b.Property<string>("ShortUrl")
.HasAnnotation("CaseSensitive", true);

b.Property<int?>("UserId");

b.Property<int>("Views");

b.HasKey("ShortenedUrlId");

b.HasIndex("UserId");

b.ToTable("ShortenedUrls");
});

modelBuilder.Entity("Teknik.Areas.Stats.Models.Takedown", b =>
{
b.Property<int>("TakedownId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("ActionTaken");

b.Property<DateTime>("DateActionTaken");

b.Property<DateTime>("DateRequested");

b.Property<string>("Reason");

b.Property<string>("Requester");

b.Property<string>("RequesterContact");

b.HasKey("TakedownId");

b.ToTable("Takedowns");
});

modelBuilder.Entity("Teknik.Areas.Stats.Models.Transaction", b =>
{
b.Property<int>("TransactionId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<decimal>("Amount")
.HasColumnType("decimal(19, 5)");

b.Property<int>("Currency");

b.Property<DateTime>("DateSent");

b.Property<string>("Reason");

b.HasKey("TransactionId");

b.ToTable("Transactions");
});

modelBuilder.Entity("Teknik.Areas.Upload.Models.Upload", b =>
{
b.Property<int>("UploadId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<int>("BlockSize");

b.Property<long>("ContentLength");

b.Property<string>("ContentType");

b.Property<DateTime>("DateUploaded");

b.Property<string>("DeleteKey")
.HasAnnotation("CaseSensitive", true);

b.Property<int>("Downloads");

b.Property<DateTime?>("ExpireDate");

b.Property<string>("FileName")
.HasAnnotation("CaseSensitive", true);

b.Property<string>("IV")
.HasAnnotation("CaseSensitive", true);

b.Property<string>("Key")
.HasAnnotation("CaseSensitive", true);

b.Property<int>("KeySize");

b.Property<int>("MaxDownloads");

b.Property<int?>("Takedown_TakedownId");

b.Property<string>("Url")
.HasAnnotation("CaseSensitive", true);

b.Property<int?>("UserId");

b.HasKey("UploadId");

b.HasIndex("Takedown_TakedownId");

b.HasIndex("UserId");

b.ToTable("Uploads");
});

modelBuilder.Entity("Teknik.Areas.Users.Models.InviteCode", b =>
{
b.Property<int>("InviteCodeId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<bool>("Active");

b.Property<int?>("ClaimedUserId");

b.Property<string>("Code")
.HasAnnotation("CaseSensitive", true);

b.Property<int?>("OwnerId");

b.HasKey("InviteCodeId");

b.HasIndex("ClaimedUserId")
.IsUnique()
.HasFilter("[ClaimedUserId] IS NOT NULL");

b.HasIndex("OwnerId");

b.ToTable("InviteCodes");
});

modelBuilder.Entity("Teknik.Areas.Users.Models.LoginInfo", b =>
{
b.Property<int>("LoginInfoId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("LoginProvider");

b.Property<string>("ProviderDisplayName");

b.Property<string>("ProviderKey");

b.Property<int>("UserId");

b.HasKey("LoginInfoId");

b.HasIndex("UserId");

b.ToTable("UserLogins");
});

modelBuilder.Entity("Teknik.Areas.Users.Models.User", b =>
{
b.Property<int>("UserId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("Username");

b.HasKey("UserId");

b.ToTable("Users");
});

modelBuilder.Entity("Teknik.Areas.Vault.Models.Vault", b =>
{
b.Property<int>("VaultId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<DateTime>("DateCreated");

b.Property<DateTime>("DateEdited");

b.Property<string>("Description");

b.Property<string>("Title");

b.Property<string>("Url");

b.Property<int?>("UserId");

b.Property<int>("Views");

b.HasKey("VaultId");

b.HasIndex("UserId");

b.ToTable("Vaults");
});

modelBuilder.Entity("Teknik.Areas.Vault.Models.VaultItem", b =>
{
b.Property<int>("VaultItemId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<DateTime>("DateAdded");

b.Property<string>("Description");

b.Property<string>("Discriminator")
.IsRequired();

b.Property<string>("Title");

b.Property<int>("VaultId");

b.HasKey("VaultItemId");

b.HasIndex("VaultId");

b.ToTable("VaultItems");

b.HasDiscriminator<string>("Discriminator").HasValue("VaultItem");
});

modelBuilder.Entity("Teknik.Areas.Vault.Models.PasteVaultItem", b =>
{
b.HasBaseType("Teknik.Areas.Vault.Models.VaultItem");

b.Property<int>("PasteId");

b.HasIndex("PasteId");

b.HasDiscriminator().HasValue("PasteVaultItem");
});

modelBuilder.Entity("Teknik.Areas.Vault.Models.UploadVaultItem", b =>
{
b.HasBaseType("Teknik.Areas.Vault.Models.VaultItem");

b.Property<int>("UploadId");

b.HasIndex("UploadId");

b.HasDiscriminator().HasValue("UploadVaultItem");
});

modelBuilder.Entity("Teknik.Areas.Blog.Models.Blog", b =>
{
b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Blog.Models.BlogPost", b =>
{
b.HasOne("Teknik.Areas.Blog.Models.Blog", "Blog")
.WithMany("BlogPosts")
.HasForeignKey("BlogId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Blog.Models.BlogPostComment", b =>
{
b.HasOne("Teknik.Areas.Blog.Models.BlogPost", "BlogPost")
.WithMany("Comments")
.HasForeignKey("BlogPostId")
.OnDelete(DeleteBehavior.Cascade);

b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany()
.HasForeignKey("UserId");
});

modelBuilder.Entity("Teknik.Areas.Blog.Models.BlogPostTag", b =>
{
b.HasOne("Teknik.Areas.Blog.Models.BlogPost", "BlogPost")
.WithMany("Tags")
.HasForeignKey("BlogPostId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Paste.Models.Paste", b =>
{
b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany("Pastes")
.HasForeignKey("UserId");
});

modelBuilder.Entity("Teknik.Areas.Podcast.Models.PodcastComment", b =>
{
b.HasOne("Teknik.Areas.Podcast.Models.Podcast", "Podcast")
.WithMany("Comments")
.HasForeignKey("PodcastId")
.OnDelete(DeleteBehavior.Cascade);

b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Podcast.Models.PodcastFile", b =>
{
b.HasOne("Teknik.Areas.Podcast.Models.Podcast", "Podcast")
.WithMany("Files")
.HasForeignKey("PodcastId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Podcast.Models.PodcastTag", b =>
{
b.HasOne("Teknik.Areas.Podcast.Models.Podcast", "Podcast")
.WithMany("Tags")
.HasForeignKey("PodcastId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Shortener.Models.ShortenedUrl", b =>
{
b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany("ShortenedUrls")
.HasForeignKey("UserId");
});

modelBuilder.Entity("Teknik.Areas.Upload.Models.Upload", b =>
{
b.HasOne("Teknik.Areas.Stats.Models.Takedown")
.WithMany("Attachments")
.HasForeignKey("Takedown_TakedownId");

b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany("Uploads")
.HasForeignKey("UserId");
});

modelBuilder.Entity("Teknik.Areas.Users.Models.InviteCode", b =>
{
b.HasOne("Teknik.Areas.Users.Models.User", "ClaimedUser")
.WithOne("ClaimedInviteCode")
.HasForeignKey("Teknik.Areas.Users.Models.InviteCode", "ClaimedUserId");

b.HasOne("Teknik.Areas.Users.Models.User", "Owner")
.WithMany("OwnedInviteCodes")
.HasForeignKey("OwnerId");
});

modelBuilder.Entity("Teknik.Areas.Users.Models.LoginInfo", b =>
{
b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany("Logins")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Users.Models.User", b =>
{
b.OwnsOne("Teknik.Areas.Users.Models.BlogSettings", "BlogSettings", b1 =>
{
b1.Property<int>("UserId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b1.Property<string>("Description")
.HasColumnName("Description");

b1.Property<string>("Title")
.HasColumnName("Title");

b1.HasKey("UserId");

b1.ToTable("Users");

b1.HasOne("Teknik.Areas.Users.Models.User")
.WithOne("BlogSettings")
.HasForeignKey("Teknik.Areas.Users.Models.BlogSettings", "UserId")
.OnDelete(DeleteBehavior.Cascade);
});

b.OwnsOne("Teknik.Areas.Users.Models.UploadSettings", "UploadSettings", b1 =>
{
b1.Property<int>("UserId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b1.Property<bool>("Encrypt")
.HasColumnName("Encrypt");

b1.Property<int>("ExpirationLength")
.HasColumnName("ExpirationLength");

b1.Property<int>("ExpirationUnit")
.HasColumnName("ExpirationUnit");

b1.HasKey("UserId");

b1.ToTable("Users");

b1.HasOne("Teknik.Areas.Users.Models.User")
.WithOne("UploadSettings")
.HasForeignKey("Teknik.Areas.Users.Models.UploadSettings", "UserId")
.OnDelete(DeleteBehavior.Cascade);
});

b.OwnsOne("Teknik.Areas.Users.Models.UserSettings", "UserSettings", b1 =>
{
b1.Property<int>("UserId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b1.Property<string>("About")
.HasColumnName("About");

b1.Property<string>("Quote")
.HasColumnName("Quote");

b1.Property<string>("Website")
.HasColumnName("Website");

b1.HasKey("UserId");

b1.ToTable("Users");

b1.HasOne("Teknik.Areas.Users.Models.User")
.WithOne("UserSettings")
.HasForeignKey("Teknik.Areas.Users.Models.UserSettings", "UserId")
.OnDelete(DeleteBehavior.Cascade);
});
});

modelBuilder.Entity("Teknik.Areas.Vault.Models.Vault", b =>
{
b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany("Vaults")
.HasForeignKey("UserId");
});

modelBuilder.Entity("Teknik.Areas.Vault.Models.VaultItem", b =>
{
b.HasOne("Teknik.Areas.Vault.Models.Vault", "Vault")
.WithMany("VaultItems")
.HasForeignKey("VaultId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Vault.Models.PasteVaultItem", b =>
{
b.HasOne("Teknik.Areas.Paste.Models.Paste", "Paste")
.WithMany("PasteVaultItems")
.HasForeignKey("PasteId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Vault.Models.UploadVaultItem", b =>
{
b.HasOne("Teknik.Areas.Upload.Models.Upload", "Upload")
.WithMany("UploadVaultItems")
.HasForeignKey("UploadId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

+ 24
- 0
Teknik/Data/Migrations/20190124080711_PasteEditDate.cs Bestand weergeven

@@ -0,0 +1,24 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;

namespace Teknik.Data.Migrations
{
public partial class PasteEditDate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "DateEdited",
table: "Pastes",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
}

protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "DateEdited",
table: "Pastes");
}
}
}

+ 2
- 0
Teknik/Data/Migrations/TeknikEntitiesModelSnapshot.cs Bestand weergeven

@@ -138,6 +138,8 @@ namespace Teknik.Data.Migrations

b.Property<string>("Content");

b.Property<DateTime>("DateEdited");

b.Property<DateTime>("DatePosted");

b.Property<string>("DeleteKey")

+ 7
- 0
Teknik/Routes.cs Bestand weergeven

@@ -417,6 +417,13 @@ namespace Teknik
template: "Download/{url}/{password?}",
defaults: new { area = "Paste", controller = "Paste", action = "ViewPaste", type = "Download" }
);
routes.MapSubdomainRoute(
name: "Paste.Edit",
domains: new List<string>() { config.Host },
subDomains: new List<string>() { "paste", "p" },
template: "Edit/{url}/{password?}",
defaults: new { area = "Paste", controller = "Paste", action = "Edit" }
);
routes.MapSubdomainRoute(
name: "Paste.Delete",
domains: new List<string>() { config.Host },

+ 3
- 0
Teknik/Scripts/Paste/EditPaste.js Bestand weergeven

@@ -0,0 +1,3 @@
$(document).ready(function () {
$('#content').focus();
});

+ 32
- 1
Teknik/Scripts/Paste/ViewPaste.js Bestand weergeven

@@ -1,10 +1,41 @@
/* globals createVaultURL */
/* globals createVaultURL, deletePasteURL */
$(document).ready(function () {
linkCreateVault($('#create-vault'));

$('#add-to-vault-menu').find('.add-to-vault').each(function () {
linkAddToVault($(this));
});

$('#delete-paste').click(function () {
var id = $(this).data('paste-url');

bootbox.confirm("Are you sure you want to delete this paste?", function (result) {
if (result) {
$.ajax({
type: "POST",
url: deletePasteURL,
data: { id: id },
headers: { 'X-Requested-With': 'XMLHttpRequest' },
xhrFields: {
withCredentials: true
},
success: function (response) {
if (response.result) {
window.location = response.redirect;
}
else {
$("#top_msg").css('display', 'inline', 'important');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(response) + '</div>');
}
},
error: function (response) {
$("#top_msg").css('display', 'inline', 'important');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(response.responseText) + '</div>');
}
});
}
});
});
});

function linkCreateVault(element) {

+ 3
- 0
Teknik/Teknik.csproj Bestand weergeven

@@ -96,6 +96,9 @@
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="Areas\Paste\Views\Paste\Edit.cshtml">
<Pack>$(IncludeRazorContentInPack)</Pack>
</Content>
<Content Update="Areas\User\Views\User\Settings\ClientView.cshtml">
<Pack>$(IncludeRazorContentInPack)</Pack>
</Content>

+ 19
- 0
Teknik/bundleconfig.json Bestand weergeven

@@ -99,6 +99,7 @@
{
"outputFileName": "./wwwroot/js/paste.view.min.js",
"inputFiles": [
"./wwwroot/lib/bootbox/js/bootbox.js",
"./wwwroot/js/app/lib/Prism/prism.js",
"./wwwroot/js/app/lib/Prism/prism-line-numbers.js",
"./wwwroot/js/app/lib/Prism/prism-line-highlight.js",
@@ -113,6 +114,24 @@
"./wwwroot/css/app/lib/Prism/prism-vs.css"
]
},
{
"outputFileName": "./wwwroot/js/paste.edit.min.js",
"inputFiles": [
"./wwwroot/js/app/lib/Prism/prism.js",
"./wwwroot/js/app/lib/Prism/prism-line-numbers.js",
"./wwwroot/js/app/lib/Prism/prism-line-highlight.js",
"./wwwroot/js/app/Paste/EditPaste.js"
]
},
{
"outputFileName": "./wwwroot/css/paste.edit.min.css",
"inputFiles": [
"./wwwroot/css/app/lib/Prism/prism-line-numbers.css",
"./wwwroot/css/app/lib/Prism/prism-line-highlight.css",
"./wwwroot/css/app/lib/Prism/prism-vs.css",
"./wwwroot/css/app/Paste/EditPaste.css"
]
},
{
"outputFileName": "./wwwroot/js/podcast.min.js",
"inputFiles": [

Laden…
Annuleren
Opslaan