Skip to content

Commit 7b1e585

Browse files
committed
Implement comment deletion
1 parent 6d84b4c commit 7b1e585

File tree

6 files changed

+303
-0
lines changed

6 files changed

+303
-0
lines changed

src/controllers/Comment/Delete.php

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
<?php
2+
3+
namespace BNETDocs\Controllers\Comment;
4+
5+
use \BNETDocs\Libraries\CSRF;
6+
use \BNETDocs\Libraries\Comment;
7+
use \BNETDocs\Libraries\Common;
8+
use \BNETDocs\Libraries\Controller;
9+
use \BNETDocs\Libraries\Exceptions\CommentNotFoundException;
10+
use \BNETDocs\Libraries\Exceptions\UnspecifiedViewException;
11+
use \BNETDocs\Libraries\Logger;
12+
use \BNETDocs\Libraries\Router;
13+
use \BNETDocs\Libraries\UserSession;
14+
use \BNETDocs\Models\Comment\Delete as CommentDeleteModel;
15+
use \BNETDocs\Views\Comment\DeleteHtml as CommentDeleteHtmlView;
16+
use \InvalidArgumentException;
17+
use \UnexpectedValueException;
18+
19+
class Delete extends Controller {
20+
21+
public function run(Router &$router) {
22+
switch ($router->getRequestPathExtension()) {
23+
case "htm": case "html": case "":
24+
$view = new CommentDeleteHtmlView();
25+
break;
26+
default:
27+
throw new UnspecifiedViewException();
28+
}
29+
30+
$data = $router->getRequestQueryArray();
31+
$model = new CommentDeleteModel();
32+
$model->comment = null;
33+
$model->csrf_id = mt_rand();
34+
$model->csrf_token = CSRF::generate($model->csrf_id);
35+
$model->error = null;
36+
$model->id = (isset($data["id"]) ? $data["id"] : null);
37+
$model->parent_id = null;
38+
$model->parent_type = null;
39+
$model->title = null;
40+
$model->user_session = UserSession::load($router);
41+
42+
try { $model->comment = new Comment($model->id); }
43+
catch (CommentNotFoundException $e) { $model->comment = null; }
44+
catch (InvalidArgumentException $e) { $model->comment = null; }
45+
46+
if ($model->comment === null) {
47+
$model->error = "NOT_FOUND";
48+
} else {
49+
$model->content = $model->comment->getContent(true);
50+
$model->parent_type = $model->comment->getParentType();
51+
$model->parent_id = $model->comment->getParentId();
52+
53+
if ($router->getRequestMethod() == "POST") {
54+
$this->tryDelete($router, $model);
55+
}
56+
}
57+
58+
ob_start();
59+
$view->render($model);
60+
$router->setResponseCode(200);
61+
$router->setResponseTTL(0);
62+
$router->setResponseHeader("Content-Type", $view->getMimeType());
63+
$router->setResponseContent(ob_get_contents());
64+
ob_end_clean();
65+
}
66+
67+
protected function tryDelete(Router &$router, CommentDeleteModel &$model) {
68+
if (!isset($model->user_session)) {
69+
$model->error = "NOT_LOGGED_IN";
70+
return;
71+
}
72+
73+
$data = $router->getRequestBodyArray();
74+
$csrf_id = (isset($data["csrf_id" ]) ? $data["csrf_id" ] : null);
75+
$csrf_token = (isset($data["csrf_token"]) ? $data["csrf_token"] : null);
76+
$csrf_valid = CSRF::validate($csrf_id, $csrf_token);
77+
78+
if (!$csrf_valid) {
79+
$model->error = "INVALID_CSRF";
80+
return;
81+
}
82+
CSRF::invalidate($csrf_id);
83+
84+
$model->error = false;
85+
86+
$id = (int) $model->id;
87+
$parent_type = (int) $model->parent_type;
88+
$parent_id = (int) $model->parent_id;
89+
$user_id = $model->user_session->user_id;
90+
91+
$log_key = "";
92+
switch ($parent_type) {
93+
case Comment::PARENT_TYPE_DOCUMENT: $log_key = "_document"; break;
94+
case Comment::PARENT_TYPE_COMMENT: $log_key = "_comment"; break;
95+
case Comment::PARENT_TYPE_NEWS_POST: $log_key = "_news"; break;
96+
case Comment::PARENT_TYPE_PACKET: $log_key = "_packet"; break;
97+
case Comment::PARENT_TYPE_SERVER: $log_key = "_server"; break;
98+
case Comment::PARENT_TYPE_USER: $log_key = "_user"; break;
99+
default: throw new UnexpectedValueException(
100+
"Parent type: " . $parent_type
101+
);
102+
}
103+
104+
try {
105+
106+
$success = Comment::delete($id, $parent_type, $parent_id);
107+
108+
} catch (QueryException $e) {
109+
110+
// SQL error occurred. We can show a friendly message to the user while
111+
// also notifying this problem to staff.
112+
Logger::logException($e);
113+
114+
$success = false;
115+
116+
}
117+
118+
if (!$success) {
119+
$model->error = "INTERNAL_ERROR";
120+
} else {
121+
$model->error = false;
122+
}
123+
124+
Logger::logEvent(
125+
"comment_deleted" . $log_key,
126+
$user_id,
127+
getenv("REMOTE_ADDR"),
128+
json_encode([
129+
"error" => $model->error,
130+
"comment_id" => $id,
131+
"parent_type" => $parent_type,
132+
"parent_id" => $parent_id
133+
])
134+
);
135+
}
136+
137+
}

src/libraries/Comment.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,31 @@ public static function create($parent_type, $parent_id, $user_id, $content) {
9191
}
9292
}
9393

94+
public static function delete($id, $parent_type, $parent_id) {
95+
if (!isset(Common::$database)) {
96+
Common::$database = DatabaseDriver::getDatabaseObject();
97+
}
98+
$successful = false;
99+
try {
100+
$stmt = Common::$database->prepare("
101+
DELETE FROM `comments` WHERE `id` = :id LIMIT 1;
102+
");
103+
$stmt->bindParam(":id", $id, PDO::PARAM_INT);
104+
$successful = $stmt->execute();
105+
$stmt->closeCursor();
106+
if ($successful) {
107+
Common::$cache->delete("bnetdocs-comment-" . (int) $id);
108+
Common::$cache->delete(
109+
"bnetdocs-comment-" . (int) $parent_type . "-" . (int) $parent_id
110+
);
111+
}
112+
} catch (PDOException $e) {
113+
throw new QueryException("Cannot delete comment", $e);
114+
} finally {
115+
return $successful;
116+
}
117+
}
118+
94119
public static function getAll($parent_type, $parent_id) {
95120
$ck = "bnetdocs-comment-" . $parent_type . "-" . $parent_id;
96121
$cv = Common::$cache->get($ck);

src/libraries/Router.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use \BNETDocs\Controllers\Attachment\Download as AttachmentDownloadController;
66
use \BNETDocs\Controllers\Comment\Create as CommentCreateController;
7+
use \BNETDocs\Controllers\Comment\Delete as CommentDeleteController;
78
use \BNETDocs\Controllers\Credits as CreditsController;
89
use \BNETDocs\Controllers\Document\Create as DocumentCreateController;
910
use \BNETDocs\Controllers\Document\Delete as DocumentDeleteController;
@@ -275,6 +276,9 @@ public function route(Pair &$redirect = null) {
275276
case "create":
276277
$controller = new CommentCreateController();
277278
break;
279+
case "delete": case "delete.htm": case "delete.html":
280+
$controller = new CommentDeleteController();
281+
break;
278282
default:
279283
throw new ControllerNotFoundException(
280284
$path . "/" . $subpath

src/models/Comment/Delete.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace BNETDocs\Models\Comment;
4+
5+
use \BNETDocs\Libraries\Model;
6+
7+
class Delete extends Model {
8+
9+
public $acl_allowed;
10+
public $comment;
11+
public $csrf_id;
12+
public $csrf_token;
13+
public $error;
14+
public $id;
15+
public $parent_id;
16+
public $parent_type;
17+
public $title;
18+
public $user;
19+
public $user_session;
20+
21+
public function __construct() {
22+
parent::__construct();
23+
$this->acl_allowed = null;
24+
$this->comment = null;
25+
$this->csrf_id = null;
26+
$this->csrf_token = null;
27+
$this->error = null;
28+
$this->id = null;
29+
$this->parent_id = null;
30+
$this->parent_type = null;
31+
$this->title = null;
32+
$this->user = null;
33+
$this->user_session = null;
34+
}
35+
36+
}

src/templates/Comment/Delete.phtml

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
namespace BNETDocs\Templates;
3+
4+
use \BNETDocs\Libraries\Pair;
5+
6+
$title = "Delete Comment";
7+
$description = "This form allows an individual to delete a comment.";
8+
9+
$this->opengraph->attach(new Pair("url", "/comment/delete"));
10+
$this->opengraph->attach(new Pair("type", "article"));
11+
12+
switch ($this->getContext()->error) {
13+
case "ACL_NOT_SET":
14+
$message = "You do not have the privilege to delete comments.";
15+
break;
16+
case "NOT_FOUND":
17+
$message = "Cannot find comment by that id.";
18+
break;
19+
case "NOT_LOGGED_IN":
20+
$message = "You must be logged in to delete comments.";
21+
break;
22+
case "INVALID_CSRF":
23+
$message = "The Cross-Site Request Forgery token was invalid. Either the "
24+
. "delete comment form expired, or this may have been a malicious "
25+
. "attempt to delete a comment.";
26+
break;
27+
case "INTERNAL_ERROR":
28+
$message = "An internal error occurred while processing your request. "
29+
. "Our staff has been notified of the issue. Try again later.";
30+
break;
31+
default:
32+
$message = $this->getContext()->error;
33+
}
34+
35+
$c = $this->getContext()->comment;
36+
$c_id = $c->getId();
37+
$c_user = $c->getUser();
38+
$c_user_id = $c->getUserId();
39+
$c_user_name = $c_user->getName();
40+
$c_user_url = $c_user->getURI();
41+
$c_user_avatar = $c_user->getAvatarURI(22);
42+
43+
$this->additional_css[] = "/a/comments.css";
44+
$this->additional_css[] = "/a/forms.css";
45+
require("./header.inc.phtml");
46+
?>
47+
<article>
48+
<?php if (is_null($this->getContext()->error)) { ?>
49+
<header>Delete Comment</header>
50+
<form method="POST" action="?id=<?php echo $this->getContext()->id; ?>">
51+
<input type="hidden" name="csrf_id" value="<?php echo $this->getContext()->csrf_id; ?>"/>
52+
<input type="hidden" name="csrf_token" value="<?php echo $this->getContext()->csrf_token; ?>"/>
53+
<section>
54+
<p>Are you sure you wish to delete this comment?</p>
55+
<hr/><table class="comments"><tbody>
56+
<tr><td><a href="<?php echo $c_user_url; ?>"><img class="avatar" src="<?php echo $c_user_avatar; ?>"/> <?php echo filter_var($c_user_name, FILTER_SANITIZE_STRING); ?></a><br/><time class="comment_timestamp" datetime="<?php echo $c->getCreatedDateTime()->format("c"); ?>"><?php echo $c->getCreatedDateTime()->format("D M j, Y g:ia T"); ?></time></td><td><?php echo $c->getContent(true); ?></td></tr>
57+
</tbody></table><hr/>
58+
<p><input type="submit" value="Delete Comment" tabindex="1" autofocus="autofocus"/></p>
59+
</section>
60+
</form>
61+
<?php } else if ($this->getContext()->error === false) { ?>
62+
<header class="green">Comment Deleted</header>
63+
<section class="green">
64+
<p>You have successfully deleted the comment!</p>
65+
<p>Use the navigation to the left to move to another page.</p>
66+
</section>
67+
<?php } else { ?>
68+
<header class="red">Delete Comment</header>
69+
<section class="red">
70+
<p>An error occurred while attempting to delete the comment.</p>
71+
<p><?php echo $message; ?></p>
72+
<p>Use the navigation to the left to move to another page.</p>
73+
</section>
74+
<?php } ?>
75+
</article>
76+
<?php require("./footer.inc.phtml"); ?>

src/views/Comment/DeleteHtml.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace BNETDocs\Views\Comment;
4+
5+
use \BNETDocs\Libraries\Common;
6+
use \BNETDocs\Libraries\Exceptions\IncorrectModelException;
7+
use \BNETDocs\Libraries\Model;
8+
use \BNETDocs\Libraries\Template;
9+
use \BNETDocs\Libraries\View;
10+
use \BNETDocs\Models\Comment\Delete as CommentDeleteModel;
11+
12+
class DeleteHtml extends View {
13+
14+
public function getMimeType() {
15+
return "text/html;charset=utf-8";
16+
}
17+
18+
public function render(Model &$model) {
19+
if (!$model instanceof CommentDeleteModel) {
20+
throw new IncorrectModelException();
21+
}
22+
(new Template($model, "Comment/Delete"))->render();
23+
}
24+
25+
}

0 commit comments

Comments
 (0)