diff --git a/README.md b/README.md index c0d8693e..1e6e456e 100644 --- a/README.md +++ b/README.md @@ -90,5 +90,4 @@ Try accessing this endpoint: [local.bnetdocs.org](https://local.bnetdocs.org) environment is not your `localhost`. ## Contributing -Please read the [CONTRIBUTING.md] -(https://github.com/BNETDocs/bnetdocs-web/blob/phoenix/CONTRIBUTING.md) file. +Please read the [CONTRIBUTING.md](/CONTRIBUTING.md) file. diff --git a/src/controllers/Packet/Edit.php b/src/controllers/Packet/Edit.php new file mode 100644 index 00000000..d906f96c --- /dev/null +++ b/src/controllers/Packet/Edit.php @@ -0,0 +1,170 @@ +getRequestQueryArray(); + $model = new PacketEditModel(); + $model->csrf_id = mt_rand(); + $model->csrf_token = CSRF::generate($model->csrf_id, 900); // 15 mins + $model->error = null; + $model->format = null; + $model->id = null; + $model->markdown = null; + $model->name = null; + $model->packet = null; + $model->packet_id = (isset($data["id"]) ? $data["id"] : null); + $model->published = null; + $model->remarks = null; + $model->user = ( + isset($_SESSION['user_id']) ? new User($_SESSION['user_id']) : null + ); + + $model->acl_allowed = ($model->user && $model->user->getAcl( + User::OPTION_ACL_PACKET_MODIFY + )); + + try { $model->packet = new Packet($model->packet_id); } + catch (PacketNotFoundException $e) { $model->packet = null; } + catch (InvalidArgumentException $e) { $model->packet = null; } + + if ($model->packet === null) { + $model->error = "NOT_FOUND"; + } else { + $flags = $model->packet->getOptionsBitmask(); + + $model->id = $model->packet->getPacketId(); + $model->name = $model->packet->getPacketName(); + $model->format = $model->packet->getPacketFormat(); + $model->remarks = $model->packet->getPacketRemarks(false); + $model->markdown = ($flags & Packet::OPTION_MARKDOWN); + $model->published = ($flags & Packet::OPTION_PUBLISHED); + + if ($router->getRequestMethod() == "POST") { + $this->handlePost($router, $model); + } + } + + $view->render($model); + + $model->_responseCode = ($model->acl_allowed ? 200 : 403); + $model->_responseHeaders["Content-Type"] = $view->getMimeType(); + $model->_responseTTL = 0; + + return $model; + + } + + protected function handlePost(Router &$router, PacketEditModel &$model) { + if (!$model->acl_allowed) { + $model->error = "ACL_NOT_SET"; + return; + } + if (!isset(Common::$database)) { + Common::$database = DatabaseDriver::getDatabaseObject(); + } + + $data = $router->getRequestBodyArray(); + $csrf_id = (isset($data["csrf_id" ]) ? $data["csrf_id" ] : null); + $csrf_token = (isset($data["csrf_token"]) ? $data["csrf_token"] : null); + $csrf_valid = CSRF::validate($csrf_id, $csrf_token); + $id = (isset($data["id" ]) ? $data["id" ] : null); + $name = (isset($data["name" ]) ? $data["name" ] : null); + $format = (isset($data["format" ]) ? $data["format" ] : null); + $remarks = (isset($data["remarks" ]) ? $data["remarks" ] : null); + $markdown = (isset($data["markdown" ]) ? $data["markdown" ] : null); + $content = (isset($data["content" ]) ? $data["content" ] : null); + $publish = (isset($data["publish" ]) ? $data["publish" ] : null); + $save = (isset($data["save" ]) ? $data["save" ] : null); + + $model->id = $id; + $model->name = $name; + $model->format = $format; + $model->remarks = $remarks; + $model->markdown = $markdown; + $model->content = $content; + + if (!$csrf_valid) { + $model->error = "INVALID_CSRF"; + return; + } + CSRF::invalidate($csrf_id); + + if (empty($name)) { + $model->error = "EMPTY_NAME"; + } else if (empty($format)) { + $model->error = "EMPTY_FORMAT"; + } else if (empty($remarks)) { + $model->error = "EMPTY_REMARKS"; + } + + $user_id = $model->user->getId(); + + try { + + $model->packet->setPacketId($model->id); + $model->packet->setPacketName($model->name); + $model->packet->setPacketFormat($model->format); + $model->packet->setPacketRemarks($model->remarks); + $model->packet->setMarkdown($model->markdown); + $model->packet->setPublished($publish); + + $model->packet->setEditedCount( + $model->packet->getEditedCount() + 1 + ); + $model->packet->setEditedDateTime( + new DateTime("now", new DateTimeZone("UTC")) + ); + + $success = $model->packet->save(); + + } catch (QueryException $e) { + + // SQL error occurred. We can show a friendly message to the user while + // also notifying this problem to staff. + Logger::logException($e); + + $success = false; + + } + + if (!$success) { + $model->error = "INTERNAL_ERROR"; + } else { + $model->error = false; + } + + Logger::logEvent( + "packet_edited", + $user_id, + getenv("REMOTE_ADDR"), + json_encode([ + "error" => $model->error, + "packet_id" => $model->packet_id, + "options_bitmask" => $model->packet->getOptionsBitmask(), + "name" => $model->packet->getPacketName(), + "format" => $model->packet->getPacketFormat(), + "remarks" => $model->packet->getPacketRemarks(false), + ]) + ); + } + +} diff --git a/src/libraries/Document.php b/src/libraries/Document.php index c87476ae..adb63a74 100644 --- a/src/libraries/Document.php +++ b/src/libraries/Document.php @@ -367,13 +367,13 @@ public function save() { `id` = :id LIMIT 1; "); - $stmt->bindParam(":content", $this->content, PDO::PARAM_INT); + $stmt->bindParam(":content", $this->content, PDO::PARAM_STR); $stmt->bindParam(":created_dt", $this->created_datetime, PDO::PARAM_INT); $stmt->bindParam(":edited_count", $this->edited_count, PDO::PARAM_INT); $stmt->bindParam(":edited_dt", $this->edited_datetime, PDO::PARAM_INT); $stmt->bindParam(":id", $this->id, PDO::PARAM_INT); $stmt->bindParam(":options", $this->options_bitmask, PDO::PARAM_INT); - $stmt->bindParam(":title", $this->title, PDO::PARAM_INT); + $stmt->bindParam(":title", $this->title, PDO::PARAM_STR); $stmt->bindParam(":user_id", $this->user_id, PDO::PARAM_INT); if (!$stmt->execute()) { throw new QueryException("Cannot save document"); diff --git a/src/libraries/Packet.php b/src/libraries/Packet.php index d3771c98..16810c55 100644 --- a/src/libraries/Packet.php +++ b/src/libraries/Packet.php @@ -485,4 +485,119 @@ public function refresh() { return false; } + public function save() { + if (!isset(Common::$database)) { + Common::$database = DatabaseDriver::getDatabaseObject(); + } + try { + $stmt = Common::$database->prepare(" + UPDATE + `packets` + SET + `created_datetime` = :created_dt, + `edited_count` = :edited_count, + `edited_datetime` = :edited_dt, + `options_bitmask` = :options, + `packet_application_layer_id` = :application_layer_id, + `packet_direction_id` = :direction_id, + `packet_format` = :format, + `packet_name` = :name, + `packet_remarks` = :remarks, + `packet_transport_layer_id` = :transport_layer_id, + `user_id` = :user_id + WHERE + `id` = :id + LIMIT 1; + "); + $stmt->bindParam( + ":application_layer_id", $this->packet_application_layer_id, + PDO::PARAM_INT + ); + $stmt->bindParam(":created_dt", $this->created_datetime, PDO::PARAM_INT); + $stmt->bindParam(":edited_count", $this->edited_count, PDO::PARAM_INT); + $stmt->bindParam(":edited_dt", $this->edited_datetime, PDO::PARAM_INT); + $stmt->bindParam( + ":direction_id", $this->packet_direction_id, PDO::PARAM_INT + ); + $stmt->bindParam(":format", $this->packet_format, PDO::PARAM_STR); + $stmt->bindParam(":id", $this->id, PDO::PARAM_INT); + $stmt->bindParam(":name", $this->packet_name, PDO::PARAM_STR); + $stmt->bindParam(":options", $this->options_bitmask, PDO::PARAM_INT); + $stmt->bindParam(":remarks", $this->packet_remarks, PDO::PARAM_STR); + $stmt->bindParam( + ":transport_layer_id", $this->packet_transport_layer_id, + PDO::PARAM_INT + ); + $stmt->bindParam(":user_id", $this->user_id, PDO::PARAM_INT); + if (!$stmt->execute()) { + throw new QueryException("Cannot save document"); + } + $stmt->closeCursor(); + + $object = new StdClass(); + $object->created_datetime = $this->created_datetime; + $object->edited_count = $this->edited_count; + $object->edited_datetime = $this->edited_datetime; + $object->id = $this->id; + $object->options_bitmask = $this->options_bitmask; + $object->packet_application_layer_id = $this->packet_application_layer_id; + $object->packet_direction_id = $this->packet_direction_id; + $object->packet_format = $this->packet_format; + $object->packet_id = $this->packet_id; + $object->packet_name = $this->packet_name; + $object->packet_remarks = $this->packet_remarks; + $object->packet_transport_layer_id = $this->packet_transport_layer_id; + $object->user_id = $this->user_id; + + $cache_key = "bnetdocs-packet-" . $this->id; + Common::$cache->set($cache_key, serialize($object), 300); + Common::$cache->delete("bnetdocs-packets"); + + return true; + } catch (PDOException $e) { + throw new QueryException("Cannot save packet", $e); + } + return false; + } + + public function setEditedCount($value) { + $this->edited_count = $value; + } + + public function setEditedDateTime(\DateTime $value) { + $this->edited_datetime = $value->format("Y-m-d H:i:s"); + } + + public function setMarkdown($value) { + if ($value) { + $this->options_bitmask |= self::OPTION_MARKDOWN; + } else { + $this->options_bitmask &= ~self::OPTION_MARKDOWN; + } + } + + public function setPacketFormat($value) { + $this->packet_format = $value; + } + + public function setPacketId($value) { + $this->packet_id = $value; + } + + public function setPacketName($value) { + $this->packet_name = $value; + } + + public function setPacketRemarks($value) { + $this->packet_remarks = $value; + } + + public function setPublished($value) { + if ($value) { + $this->options_bitmask |= self::OPTION_PUBLISHED; + } else { + $this->options_bitmask &= ~self::OPTION_PUBLISHED; + } + } + } diff --git a/src/main.php b/src/main.php index 1e3a2ea1..07d5d898 100644 --- a/src/main.php +++ b/src/main.php @@ -163,6 +163,9 @@ function main() { //$router->addRoute( // URL: /packet/create // "#^/packet/create/?$#", "Packet\\Create", "Packet\\CreateHtml" //); + $router->addRoute( // URL: /packet/edit + "#^/packet/edit/?$#", "Packet\\Edit", "Packet\\EditHtml" + ); $router->addRoute( // URL: /packet/index.cpp "#^/packet/index\.cpp/?$#", "Packet\\Index", "Packet\\IndexCpp" ); diff --git a/src/models/Document/Edit.php b/src/models/Document/Edit.php index a315ad1c..c91356ff 100644 --- a/src/models/Document/Edit.php +++ b/src/models/Document/Edit.php @@ -7,6 +7,7 @@ class Edit extends Model { public $acl_allowed; + public $category; public $content; public $csrf_id; public $csrf_token; diff --git a/src/models/Packet/Edit.php b/src/models/Packet/Edit.php new file mode 100644 index 00000000..fbbe28d8 --- /dev/null +++ b/src/models/Packet/Edit.php @@ -0,0 +1,23 @@ +getContext()->error) { $message = "Cannot find document by that id."; break; case "INVALID_CSRF": - $message = "The Cross-Site Request Forgery token was invalid. Either the" - . "edit document form expired, or this may have been a malicious attempt" - . "to create a document."; + $message = "The Cross-Site Request Forgery token was invalid. Either the " + . "edit document form expired, or this may have been a malicious " + . "attempt to create a document."; break; case "EMPTY_TITLE": $message = "The title of the document is required."; diff --git a/src/templates/Packet/Edit.phtml b/src/templates/Packet/Edit.phtml new file mode 100644 index 00000000..802a84ad --- /dev/null +++ b/src/templates/Packet/Edit.phtml @@ -0,0 +1,112 @@ +opengraph->attach(new Pair("url", "/packet/edit")); +$this->opengraph->attach(new Pair("type", "article")); + +switch ($this->getContext()->error) { + case "ACL_NOT_SET": + $message = "You do not have the privilege to edit packets."; + break; + case "NOT_FOUND": + $message = "Cannot find packet by that id."; + break; + case "INVALID_CSRF": + $message = "The Cross-Site Request Forgery token was invalid. Either the " + . "edit packet form expired, or this may have been a malicious attempt " + . "to create a packet."; + break; + case "EMPTY_NAME": + $message = "The name of the packet is required."; + break; + case "EMPTY_FORMAT": + $message = "The format of the packet is required."; + break; + case "EMPTY_REMARKS": + $message = "The remarks of the packet is required."; + break; + case "INTERNAL_ERROR": + $message = "An internal error occurred while processing your request. " + . "Our staff has been notified of the issue. Try again later."; + break; + default: + $message = $this->getContext()->error; +} + +$this->additional_css[] = "/a/forms.css"; +require("./header.inc.phtml"); +?> +
+getContext()->error !== false) { ?> +
Edit Packet
+ +

+ +getContext()->error != "NOT_FOUND") { ?> +
"> + "/> + "/> +
+ + (base 10 / decimal format)
+ +
+
+
+ +
+
+ + +
+
+ + + + getContext()->markdown) + echo " checked=\"checked\""; + ?>/> + + +
+
+ + +
+
+ + +
Edit Packet
+
+

Your packet has been edited.

+

Use the navigation to the left to move to another page.

+
+ +
+ diff --git a/src/templates/Packet/View.phtml b/src/templates/Packet/View.phtml index e9762bc4..24c3d6de 100644 --- a/src/templates/Packet/View.phtml +++ b/src/templates/Packet/View.phtml @@ -52,6 +52,13 @@ if ($object) { $this->opengraph->attach(new Pair("url", $url)); +$edit_url = Common::relativeUrlToAbsolute("/packet/edit?id=" . urlencode($object_id)); +$delete_url = Common::relativeUrlToAbsolute("/packet/delete?id=" . urlencode($object_id)); +$edit_visible = ($logged_in && ($logged_in->getOptionsBitmask() + & User::OPTION_ACL_PACKET_MODIFY)); +$delete_visible = ($logged_in && ($logged_in->getOptionsBitmask() + & User::OPTION_ACL_PACKET_DELETE)); + $this->additional_css[] = "/a/packet.css"; $this->additional_css[] = "/a/comments.css"; if ($logged_in) $this->additional_css[] = "/a/forms.css"; @@ -62,6 +69,12 @@ require("./header.inc.phtml"); "/> getPacketName()); ?>&url=" rel="external" data-popup="1">"/> "/> + + Edit + + + Delete +
diff --git a/src/views/Packet/EditHtml.php b/src/views/Packet/EditHtml.php new file mode 100644 index 00000000..9bbf5bc6 --- /dev/null +++ b/src/views/Packet/EditHtml.php @@ -0,0 +1,24 @@ +render(); + } + +}