Skip to content

Commit

Permalink
Add support for deleting answers
Browse files Browse the repository at this point in the history
  • Loading branch information
mythz committed May 9, 2024
1 parent 3067df9 commit e798989
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 21 deletions.
22 changes: 22 additions & 0 deletions MyApp.ServiceInterface/App/DeleteAnswersCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Data;
using ServiceStack;
using ServiceStack.OrmLite;
using MyApp.Data;
using MyApp.ServiceModel;

namespace MyApp.ServiceInterface.App;

public class DeleteAnswersCommand(IDbConnection db) : IAsyncCommand<DeleteAnswers>
{
public async Task ExecuteAsync(DeleteAnswers request)
{
foreach (var refId in request.Ids)
{
await db.DeleteAsync<Vote>(x => x.RefId == refId);
await db.DeleteByIdAsync<Post>(refId);
await db.DeleteAsync<StatTotals>(x => x.Id == refId);
await db.DeleteAsync<Notification>(x => x.RefId == refId);
await db.DeleteAsync<PostEmail>(x => x.RefId == refId);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

namespace MyApp.ServiceInterface.App;

public class DeletePostCommand(AppConfig appConfig, IDbConnection db) : IAsyncCommand<DeletePost>
public class DeletePostsCommand(AppConfig appConfig, IDbConnection db) : IAsyncCommand<DeletePosts>
{
public async Task ExecuteAsync(DeletePost request)
public async Task ExecuteAsync(DeletePosts request)
{
foreach (var postId in request.Ids)
{
Expand Down
9 changes: 8 additions & 1 deletion MyApp.ServiceInterface/Data/QuestionsProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public async Task<QuestionFiles> GetQuestionAsync(int id)
var answerPath = GetAnswerPath(postId, userName);

var file = fs.GetFile(answerPath)
?? await r2.GetFileAsync(answerPath);
?? await r2.GetFileAsync(answerPath);

return file;
}
Expand Down Expand Up @@ -317,6 +317,13 @@ public async Task DeleteQuestionFilesAsync(int id)
await r2.DeleteFilesAsync(remoteQuestionFiles.Files.Select(x => x.VirtualPath));
}

public async Task DeleteAnswerFileAsync(string answerId)
{
var answerFile = await GetAnswerFileAsync(answerId);
if (answerFile != null)
await DeleteFileAsync(answerFile.VirtualPath);
}

public async Task<string?> GetAnswerBodyAsync(string answerId)
{
var answerFile = await GetAnswerFileAsync(answerId);
Expand Down
17 changes: 13 additions & 4 deletions MyApp.ServiceInterface/Data/Tasks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,16 @@ public class NewComment
public DateTime LastUpdated { get; set; }
}

public class DeletePost
public class DeletePosts
{
public required List<int> Ids { get; set; }
}

public class DeleteAnswers
{
public required List<string> Ids { get; set; }
}

public class CreatePostJobs
{
public required List<PostJob> PostJobs { get; set; }
Expand Down Expand Up @@ -101,8 +106,11 @@ public class DbWrites : IGet, IReturn<EmptyResponse>
[Command<UpdatePostCommand>]
public Post? UpdatePost { get; set; }

[Command<DeletePostCommand>]
public DeletePost? DeletePost { get; set; }
[Command<DeletePostsCommand>]
public DeletePosts? DeletePosts { get; set; }

[Command<DeleteAnswersCommand>]
public DeleteAnswers? DeleteAnswers { get; set; }

[Command<StartJobCommand>]
public StartJob? StartJob { get; set; }
Expand Down Expand Up @@ -192,5 +200,6 @@ public class SearchTasks
{
public int? AddPostToIndex { get; set; }
public string? AddAnswerToIndex { get; set; }
public int? DeletePost { get; set; }
public List<int>? DeletePosts { get; set; }
public List<string>? DeleteAnswers { get; set; }
}
27 changes: 25 additions & 2 deletions MyApp.ServiceInterface/QuestionServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,34 @@ public async Task<object> Any(DeleteQuestion request)
rendererCache.DeleteCachedQuestionPostHtml(request.Id);
MessageProducer.Publish(new DbWrites
{
DeletePost = new() { Ids = [request.Id] },
DeletePosts = new() { Ids = [request.Id] },
});
MessageProducer.Publish(new SearchTasks
{
DeletePost = request.Id,
DeletePosts = [request.Id],
});

if (request.ReturnUrl != null && request.ReturnUrl.StartsWith('/') && request.ReturnUrl.IndexOf(':') < 0)
return HttpResult.Redirect(request.ReturnUrl, HttpStatusCode.TemporaryRedirect);

return new EmptyResponse();
}

public async Task<object> Any(DeleteAnswer request)
{
if (!request.Id.Contains('-'))
throw new ArgumentException("Invalid Answer Id", nameof(request.Id));
var postId = request.Id.LeftPart('-').ToInt();

await questions.DeleteAnswerFileAsync(request.Id);
rendererCache.DeleteCachedQuestionPostHtml(postId);
MessageProducer.Publish(new DbWrites
{
DeleteAnswers = new() { Ids = [request.Id] },
});
MessageProducer.Publish(new SearchTasks
{
DeleteAnswers = [request.Id],
});

if (request.ReturnUrl != null && request.ReturnUrl.StartsWith('/') && request.ReturnUrl.IndexOf(':') < 0)
Expand Down
19 changes: 15 additions & 4 deletions MyApp.ServiceInterface/SearchServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,22 @@ await db.ExecuteNonQueryAsync($@"INSERT INTO {nameof(PostFts)} (
)");
}

if (request.DeletePost != null)
if (request.DeletePosts != null)
{
var id = request.DeletePost.Value;
log.LogInformation("[SEARCH] Deleting Post '{PostId}' from Search Index...", id);
await db.ExecuteNonQueryAsync($"DELETE FROM PostFts where RefId = '{id}' or RefId LIKE '{id}-%'");
foreach (var id in request.DeletePosts)
{
log.LogInformation("[SEARCH] Deleting Post '{PostId}' from Search Index...", id);
await db.ExecuteNonQueryAsync($"DELETE FROM PostFts where RefId = '{id}' or RefId LIKE '{id}-%'");
}
}

if (request.DeleteAnswers != null)
{
foreach (var refId in request.DeleteAnswers)
{
log.LogInformation("[SEARCH] Deleting Answer '{PostId}' from Search Index...", refId);
await db.ExecuteNonQueryAsync($"DELETE FROM PostFts where RefId = @refId or RefId = @refId", new { refId });
}
}
}
}
9 changes: 9 additions & 0 deletions MyApp.ServiceModel/Posts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,15 @@ public class DeleteQuestion : IGet, IReturn<EmptyResponse>
public string? ReturnUrl { get; set; }
}

[ValidateHasRole(Roles.Moderator)]
public class DeleteAnswer : IGet, IReturn<EmptyResponse>
{
[ValidateNotEmpty]
public string Id { get; set; }

public string? ReturnUrl { get; set; }
}

public class GetRequestInfo : IGet, IReturn<string> {}

public class GetUserReputations : IGet, IReturn<GetUserReputationsResponse>
Expand Down
3 changes: 2 additions & 1 deletion MyApp/Components/Shared/AnswerPost.razor
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<span data-rep-user=@UserName class="text-xs font-semibold">@AppConfig.GetReputation(UserName)</span>
</div>
</div>
<div class="answer-aside"></div>
</div>
<div class="w-full">
@if (Question.Meta?.ModelVotes?.TryGetValue(Answer.CreatedBy!, out var votes) == true &&
Expand Down Expand Up @@ -73,7 +74,7 @@

@if (Question.Post.LockedDate == null)
{
<div data-answer=@Answer.RefId class="relative mt-4 text-sm">
<div class="relative mt-4 text-sm">
<div class="share-dialog absolute"></div>
<span class="share-link mr-2 cursor-pointer select-none text-indigo-700 dark:text-indigo-300 hover:text-indigo-500" title="Share this Answer">share</span>
<span class="edit-link mr-2 cursor-pointer select-none text-indigo-700 dark:text-indigo-300 hover:text-indigo-500" title="Edit this Answer">edit</span>
Expand Down
4 changes: 0 additions & 4 deletions MyApp/Configure.Db.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@ public class ConfigureDb : IHostingStartup
public const string AnalyticsDbPath = "App_Data/analytics.db";
public const string CreatorKitDbPath = "App_Data/creatorkit.db";
public const string ArchiveDbPath = "App_Data/archive.db";
#if DEBUG
public const string SearchDbPath = "../../pvq/dist/search.db";
#else
public const string SearchDbPath = "App_Data/search.db";
#endif

public void Configure(IWebHostBuilder builder) => builder
.ConfigureServices((context, services) => {
Expand Down
22 changes: 21 additions & 1 deletion MyApp/wwwroot/mjs/dtos.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* Options:
Date: 2024-05-05 17:08:25
Date: 2024-05-09 12:12:17
Version: 8.23
Tip: To override a DTO option, remove "//" prefix before updating
BaseUrl: https://localhost:5001
Expand Down Expand Up @@ -1541,6 +1541,15 @@ export class CalculateLeaderBoard {
getMethod() { return 'GET' }
createResponse() { return new CalculateLeaderboardResponse() }
}
export class CalculateTop1KLeaderboard {
/** @param {{modelsToExclude?:string}} [init] */
constructor(init) { Object.assign(this, init) }
/** @type {?string} */
modelsToExclude;
getTypeName() { return 'CalculateTop1KLeaderboard' }
getMethod() { return 'GET' }
createResponse() { return new CalculateLeaderboardResponse() }
}
export class GetLeaderboardStatsByTag {
/** @param {{tag?:string,modelsToExclude?:string}} [init] */
constructor(init) { Object.assign(this, init) }
Expand Down Expand Up @@ -1581,6 +1590,17 @@ export class DeleteQuestion {
getMethod() { return 'GET' }
createResponse() { return new EmptyResponse() }
}
export class DeleteAnswer {
/** @param {{id?:string,returnUrl?:string}} [init] */
constructor(init) { Object.assign(this, init) }
/** @type {string} */
id;
/** @type {?string} */
returnUrl;
getTypeName() { return 'DeleteAnswer' }
getMethod() { return 'GET' }
createResponse() { return new EmptyResponse() }
}
export class AnswerQuestion {
/** @param {{postId?:number,body?:string,refId?:string,refUrn?:string}} [init] */
constructor(init) { Object.assign(this, init) }
Expand Down
33 changes: 31 additions & 2 deletions MyApp/wwwroot/mjs/question.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { renderMarkdown } from "markdown"
import {
UserPostData, PostVote, GetQuestionFile,
AnswerQuestion, UpdateQuestion, PreviewMarkdown, GetAnswerBody, CreateComment, GetMeta,
DeleteQuestion, DeleteComment, GetUserReputations, CommentVote,
DeleteQuestion, DeleteAnswer, DeleteComment, GetUserReputations, CommentVote,
ShareContent, FlagContent, GetAnswer,
WaitForUpdate, GetLastUpdated,
} from "dtos.mjs"
Expand Down Expand Up @@ -436,6 +436,29 @@ const QuestionAside = {
}
}

const AnswerAside = {
template:`
<div v-if="isModerator" class="mt-2 flex justify-center">
<svg class="mr-1 align-sub text-gray-400 hover:text-gray-500 w-6 h-6 inline-block cursor-pointer" @click="deleteAnswer" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><title>Delete question</title><g fill="none" stroke="currentColor" stroke-linejoin="round" stroke-width="4"><path d="M9 10v34h30V10z"/><path stroke-linecap="round" d="M20 20v13m8-13v13M4 10h40"/><path d="m16 10l3.289-6h9.488L32 10z"/></g></svg>
</div>
`,
props:['id'],
setup(props) {
const { hasRole } = useAuth()
const isModerator = hasRole('Moderator')
const client = useClient()
async function deleteAnswer() {
if (confirm('Are you sure?')) {
const api = await client.api(new DeleteAnswer({ id:props.id }))
if (api.succeeded) {
location.href = location.href
}
}
}
return { deleteAnswer, isModerator }
}
}

const Comments = {
template:`
<div v-if="editing" class="mt-4 pb-1 flex w-full">
Expand Down Expand Up @@ -1067,6 +1090,7 @@ async function loadEditAnswers(ctx) {
edit = el.querySelector('.edit'),
preview = el.querySelector('.preview'),
previewHtml = preview?.innerHTML,
answerAside = el.querySelector('.answer-aside'),
footer = el.querySelector('.answer-footer')

const bus = new EventBus()
Expand All @@ -1080,7 +1104,12 @@ async function loadEditAnswers(ctx) {
footer.innerHTML = ''
}
} else {
console.warn(`could not find .edit'`)
console.warn(`could not find .edit'`, el)
}
if (answerAside) {
mount(answerAside, AnswerAside, { id })
} else {
console.warn(`could not find .answer-aside'`, el)
}
})
}
Expand Down

0 comments on commit e798989

Please sign in to comment.