From 1b3d039d6ae0775809cbff143372ec9c5565ed39 Mon Sep 17 00:00:00 2001 From: piaip Date: Wed, 21 May 2014 12:56:15 +0000 Subject: Support deletion for comments. git-svn-id: http://opensvn.csie.org/pttbbs/trunk@6002 63ad8ddf-47c3-0310-b6dd-a9e9d9715204 --- pttbbs/include/daemons.h | 5 +- pttbbs/include/proto.h | 6 ++- pttbbs/mbbsd/bbs.c | 25 +++++----- pttbbs/mbbsd/comments.c | 124 +++++++++++++++++++++++++++++++++++++++++++++-- pttbbs/mbbsd/psb.c | 48 +++++++++++++++--- 5 files changed, 182 insertions(+), 26 deletions(-) diff --git a/pttbbs/include/daemons.h b/pttbbs/include/daemons.h index 8e5d5b28..2ba227e3 100644 --- a/pttbbs/include/daemons.h +++ b/pttbbs/include/daemons.h @@ -145,14 +145,15 @@ enum BRCSTORED_OPERATIONS { enum { COMMENTD_REQ_ADD = 1, COMMENTD_REQ_QUERY_COUNT, - COMMENTD_REQ_QUERY_BODY = 3, + COMMENTD_REQ_QUERY_BODY, + COMMENTD_REQ_MARK_DELETED, }; typedef struct CommentBodyReq { time4_t time; time4_t ipv4; uint32_t userref; /* user.ctime */ - uint32_t type; + int32_t type; char userid[IDLEN + 1]; char msg[COMMENTLEN + 1]; } PACKSTRUCT CommentBodyReq; diff --git a/pttbbs/include/proto.h b/pttbbs/include/proto.h index 0e3ec112..58003099 100644 --- a/pttbbs/include/proto.h +++ b/pttbbs/include/proto.h @@ -182,14 +182,18 @@ void *CommentsOpen(const char *board, const char *file); int CommentsClose(void *ctx); int CommentsGetCount(void *ctx); const struct CommentBodyReq *CommentsRead(void *ctx, int i); +int CommentsDeleteFromTextFile(void *ctx, int i); #endif +void FormatCommentString(char *buf, size_t szbuf, int type, + const char *myid, int maxlength, + const char *msg, const char *tail); /* psb (panty and stocking browser) */ int psb_view_edit_history(const char *base, const char *subject, int maxrev, int current_as_base); int psb_recycle_bin(const char *base, const char *title); int psb_admin_edit(); -int pvcm_comment_manager(const char *board, const char *file); +int psb_comment_manager(const char *board, const char *file); /* chc */ void chc(int s, ChessGameMode mode); diff --git a/pttbbs/mbbsd/bbs.c b/pttbbs/mbbsd/bbs.c index d5530462..c36cbf4c 100644 --- a/pttbbs/mbbsd/bbs.c +++ b/pttbbs/mbbsd/bbs.c @@ -3134,19 +3134,8 @@ recommend(int ent, fileheader_t * fhdr, const char *direct) Cdate_mdHM(&now)); } -#ifdef OLDRECOMMEND - snprintf(buf, sizeof(buf), - ANSI_COLOR(1;31) "→ " ANSI_COLOR(33) "%s" - ANSI_RESET ANSI_COLOR(33) ":%-*s" ANSI_RESET - "推%s\n", - myid, maxlength, msg, tail); -#else - snprintf(buf, sizeof(buf), - "%s%s " ANSI_COLOR(33) "%s" ANSI_RESET ANSI_COLOR(33) - ":%-*s" ANSI_RESET "%s\n", - ctype_attr2[type], ctype[type], myid, - maxlength, msg, tail); -#endif // OLDRECOMMEND + FormatCommentString(buf, sizeof(buf), type, + myid, maxlength, msg, tail); } if (do_add_recommend(direct, fhdr, ent, buf, type) < 0) @@ -4499,7 +4488,15 @@ manage_post(int ent, fileheader_t * fhdr, const char *direct) { #ifdef USE_COMMENTD case 'v': - pvcm_comment_manager(currboard, fhdr->filename); + { + boardheader_t *bp = bp = getbcache(currbid); + assert(bp); + if (!(bp->brdattr & BRD_BM_MASK_CONTENT)) { + vmsg("要先開啟刪特定文字的權限。"); + return FULLUPDATE; + } + psb_comment_manager(currboard, fhdr->filename); + } break; #endif } diff --git a/pttbbs/mbbsd/comments.c b/pttbbs/mbbsd/comments.c index 954de5f8..8546478d 100644 --- a/pttbbs/mbbsd/comments.c +++ b/pttbbs/mbbsd/comments.c @@ -10,6 +10,36 @@ typedef struct { CommentBodyReq *resp; } CommentsCtx; +void FormatCommentString(char *buf, size_t szbuf, int type, + const char *myid, int maxlength, + const char *msg, const char *tail) +{ +#ifdef OLDRECOMMEND + snprintf(buf, szbuf, + ANSI_COLOR(1;31) "→ " ANSI_COLOR(33) "%s" ANSI_RESET + ANSI_COLOR(33) ":%-*s" ANSI_RESET "推%s\n", + myid, maxlength, msg, tail); +#else + // TODO(piaip) Make bbs.c#recomment use same structure. + // Now we just assume they are the same. + const int RECTYPE_SIZE = 3; + static const char *ctype[RECTYPE_SIZE] = { + "推", "噓", "→", + }; + static const char *ctype_attr2[RECTYPE_SIZE] = { + ANSI_COLOR(1;37), + ANSI_COLOR(1;31), + ANSI_COLOR(1;31), + }; + snprintf(buf, szbuf, + "%s%s " ANSI_COLOR(33) "%s" ANSI_RESET ANSI_COLOR(33) + ":%-*s" ANSI_RESET "%s\n", + ctype_attr2[type], ctype[type], myid, + maxlength, msg, tail); +#endif // OLDRECOMMEND + +} + int CommentsAddRecord(const char *board, const char *file, int type, const char *msg) { @@ -77,7 +107,7 @@ static int CommentsLoad(CommentsCtx *c, int i) CommentQueryRequest req = {0}; CommentBodyReq *resp; - if (i >= c->allocated) + if (i >= (int)c->allocated) CommentsAlloc(c, i); assert(c->resp); @@ -105,16 +135,44 @@ const struct CommentBodyReq *CommentsRead(void *ctx, int i) { CommentsCtx *c = (CommentsCtx *)ctx; - while (i >= c->loaded) { + while (i >= (int)c->loaded) { if (CommentsLoad(c, c->loaded++)) break; } - if (i >= c->loaded) + if (i >= (int)c->loaded) return NULL; return &c->resp[i]; } +static int CommentsDelete(void *ctx, int i) +{ + int s, result = 0; + CommentsCtx *c = (CommentsCtx *)ctx; + if (!CommentsRead(ctx, i)) + return -1; + // TODO(piaip) Notif commentd. + c->resp[i].type |= 0x80000000; + + CommentQueryRequest req = {0}; + req.cb = sizeof(req); + req.operation = COMMENTD_REQ_MARK_DELETED; + strlcpy(req.key.board, c->key.board, sizeof(req.key.board)); + strlcpy(req.key.file, c->key.file, sizeof(req.key.file)); + req.start = i; + s = toconnectex(COMMENTD_ADDR, 10); + if (s < 0) { + return -1; + } + if (towrite(s, &req, sizeof(req)) < 0 || + toread(s, &result, sizeof(result)) < 0) { + close(s); + return -1; + } + close(s); + return result; +} + int CommentsGetCount(void *ctx) { CommentsCtx *c = (CommentsCtx *)ctx; @@ -135,4 +193,64 @@ int CommentsGetCount(void *ctx) } return num; } + +int CommentsDeleteFromTextFile(void *ctx, int i) +{ + size_t pattern_len; + CommentsCtx *c = (CommentsCtx *)ctx; + const CommentBodyReq *req; + char buf[ANSILINELEN], pattern[ANSILINELEN]; + char filename[PATHLEN], tmpfile[PATHLEN]; + FILE *in, *out; + int found = 0; + + req = CommentsRead(ctx, i); + if (!req || req->type < 0) + return -1; + + setbfile(filename, c->key.board, c->key.file); + snprintf(tmpfile, sizeof(tmpfile), "%s.tmp", filename); + FormatCommentString(pattern, sizeof(pattern), req->type, + req->userid, 0, req->msg, ""); + // It's stupid but we have to remove the trailing ANSI_RESET + // for comparison. + *strrchr(pattern, ESC_CHR) = 0; + /* Remove the trailing \n for strncmp. */ + pattern_len = strlen(pattern); + // Now, try to construct and filter the message from file. + in = fopen(filename, "rt"); + out = fopen(tmpfile, "wt"); + while (fgets(buf, sizeof(buf), in)) { + if (strncmp(buf, pattern, pattern_len) == 0 && + (buf[pattern_len] == ' ' || buf[pattern_len] == ESC_CHR) && + !found) { + fprintf(out, "[本行文字已被 %s 刪除。]\n", cuser.userid); + found = 1; + } else { + fputs(buf, out); + } + } + fclose(out); + fclose(in); + if (found) { + // For everytime we have to use time capsule system. +#ifdef USE_TIME_CAPSULE + int rev = timecapsule_add_revision(filename); +#endif + remove(filename); + rename(tmpfile, filename); +#ifdef USE_TIME_CAPSULE + if (rev > 0) { + char revfn[PATHLEN]; + timecapsule_get_by_revision(filename, rev, revfn, sizeof(revfn)); + log_filef(revfn, 0, "\n※ 刪除推文: %s %s <%s:%s>", cuser.userid, + Cdatelite(&now), req->userid, req->msg); + } +#endif + CommentsDelete(ctx, i); + } else { + remove(tmpfile); + } + return 0; +} #endif diff --git a/pttbbs/mbbsd/psb.c b/pttbbs/mbbsd/psb.c index 769640af..96cbc22d 100644 --- a/pttbbs/mbbsd/psb.c +++ b/pttbbs/mbbsd/psb.c @@ -675,7 +675,7 @@ pvcm_header(void *ctx GCC_UNUSED) { static int pvcm_footer(void *ctx GCC_UNUSED) { vs_footer(" 推文 ", - " (↑/↓/PgUp/PgDn)移動 \t(q/←)跳出"); + " (↑/↓/PgUp/PgDn)移動 (d)刪除\t(q/←)跳出"); move(b_lines-1, 0); return 0; } @@ -686,21 +686,56 @@ pvcm_renderer(int i, int curr, int total, int rows GCC_UNUSED, void *ctx) { CommentBodyReq *resp = CommentsRead(cx->cmctx, i); if (!resp) return 0; + if (resp->type < 0) { + prints("%c %06d <已刪>\n", (i == curr)? '>' : ' ', i + 1); + } else { + prints("%c %06d %-12.12s %s\n", + (i == curr) ? '>' : ' ', + i + 1, resp->userid, resp->msg); + } + return 0; +} + +static int +pvcm_input_processor(int key, int curr, int total GCC_UNUSED, int rows GCC_UNUSED, void *ctx) { + int result; + pvcm_ctx *cx = (pvcm_ctx*) ctx; + + switch(key) { + case KEY_DEL: + case 'd': + if (vans("確定要刪除嗎? (y/N) ") == 'y') { + CommentsDeleteFromTextFile(cx->cmctx, curr); + } + return PSB_NOP; + } + return PSB_NA; +} - prints("%c %06d %-12.12s %s\n", - (i == curr) ? '>' : ' ', - i + 1, resp->userid, resp->msg); +static int +pvcm_welcome() { + clear(); + move(2, 0); + vs_hdr2("刪除推文", "實驗警告"); + outs(ANSI_COLOR(1;31) +" 這是實驗中的刪推文界面。\n\n" ANSI_RESET +" 提醒您: (1) 刪推文會從檔案前面開始找看起來作者跟內文相同的第一筆。\n" +" 目前沒辦法100%%確認找到正確的位置,但起碼內文是相同的。\n\n" +" (2) 被編輯過造成內容有變動的推文無法刪除。\n\n" + ""); + doupdate(); + pressanykey(); return 0; } int -pvcm_comment_manager(const char *board, const char *file) { +psb_comment_manager(const char *board, const char *file) { pvcm_ctx pvcmctx = { NULL, }; PSB_CTX ctx = { .curr = 0, - .total = 0, // maxrev + pvrbctx.base_as_current, + .total = 0, .header_lines = 2, .footer_lines = 2, .allow_pbs_version_message = 0, @@ -708,6 +743,7 @@ pvcm_comment_manager(const char *board, const char *file) { .header = pvcm_header, .footer = pvcm_footer, .renderer = pvcm_renderer, + .input_processor = pvcm_input_processor, }; pvcmctx.cmctx = CommentsOpen(board, file); if (!pvcmctx.cmctx) -- cgit v1.2.3