visible && $fields['visible']) $fields['ts'] = $cur_ts; $fields['update_ts'] = $cur_ts; if ($fields['md'] != $this->md) { $fields['html'] = markup::markdownToHtml($fields['md']); $fields['text'] = markup::htmlToText($fields['html']); } if ((isset($fields['toc']) && $fields['toc']) || $this->toc) { $fields['toc_html'] = markup::toc($fields['md']); } parent::edit($fields); $this->updateImagePreviews(); } function updateHtml() { $html = markup::markdownToHtml($this->md); $this->html = $html; DB()->query("UPDATE posts SET html=? WHERE id=?", $html, $this->id); } function updateText() { $html = markup::markdownToHtml($this->md); $text = markup::htmlToText($html); $this->text = $text; DB()->query("UPDATE posts SET text=? WHERE id=?", $text, $this->id); } function getDescriptionPreview(int $len): string { if (mb_strlen($this->text) >= $len) return mb_substr($this->text, 0, $len-3).'...'; return $this->text; } function getFirstImage(): ?Upload { if (!preg_match('/\{image:([\w]{8})/', $this->md, $match)) return null; return uploads::getUploadByRandomId($match[1]); } function getUrl(): string { return $this->shortName != '' ? "/{$this->shortName}/" : "/{$this->id}/"; } function getDate(): string { return date('j M', $this->ts); } function getYear(): int { return (int)date('Y', $this->ts); } function getFullDate(): string { return date('j F Y', $this->ts); } function getUpdateDate(): string { return date('j M', $this->updateTs); } function getFullUpdateDate(): string { return date('j F Y', $this->updateTs); } function getHtml(bool $is_retina, string $theme): string { $html = $this->html; $html = markup::htmlImagesFix($html, $is_retina, $theme); return $html; } function getToc(): ?string { return $this->toc ? $this->tocHtml : null; } function isUpdated(): bool { return $this->updateTs && $this->updateTs != $this->ts; } /** * @return Tag[] */ function getTags(): array { $db = DB(); $q = $db->query("SELECT tags.* FROM posts_tags LEFT JOIN tags ON tags.id=posts_tags.tag_id WHERE posts_tags.post_id=? ORDER BY posts_tags.tag_id", $this->id); return array_map('Tag::create_instance', $db->fetchAll($q)); } /** * @return int[] */ function getTagIds(): array { $ids = []; $db = DB(); $q = $db->query("SELECT tag_id FROM posts_tags WHERE post_id=? ORDER BY tag_id", $this->id); while ($row = $db->fetch($q)) { $ids[] = (int)$row['tag_id']; } return $ids; } function setTagIds(array $new_tag_ids) { $cur_tag_ids = $this->getTagIds(); $add_tag_ids = array_diff($new_tag_ids, $cur_tag_ids); $rm_tag_ids = array_diff($cur_tag_ids, $new_tag_ids); $db = DB(); if (!empty($add_tag_ids)) { $rows = []; foreach ($add_tag_ids as $id) $rows[] = ['post_id' => $this->id, 'tag_id' => $id]; $db->multipleInsert('posts_tags', $rows); } if (!empty($rm_tag_ids)) $db->query("DELETE FROM posts_tags WHERE post_id=? AND tag_id IN(".implode(',', $rm_tag_ids).")", $this->id); $upd_tag_ids = array_merge($new_tag_ids, $rm_tag_ids); $upd_tag_ids = array_unique($upd_tag_ids); foreach ($upd_tag_ids as $id) tags::recountTagPosts($id); } /** * @param bool $update Whether to overwrite preview if already exists * @return int * @throws Exception */ function updateImagePreviews(bool $update = false): int { $images = []; if (!preg_match_all('/\{image:([\w]{8}),(.*?)}/', $this->md, $matches)) return 0; for ($i = 0; $i < count($matches[0]); $i++) { $id = $matches[1][$i]; $w = $h = null; $opts = explode(',', $matches[2][$i]); foreach ($opts as $opt) { if (str_contains($opt, '=')) { list($k, $v) = explode('=', $opt); if ($k == 'w') $w = (int)$v; else if ($k == 'h') $h = (int)$v; } } $images[$id][] = [$w, $h]; } if (empty($images)) return 0; $images_affected = 0; $uploads = uploads::getUploadsByRandomId(array_keys($images), true); foreach ($uploads as $u) { foreach ($images[$u->randomId] as $s) { list($w, $h) = $s; list($w, $h) = $u->getImagePreviewSize($w, $h); if ($u->createImagePreview($w, $h, $update, $u->imageMayHaveAlphaChannel())) $images_affected++; } } return $images_affected; } } class posts { static function getCount(bool $include_hidden = false): int { $db = DB(); $sql = "SELECT COUNT(*) FROM posts"; if (!$include_hidden) { $sql .= " WHERE visible=1"; } return (int)$db->result($db->query($sql)); } static function getCountByTagId(int $tag_id, bool $include_hidden = false): int { $db = DB(); if ($include_hidden) { $sql = "SELECT COUNT(*) FROM posts_tags WHERE tag_id=?"; } else { $sql = "SELECT COUNT(*) FROM posts_tags LEFT JOIN posts ON posts.id=posts_tags.post_id WHERE posts_tags.tag_id=? AND posts.visible=1"; } return (int)$db->result($db->query($sql, $tag_id)); } /** * @return Post[] */ static function getList(int $offset = 0, int $count = -1, bool $include_hidden = false): array { $db = DB(); $sql = "SELECT * FROM posts"; if (!$include_hidden) $sql .= " WHERE visible=1"; $sql .= " ORDER BY ts DESC"; if ($offset != 0 && $count != -1) $sql .= "LIMIT $offset, $count"; $q = $db->query($sql); return array_map('Post::create_instance', $db->fetchAll($q)); } /** * @return Post[] */ static function getPostsByTagId(int $tag_id, bool $include_hidden = false): array { $db = DB(); $sql = "SELECT posts.* FROM posts_tags LEFT JOIN posts ON posts.id=posts_tags.post_id WHERE posts_tags.tag_id=?"; if (!$include_hidden) $sql .= " AND posts.visible=1"; $sql .= " ORDER BY posts.ts DESC"; $q = $db->query($sql, $tag_id); return array_map('Post::create_instance', $db->fetchAll($q)); } static function add(array $data = []): int|bool { $db = DB(); $html = \markup::markdownToHtml($data['md']); $text = \markup::htmlToText($html); $data += [ 'ts' => time(), 'html' => $html, 'text' => $text, ]; if (!$db->insert('posts', $data)) return false; $id = $db->insertId(); $post = self::get($id); $post->updateImagePreviews(); return $id; } static function delete(Post $post): void { $tags = $post->getTags(); $db = DB(); $db->query("DELETE FROM posts WHERE id=?", $post->id); $db->query("DELETE FROM posts_tags WHERE post_id=?", $post->id); foreach ($tags as $tag) tags::recountTagPosts($tag->id); } static function get(int $id): ?Post { $db = DB(); $q = $db->query("SELECT * FROM posts WHERE id=?", $id); return $db->numRows($q) ? new Post($db->fetch($q)) : null; } static function getByName(string $short_name): ?Post { $db = DB(); $q = $db->query("SELECT * FROM posts WHERE short_name=?", $short_name); return $db->numRows($q) ? new Post($db->fetch($q)) : null; } static function getPostsById(array $ids, bool $flat = false): array { if (empty($ids)) { return []; } $db = DB(); $posts = array_fill_keys($ids, null); $q = $db->query("SELECT * FROM posts WHERE id IN(".implode(',', $ids).")"); while ($row = $db->fetch($q)) { $posts[(int)$row['id']] = new Post($row); } if ($flat) { $list = []; foreach ($ids as $id) { $list[] = $posts[$id]; } unset($posts); return $list; } return $posts; } }