Skip to content

Commit 1591fd0

Browse files
committed
Implement attachment viewing support
1 parent 9586eb0 commit 1591fd0

File tree

6 files changed

+309
-3
lines changed

6 files changed

+309
-3
lines changed

src/controllers/Document/View.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace BNETDocs\Controllers\Document;
44

55
use \BNETDocs\Controllers\Redirect as RedirectController;
6+
use \BNETDocs\Libraries\Attachment;
67
use \BNETDocs\Libraries\Comment;
78
use \BNETDocs\Libraries\Common;
89
use \BNETDocs\Libraries\Controller;
@@ -58,6 +59,10 @@ public function run(Router &$router) {
5859
throw new UnspecifiedViewException();
5960
}
6061
if ($model->document) {
62+
$model->attachments = Attachment::getAll(
63+
Comment::PARENT_TYPE_DOCUMENT,
64+
$model->document_id
65+
);
6166
$model->comments = Comment::getAll(
6267
Comment::PARENT_TYPE_DOCUMENT,
6368
$model->document_id

src/libraries/Attachment.php

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
<?php
2+
3+
namespace BNETDocs\Libraries;
4+
5+
use \BNETDocs\Libraries\Cache;
6+
use \BNETDocs\Libraries\Common;
7+
use \BNETDocs\Libraries\Database;
8+
use \BNETDocs\Libraries\DatabaseDriver;
9+
use \BNETDocs\Libraries\Exceptions\AttachmentNotFoundException;
10+
use \BNETDocs\Libraries\Exceptions\QueryException;
11+
use \BNETDocs\Libraries\User;
12+
use \DateTime;
13+
use \DateTimeZone;
14+
use \InvalidArgumentException;
15+
use \JsonSerializable;
16+
use \PDO;
17+
use \PDOException;
18+
use \StdClass;
19+
20+
class Attachment implements JsonSerializable {
21+
22+
const PARENT_TYPE_DOCUMENT = 0;
23+
const PARENT_TYPE_COMMENT = 1;
24+
const PARENT_TYPE_NEWS_POST = 2;
25+
const PARENT_TYPE_PACKET = 3;
26+
const PARENT_TYPE_SERVER = 4;
27+
const PARENT_TYPE_USER = 5;
28+
29+
protected $created_datetime;
30+
protected $filename;
31+
protected $id;
32+
protected $parent_id;
33+
protected $parent_type;
34+
protected $user_id;
35+
36+
public function __construct($data) {
37+
if (is_numeric($data)) {
38+
$this->created_datetime = null;
39+
$this->filename = null;
40+
$this->id = (int) $data;
41+
$this->parent_id = null;
42+
$this->parent_type = null;
43+
$this->user_id = null;
44+
$this->refresh();
45+
} else if ($data instanceof StdClass) {
46+
self::normalize($data);
47+
$this->created_datetime = $data->created_datetime;
48+
$this->filename = $data->filename;
49+
$this->id = $data->id;
50+
$this->parent_id = $data->parent_id;
51+
$this->parent_type = $data->parent_type;
52+
$this->user_id = $data->user_id;
53+
} else {
54+
throw new InvalidArgumentException("Cannot use data argument");
55+
}
56+
}
57+
58+
public static function getAll($parent_type, $parent_id) {
59+
$ck = "bnetdocs-attachment-" . $parent_type . "-" . $parent_id;
60+
$cv = Common::$cache->get($ck);
61+
if ($cv !== false && !empty($cv)) {
62+
$ids = explode(",", $cv);
63+
$objects = [];
64+
foreach ($ids as $id) {
65+
$objects[] = new self($id);
66+
}
67+
return $objects;
68+
}
69+
if (!isset(Common::$database)) {
70+
Common::$database = DatabaseDriver::getDatabaseObject();
71+
}
72+
try {
73+
$stmt = Common::$database->prepare("
74+
SELECT
75+
`created_datetime`,
76+
`filename`,
77+
`id`,
78+
`parent_id`,
79+
`parent_type`,
80+
`user_id`
81+
FROM `attachments`
82+
WHERE
83+
`parent_type` = :parent_type AND
84+
`parent_id` = :parent_id
85+
ORDER BY
86+
`filename` ASC,
87+
`created_datetime` ASC,
88+
`id` ASC
89+
;
90+
");
91+
$stmt->bindParam(":parent_type", $parent_type, PDO::PARAM_INT);
92+
$stmt->bindParam(":parent_id", $parent_id, PDO::PARAM_INT);
93+
if (!$stmt->execute()) {
94+
throw new QueryException("Cannot refresh attachment");
95+
}
96+
$ids = [];
97+
$objects = [];
98+
while ($row = $stmt->fetch(PDO::FETCH_OBJ)) {
99+
$ids[] = (int) $row->id;
100+
$objects[] = new self($row);
101+
Common::$cache->set(
102+
"bnetdocs-attachment-" . $row->id, serialize($row), 300
103+
);
104+
}
105+
$stmt->closeCursor();
106+
Common::$cache->set($ck, implode(",", $ids), 300);
107+
return $objects;
108+
} catch (PDOException $e) {
109+
throw new QueryException("Cannot refresh attachment", $e);
110+
}
111+
return null;
112+
}
113+
114+
public function getContent() {
115+
return "NOT YET IMPLEMENTED"; // TODO: Retrieve file from database
116+
}
117+
118+
public function getContentSize($format = false) {
119+
$bytes = strlen($this->getContent());
120+
121+
if ($format) {
122+
123+
$kilobytes = 1024;
124+
$megabytes = 1024 * $kilobytes;
125+
$gigabytes = 1024 * $megabytes;
126+
$terabytes = 1024 * $gigabytes;
127+
128+
if ($bytes >= $terabytes) {
129+
$bytes = round($bytes / $terabytes, 2) . " TiB";
130+
} else if ($bytes >= $gigabytes) {
131+
$bytes = round($bytes / $gigabytes, 2) . " GiB";
132+
} else if ($bytes >= $megabytes) {
133+
$bytes = round($bytes / $megabytes, 2) . " MiB";
134+
} else if ($bytes >= $kilobytes) {
135+
$bytes = round($bytes / $kilobytes, 2) . " KiB";
136+
} else {
137+
$bytes = $bytes . " B";
138+
}
139+
140+
}
141+
142+
return $bytes;
143+
}
144+
145+
public function getCreatedDateTime() {
146+
if (is_null($this->created_datetime)) {
147+
return $this->created_datetime;
148+
} else {
149+
$tz = new DateTimeZone("UTC");
150+
$dt = new DateTime($this->created_datetime);
151+
$dt->setTimezone($tz);
152+
return $dt;
153+
}
154+
}
155+
156+
public function getFilename() {
157+
return $this->filename;
158+
}
159+
160+
public function getId() {
161+
return $this->id;
162+
}
163+
164+
public function getParentId() {
165+
return $this->parent_id;
166+
}
167+
168+
public function getParentType() {
169+
return $this->parent_type;
170+
}
171+
172+
public function getUser() {
173+
if (is_null($this->user_id)) return null;
174+
return new User($this->user_id);
175+
}
176+
177+
public function getUserId() {
178+
return $this->user_id;
179+
}
180+
181+
public function jsonSerialize() {
182+
$created_datetime = $this->getCreatedDateTime();
183+
if (!is_null($created_datetime)) $created_datetime = [
184+
"iso" => $created_datetime->format("r"),
185+
"unix" => $created_datetime->getTimestamp(),
186+
];
187+
188+
return [
189+
"created_datetime" => $created_datetime,
190+
"filename" => $this->getFilename(),
191+
"id" => $this->getId(),
192+
"parent_id" => $this->getParentId(),
193+
"parent_type" => $this->getParentType(),
194+
"user" => $this->getUser(),
195+
];
196+
}
197+
198+
protected static function normalize(StdClass &$data) {
199+
$data->created_datetime = (string) $data->created_datetime;
200+
$data->filename = (string) $data->filename;
201+
$data->id = (int) $data->id;
202+
$data->parent_id = (int) $data->parent_id;
203+
$data->parent_type = (int) $data->parent_type;
204+
$data->user_id = (int) $data->user_id;
205+
206+
return true;
207+
}
208+
209+
public function refresh() {
210+
$ck = "bnetdocs-attachment-" . $this->id;
211+
$cv = Common::$cache->get($ck);
212+
if ($cv !== false) {
213+
$cv = unserialize($cv);
214+
$this->created_datetime = $cv->created_datetime;
215+
$this->filename = $cv->filename;
216+
$this->id = $cv->id;
217+
$this->parent_id = $cv->parent_id;
218+
$this->parent_type = $cv->parent_type;
219+
$this->user_id = $cv->user_id;
220+
return true;
221+
}
222+
if (!isset(Common::$database)) {
223+
Common::$database = DatabaseDriver::getDatabaseObject();
224+
}
225+
try {
226+
$stmt = Common::$database->prepare("
227+
SELECT
228+
`created_datetime`,
229+
`filename`,
230+
`id`,
231+
`parent_id`,
232+
`parent_type`,
233+
`user_id`
234+
FROM `attachments`
235+
WHERE `id` = :id
236+
LIMIT 1;
237+
");
238+
$stmt->bindParam(":id", $this->id, PDO::PARAM_INT);
239+
if (!$stmt->execute()) {
240+
throw new QueryException("Cannot refresh attachment");
241+
} else if ($stmt->rowCount() == 0) {
242+
throw new AttachmentNotFoundException($this->id);
243+
}
244+
$row = $stmt->fetch(PDO::FETCH_OBJ);
245+
$stmt->closeCursor();
246+
self::normalize($row);
247+
$this->created_datetime = $row->created_datetime;
248+
$this->filename = $row->filename;
249+
$this->id = $row->id;
250+
$this->parent_id = $row->parent_id;
251+
$this->parent_type = $row->parent_type;
252+
$this->user_id = $row->user_id;
253+
Common::$cache->set($ck, serialize($row), 300);
254+
return true;
255+
} catch (PDOException $e) {
256+
throw new QueryException("Cannot refresh attachment", $e);
257+
}
258+
return false;
259+
}
260+
261+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace BNETDocs\Libraries\Exceptions;
4+
5+
use \BNETDocs\Libraries\Exceptions\BNETDocsException;
6+
use \BNETDocs\Libraries\Logger;
7+
use \Exception;
8+
9+
class AttachmentNotFoundException extends BNETDocsException {
10+
11+
public function __construct($query, Exception &$prev_ex = null) {
12+
parent::__construct("Attachment not found", 22, $prev_ex);
13+
Logger::logMetric("query", $query);
14+
}
15+
16+
}

src/libraries/Exceptions/Reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ All of the following errors are subclassed from the `BNETDocsException` class.
2626
| 19 | `PacketDirectionInvalidException` | Packet direction is invalid |
2727
| 20 | `ProductNotFoundException` | Product not found |
2828
| 21 | `CommentNotFoundException` | Comment not found |
29+
| 22 | `AttachmentNotFoundException` | Attachment not found |

src/models/Document/View.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66

77
class View extends Model {
88

9+
public $attachments;
910
public $comments;
1011
public $document;
1112
public $document_id;
1213
public $user_session;
1314

1415
public function __construct() {
1516
parent::__construct();
17+
$this->attachments = null;
1618
$this->comments = null;
1719
$this->document = null;
1820
$this->document_id = null;

src/templates/Document/View.phtml

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ use \BNETDocs\Libraries\Common;
55
use \BNETDocs\Libraries\Gravatar;
66
use \BNETDocs\Libraries\Pair;
77

8-
$comments = $this->getContext()->comments;
9-
$object_id = $this->getContext()->document_id;
10-
$object = $this->getContext()->document;
8+
$attachments = $this->getContext()->attachments;
9+
$comments = $this->getContext()->comments;
10+
$object_id = $this->getContext()->document_id;
11+
$object = $this->getContext()->document;
1112

1213
$title = "Document Not Found";
1314
$description = "The requested document does not exist or could not be found.";
@@ -57,6 +58,26 @@ require("./header.inc.phtml");
5758
<?php } ?>
5859
</footer>
5960
</article>
61+
<?php if ($attachments) { ?>
62+
<article>
63+
<header>Attachments</header>
64+
<section>
65+
<table><thead><th>Filename</th><th>Size</th><th>Upload Date</th><th>Author</th></thead><tbody>
66+
<?php foreach ($attachments as $a) {
67+
$a_filename = htmlentities($a->getFilename(), ENT_HTML5, "UTF-8");
68+
$a_size = $a->getContentSize(true);
69+
$a_uploaddate = $a->getCreatedDateTime()->format("l, F j, Y");
70+
$a_author_name = $a->getUser()->getName();
71+
$a_author_id = $a->getUserId();
72+
$a_author_url = Common::relativeUrlToAbsolute("/user/" . $a_author_id . "/" . Common::sanitizeForUrl($a_author_name, true));
73+
$a_author_avatar = "https:" . (new Gravatar($a->getUser()->getEmail()))->getUrl(22, "identicon");
74+
?>
75+
<tr><td><?php echo $a_filename; ?></td><td><?php echo $a_size; ?></td><td><?php echo $a_uploaddate; ?></td><td><a href="<?php echo $a_author_url; ?>"><img class="avatar" src="<?php echo $a_author_avatar; ?>"/> <?php echo htmlspecialchars($a_author_name, ENT_HTML5, "UTF-8"); ?></a></td></tr>
76+
<?php } ?>
77+
</tbody></table>
78+
</section>
79+
</article>
80+
<?php } ?>
6081
<article>
6182
<header>Comments</header>
6283
<section>

0 commit comments

Comments
 (0)