From 78aeb160cdd177d961db779bb39c6a5ae944af5a Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Wed, 1 Apr 2015 12:46:28 -0700 Subject: [PATCH 1/2] working on abuse report UI --- ast/help.ts | 16 ++++ editor/scriptList.ts | 215 +++++++++++++++++++++++++++++++++++++++---- rt/svg.ts | 1 + 3 files changed, 213 insertions(+), 19 deletions(-) diff --git a/ast/help.ts b/ast/help.ts index a256f5dc..9777d163 100644 --- a/ast/help.ts +++ b/ast/help.ts @@ -224,6 +224,13 @@ module TDev { resolved?: string; } + export interface JsonAbuseReport extends JsonPubOnPub + { + text:string; // report text + resolution:string; + publicationuserid:string; + } + export interface JsonReview extends JsonPubOnPub { ispositive: boolean; @@ -305,6 +312,15 @@ module TDev { sources: JsonVideoSource[]; } + export interface CanDeleteResponse { + publicationkind: string; + publicationname: string; + publicationuserid: string; + candelete:boolean; + candeletekind:boolean; + hasabusereports:boolean; + } + export class MdComments { public userid:string; diff --git a/editor/scriptList.ts b/editor/scriptList.ts index 602fda3d..d102f4ba 100644 --- a/editor/scriptList.ts +++ b/editor/scriptList.ts @@ -1214,7 +1214,8 @@ module TDev { export module Browser { else if (e.kind == "art") return this.getArtInfoById(e.id); else if (e.kind == "group") return this.getGroupInfoById(e.id); else if (e.kind == "document") return this.getDocumentInfo(e); - else if (e.kind == "release") return this.getReleaseInfoById(e.id); + else if (e.kind == "release") return this.getSpecificInfoById(e.id, ReleaseInfo) + else if (e.kind == "abusereport") return this.getSpecificInfoById(e.id, AbuseReportInfo) else return null; } @@ -1649,6 +1650,17 @@ module TDev { export module Browser { return si; } + public getArtInfoById(id:string) + { + var si = this.getLocation(id); + if (!si) { + si = new ArtInfo(this); + si.loadFromWeb(id); + this.saveLocation(si); + } + return si; + } + public getScriptInfoById(id:string) { var si = this.getLocation(id); @@ -1736,23 +1748,12 @@ module TDev { export module Browser { return si; } - public getReleaseInfoById(id:string) + public getSpecificInfoById(id:string, cl:any) { - var si = this.getLocation(id); + var si = this.getLocation(id); if (!si) { - si = new ReleaseInfo(this); - si.loadFromWeb(id); - this.saveLocation(si); - } - return si; - } - - public getArtInfoById(id:string) - { - var si = this.getLocation(id); - if (!si) { - si = new ArtInfo(this); - si.loadFromWeb(id); + si = new cl(this); + (si).loadFromWeb(id); this.saveLocation(si); } return si; @@ -2733,6 +2734,12 @@ module TDev { export module Browser { { if (!big || !this.getPublicationId()) return null; + if (Cloud.lite) { + return div("sdReportAbuse", HTML.mkImg("svg:SmilieSad,#000,clip=100"), lf("report/delete")).withClick(() => { + AbuseReportInfo.abuseOrDelete(this.getPublicationId()) + }); + } + return div("sdReportAbuse", HTML.mkImg("svg:SmilieSad,#000,clip=100"), lf("report abuse")).withClick(() => { window.open(Cloud.getServiceUrl() + "/user/report/" + this.getPublicationId()) }); @@ -4258,6 +4265,26 @@ module TDev { export module Browser { } } + export class AbuseReportsTab + extends ListTab + { + constructor(par:BrowserPage) { + super(par, "/abusereports") + this.isEmpty = true; + } + public getId() { return "abusereports"; } + public getName() { return lf("abuse reports"); } + + public bgIcon() { return "svg:fa-flag"; } + public noneText() { return lf("no abuse reports!"); } + public hideOnEmpty() { return true } + + public tabBox(cc:JsonIdObject):HTMLElement + { + return AbuseReportInfo.box(cc) + } + } + export class ScriptHeartsTab extends ListTab { @@ -4410,6 +4437,10 @@ module TDev { export module Browser { case "leaderboardscore": // this one should not happen anymore return div(null, lab(lf("scored {0}", (c).score), this.browser().getCreatorInfo(c).mkSmallBox()), lab(lf("in"), this.browser().getReferencedPubInfo(c).mkSmallBox())); + case "abusereport": + return div(null, lab(lf("abuse report")), + lab(lf("on"), this.browser().getReferencedPubInfo(c).mkSmallBox())); + // missing: tag, crash buckets default: debugger; @@ -4654,6 +4685,7 @@ module TDev { export module Browser { // new CommentsTab(this), , new ScriptsTab(this, lf("no scripts using this art")) // new SubscribersTab(this) + , new AbuseReportsTab(this) ]; } @@ -5380,7 +5412,7 @@ module TDev { export module Browser { if (big && !isTopic) facebook.setChildren(this.facebookLike()) - if (abuseDiv && this.publicId && this.jsonScript.userid == Cloud.getUserId()) { + if (!Cloud.isRestricted() && abuseDiv && this.publicId && this.jsonScript.userid == Cloud.getUserId()) { updateHideButton(); } @@ -5512,6 +5544,7 @@ module TDev { export module Browser { new ScriptHeartsTab(this), new TagsTab(this), new InsightsTab(this), + new AbuseReportsTab(this), ]; return r; } @@ -5600,6 +5633,8 @@ module TDev { export module Browser { }); } + if (Cloud.isRestricted()) return + // group mode? if(!st.collabSessionId) { uninstall.appendChild(HTML.mkButton(lf("edit with group"), () => { @@ -7206,7 +7241,12 @@ module TDev { export module Browser { return d.mkBox().withClick(() => { m.dismiss() Cloud.postPrivateApiAsync(c.id + "/resetpassword", { password: p }) - .then(() => ModalDialog.info(lf("password is reset"), p)) + .then(() => { + var m = ModalDialog.info(lf("password is reset"), lf("new password:")) + var inp = HTML.mkTextInput("text", "") + inp.value = p + m.add(div(null, inp)) + }) .done() }) }) @@ -7632,6 +7672,143 @@ module TDev { export module Browser { } } + export class AbuseReportInfo + extends BrowserPage + { + constructor(par:Host) { + super(par) + } + public persistentId() { return "abusereport:" + this.publicId; } + //public getTitle() { return "report " + this.publicId; } + + public getId() { return "abusereport"; } + public getName() { return lf("abuse report"); } + + public loadFromWeb(id:string) + { + this.publicId = id; + } + + public mkBoxCore(big:boolean) + { + var icon = div("sdIcon", HTML.mkImg("svg:fa-flag,white")); + icon.style.background = "#e72a2a"; + var textBlock = div("sdCommentBlockInner"); + var author = div("sdCommentAuthor"); + var hd = div("sdCommentBlock", textBlock, author); + + var addInfoInner = div("sdAddInfoInner", "/" + this.publicId); + var pubId = div("sdAddInfoOuter", addInfoInner); + var res = div("sdHeaderOuter", div("sdHeader", icon, div("sdHeaderInner", hd, pubId))); + + if (big) + res.className += " sdBigHeader"; + + return this.withUpdate(res, (u:JsonAbuseReport) => { + textBlock.setChildren([ u.text ]); + author.setChildren(["-- ", u.username]); + addInfoInner.setChildren([Util.timeSince(u.time) + " on " + u.publicationname]); + }); + } + + static box(c:JsonAbuseReport) + { + var b = TheHost; + var uid = b.getCreatorInfo(c); + var textDiv = div('sdSmallerTextBox', c.text); + var r = div("sdCmt sdCmtTop", uid.thumbnail(), + div("sdCmtTopic", + span("sdBold", c.username), + c.resolution ? div("sdCmtResolved", c.resolution) : null + //" on ", div("sdCmtScriptName", c.publicationname).withClick(() => b.loadDetails(b.getReferencedPubInfo(c))) + ), + textDiv, + div("sdCmtMeta", [ + Util.timeSince(c.time), + span("sdCmtId", " :: /" + c.id), + //div("sdCmtBtns", delBtn), + ])); + + return r; + } + + public mkSmallBox():HTMLElement + { + return this.mkBoxCore(false).withClick(() => + TheApiCacheMgr.getAsync(this.publicId, true).done(resp => AbuseReportInfo.abuseOrDelete(resp.publicationid))); + } + + public initTab() + { + this.withUpdate(this.tabContent, (c:JsonAbuseReport) => { + this.tabContent.setChildren([ + ScriptInfo.labeledBox(lf("report on"), this.browser().getReferencedPubInfo(c).mkSmallBox()), + AbuseReportInfo.box(c) ]); + }); + } + + public mkBigBox():HTMLElement { return null; } + + public mkTabsCore():BrowserTab[] { return [this]; } + + static abuseOrDelete(pubid:string) + { + Cloud.getPrivateApiAsync(pubid + "/candelete") + .then((resp:CanDeleteResponse) => { + var b = TheHost + var godelete = () => { + ModalDialog.ask(lf("Are you sure you want to delete '{0}'? No undo.", resp.publicationname), + lf("delete"), + () => { + Cloud.deletePrivateApiAsync(pubid) + .then(() => HTML.showProgressNotification(lf("gone."))) + .done() + // TODO show it's gone in the UI + }) + } + var viewreports = () => { + m.dismiss() + var inf = b.getAnyInfoByEtag({ id: pubid, kind: resp.publicationkind, ETag: "" }); + b.loadDetails(inf, "abusereports") + } + + if (resp.publicationuserid == Cloud.getUserId()) { + godelete() + } else { + var m = new ModalDialog() + var inp = HTML.mkTextInput("text", lf("Reason (eg., bad language, bullying, etc)")) + var err = div(null) + + m.add([ + div("wall-dialog-header", lf("report abuse about '{0}'", resp.publicationname)), + div("", inp), + err, + div("wall-dialog-body", resp.hasabusereports ? lf("There are already abuse report(s).") : + lf("No abuse reports so far.")), + div("wall-dialog-buttons", [ + HTML.mkButton(lf("cancel"), () => m.dismiss()), + !resp.hasabusereports ? null : HTML.mkButton(lf("view reports"), viewreports), + !resp.candelete ? null : HTML.mkButton(lf("delete"), godelete), + HTML.mkButton(lf("report"), () => { + if (inp.value.trim().length < 5) + err.setChildren(lf("Need some reason.")) + else { + m.dismiss() + Cloud.postPrivateApiAsync(pubid + "/abusereports", { text: inp.value }) + .then(() => HTML.showProgressNotification(lf("reported."))) + .done() + } + }), + ]), + ]) + m.show() + } + }) + .done() + } + + } + export class CommentInfo extends BrowserPage { @@ -7727,7 +7904,7 @@ module TDev { export module Browser { private _comments:CommentsTab; - public mkTabsCore():BrowserTab[] { return [this]; } + public mkTabsCore():BrowserTab[] { return [this, new AbuseReportsTab(this)]; } public bugCompareTo(other:CommentInfo, order:string) { var j0:JsonComment = TheApiCacheMgr.getCached(this.publicId) diff --git a/rt/svg.ts b/rt/svg.ts index 229a4230..f51045ad 100644 --- a/rt/svg.ts +++ b/rt/svg.ts @@ -167,6 +167,7 @@ export module SVG { "bell": "M912 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM1728 128q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-181 75t-75 181h-448q-52 0 -90 38t-38 90q50 42 91 88t85 119.5t74.5 158.5 t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q190 -28 307 -158.5t117 -282.5q0 -139 19.5 -260t50 -206t74.5 -158.5t85 -119.5t91 -88z", "wrench": "M384 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1028 484l-682 -682q-37 -37 -90 -37q-52 0 -91 37l-106 108q-38 36 -38 90q0 53 38 91l681 681q39 -98 114.5 -173.5t173.5 -114.5zM1662 919q0 -39 -23 -106q-47 -134 -164.5 -217.5 t-258.5 -83.5q-185 0 -316.5 131.5t-131.5 316.5t131.5 316.5t316.5 131.5q58 0 121.5 -16.5t107.5 -46.5q16 -11 16 -28t-16 -28l-293 -169v-224l193 -107q5 3 79 48.5t135.5 81t70.5 35.5q15 0 23.5 -10t8.5 -25z", "fast-backward": "M1747 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-710q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-678q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-678q4 11 13 19l710 710 q19 19 32 13t13 -32v-710q4 11 13 19z", + "flag": "M320 1280q0 -72 -64 -110v-1266q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v1266q-64 38 -64 110q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -25 -12.5 -38.5t-39.5 -27.5q-215 -116 -369 -116q-61 0 -123.5 22t-108.5 48 t-115.5 48t-142.5 22q-192 0 -464 -146q-17 -9 -33 -9q-26 0 -45 19t-19 45v742q0 32 31 55q21 14 79 43q236 120 421 120q107 0 200 -29t219 -88q38 -19 88 -19q54 0 117.5 21t110 47t88 47t54.5 21q26 0 45 -19t19 -45z", }; export function svgBoilerPlate(viewPort:string, svg:string, iconName = "") From 6a4f3e31d58247b566dc9ab1a21654b2eb42b7ec Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Wed, 1 Apr 2015 15:16:18 -0700 Subject: [PATCH 2/2] more work on abuse reports --- ast/ast.ts | 1 + ast/help.ts | 1 + editor/scriptList.ts | 63 ++++++++++++++++++++++++++++++++------------ 3 files changed, 48 insertions(+), 17 deletions(-) diff --git a/ast/ast.ts b/ast/ast.ts index 079fe86d..6369e469 100644 --- a/ast/ast.ts +++ b/ast/ast.ts @@ -2165,6 +2165,7 @@ module TDev.AST { public htmlColor() { + if (Cloud.isRestricted()) return "#85BB65" if (!this.color) return ScriptIcons.stableColorFromName(this.getName()); else return "#" + this.color.replace("#", "").slice(-6); } diff --git a/ast/help.ts b/ast/help.ts index 1646bb7d..60afbbf6 100644 --- a/ast/help.ts +++ b/ast/help.ts @@ -319,6 +319,7 @@ module TDev { publicationuserid: string; candelete:boolean; candeletekind:boolean; + canmanage:boolean; hasabusereports:boolean; } diff --git a/editor/scriptList.ts b/editor/scriptList.ts index ff56da48..375b11f5 100644 --- a/editor/scriptList.ts +++ b/editor/scriptList.ts @@ -7691,8 +7691,8 @@ module TDev { export module Browser { public mkBoxCore(big:boolean) { - var icon = div("sdIcon", HTML.mkImg("svg:fa-flag,white")); - icon.style.background = "#e72a2a"; + var icon = div("sdIcon"); + icon.style.background = "#aaa"; var textBlock = div("sdCommentBlockInner"); var author = div("sdCommentAuthor"); var hd = div("sdCommentBlock", textBlock, author); @@ -7705,6 +7705,16 @@ module TDev { export module Browser { res.className += " sdBigHeader"; return this.withUpdate(res, (u:JsonAbuseReport) => { + if (u.resolution == "ignored") { + icon.setChildren(HTML.mkImg("svg:fa-check-square-o,white")) + icon.style.background = "#308919"; + } else if (u.resolution == "deleted") { + icon.setChildren(HTML.mkImg("svg:fa-trash,white")) + icon.style.background = "#308919"; + } else { + icon.setChildren(HTML.mkImg("svg:fa-flag,white")) + icon.style.background = "#e72a2a"; + } textBlock.setChildren([ u.text ]); author.setChildren(["-- ", u.username]); addInfoInner.setChildren([Util.timeSince(u.time) + " on " + u.publicationname]); @@ -7716,7 +7726,7 @@ module TDev { export module Browser { var b = TheHost; var uid = b.getCreatorInfo(c); var textDiv = div('sdSmallerTextBox', c.text); - var r = div("sdCmt sdCmtTop", uid.thumbnail(), + var r = div("sdCmt sdCmtTop " + (c.resolution == "ignored" ? "disabledItem" : ""), uid.thumbnail(), div("sdCmtTopic", span("sdBold", c.username), c.resolution ? div("sdCmtResolved", c.resolution) : null @@ -7729,13 +7739,17 @@ module TDev { export module Browser { //div("sdCmtBtns", delBtn), ])); + r.withClick(() => { + + }) + return r; } public mkSmallBox():HTMLElement { return this.mkBoxCore(false).withClick(() => - TheApiCacheMgr.getAsync(this.publicId, true).done(resp => AbuseReportInfo.abuseOrDelete(resp.publicationid))); + TheApiCacheMgr.getAsync(this.publicId, true).done(resp => AbuseReportInfo.abuseOrDelete(resp.publicationid, this.publicId))); } public initTab() @@ -7751,7 +7765,7 @@ module TDev { export module Browser { public mkTabsCore():BrowserTab[] { return [this]; } - static abuseOrDelete(pubid:string) + static abuseOrDelete(pubid:string, abuseid:string = "") { Cloud.getPrivateApiAsync(pubid + "/candelete") .then((resp:CanDeleteResponse) => { @@ -7771,25 +7785,39 @@ module TDev { export module Browser { var inf = b.getAnyInfoByEtag({ id: pubid, kind: resp.publicationkind, ETag: "" }); b.loadDetails(inf, "abusereports") } + var goignore = () => { + m.dismiss() + Cloud.postPrivateApiAsync(abuseid, { resolution: "ignored" }) + .then(() => HTML.showProgressNotification(lf("ignored."))) + .done() + } - if (resp.publicationuserid == Cloud.getUserId()) { + if (!abuseid && resp.publicationuserid == Cloud.getUserId()) { godelete() } else { var m = new ModalDialog() var inp = HTML.mkTextInput("text", lf("Reason (eg., bad language, bullying, etc)")) var err = div(null) - m.add([ - div("wall-dialog-header", lf("report abuse about '{0}'", resp.publicationname)), - div("", inp), - err, - div("wall-dialog-body", resp.hasabusereports ? lf("There are already abuse report(s).") : - lf("No abuse reports so far.")), + if (abuseid) { + m.add([ + div("wall-dialog-header", lf("resolve report about '{0}'", resp.publicationname)), + ]) + } else { + m.add([ + div("wall-dialog-header", lf("report abuse about '{0}'", resp.publicationname)), + div("", inp), + err, + div("wall-dialog-body", resp.hasabusereports ? lf("There are already abuse report(s).") : + lf("No abuse reports so far.")), + ]) + } + + m.add( div("wall-dialog-buttons", [ HTML.mkButton(lf("cancel"), () => m.dismiss()), - !resp.hasabusereports ? null : HTML.mkButton(lf("view reports"), viewreports), - !resp.candelete ? null : HTML.mkButton(lf("delete"), godelete), - HTML.mkButton(lf("report"), () => { + resp.hasabusereports && HTML.mkButton(lf("view reports"), viewreports), + !abuseid && HTML.mkButton(lf("report"), () => { if (inp.value.trim().length < 5) err.setChildren(lf("Need some reason.")) else { @@ -7799,8 +7827,9 @@ module TDev { export module Browser { .done() } }), - ]), - ]) + abuseid && resp.canmanage && HTML.mkButton(lf("ignore report"), goignore), + resp.candelete && HTML.mkButton(lf("delete publication"), godelete), + ])) m.show() } })