1 | <?php |
||
2 | |||
3 | namespace Frontend\Modules\Blog\Engine; |
||
4 | |||
5 | use Common\Mailer\Message; |
||
6 | use ForkCMS\Utility\Thumbnails; |
||
7 | use Frontend\Core\Language\Language as FL; |
||
8 | use Frontend\Core\Engine\Model as FrontendModel; |
||
9 | use Frontend\Core\Engine\Navigation as FrontendNavigation; |
||
10 | use Frontend\Core\Engine\Url as FrontendUrl; |
||
11 | use Frontend\Core\Language\Locale; |
||
12 | use Frontend\Modules\Tags\Engine\Model as FrontendTagsModel; |
||
13 | use Frontend\Modules\Tags\Engine\TagsInterface as FrontendTagsInterface; |
||
14 | |||
15 | /** |
||
16 | * In this file we store all generic functions that we will be using in the blog module |
||
17 | */ |
||
18 | class Model implements FrontendTagsInterface |
||
19 | { |
||
20 | 3 | public static function get(string $url): array |
|
21 | { |
||
22 | 3 | $blogPost = (array) FrontendModel::getContainer()->get('database')->getRecord( |
|
23 | 3 | 'SELECT i.id, i.revision_id, i.language, i.title, i.introduction, i.text, |
|
24 | c.title AS category_title, m2.url AS category_url, i.image, |
||
25 | UNIX_TIMESTAMP(i.publish_on) AS publish_on, i.user_id, |
||
26 | i.allow_comments, m.id AS meta_id, |
||
27 | m.keywords AS meta_keywords, m.keywords_overwrite AS meta_keywords_overwrite, |
||
28 | m.description AS meta_description, m.description_overwrite AS meta_description_overwrite, |
||
29 | m.title AS meta_title, m.title_overwrite AS meta_title_overwrite, m.custom AS meta_custom, |
||
30 | m.url, |
||
31 | m.data AS meta_data, m.seo_follow AS meta_seo_follow, m.seo_index AS meta_seo_index |
||
32 | FROM blog_posts AS i |
||
33 | INNER JOIN blog_categories AS c ON i.category_id = c.id |
||
34 | INNER JOIN meta AS m ON i.meta_id = m.id |
||
35 | INNER JOIN meta AS m2 ON c.meta_id = m2.id |
||
36 | WHERE i.status = ? AND i.language = ? AND i.hidden = ? AND i.publish_on <= ? AND m.url = ? |
||
37 | LIMIT 1', |
||
38 | 3 | ['active', LANGUAGE, false, FrontendModel::getUTCDate('Y-m-d H:i'), $url] |
|
39 | ); |
||
40 | |||
41 | 3 | return self::completeBlogPost($blogPost); |
|
42 | } |
||
43 | |||
44 | 3 | public static function getAll(int $limit = 10, int $offset = 0): array |
|
45 | { |
||
46 | 3 | $items = (array) FrontendModel::getContainer()->get('database')->getRecords( |
|
47 | 3 | 'SELECT i.id, i.revision_id, i.language, i.title, i.introduction, i.text, i.num_comments AS comments_count, |
|
48 | c.title AS category_title, m2.url AS category_url, i.image, |
||
49 | UNIX_TIMESTAMP(i.publish_on) AS publish_on, i.user_id, i.allow_comments, |
||
50 | m.url |
||
51 | FROM blog_posts AS i |
||
52 | INNER JOIN blog_categories AS c ON i.category_id = c.id |
||
53 | INNER JOIN meta AS m ON i.meta_id = m.id |
||
54 | INNER JOIN meta AS m2 ON c.meta_id = m2.id |
||
55 | WHERE i.status = ? AND i.language = ? AND i.hidden = ? AND i.publish_on <= ? |
||
56 | ORDER BY i.publish_on DESC, i.id DESC |
||
57 | LIMIT ?, ?', |
||
58 | [ |
||
59 | 3 | 'active', |
|
60 | 3 | LANGUAGE, |
|
61 | false, |
||
62 | 3 | FrontendModel::getUTCDate('Y-m-d H:i'), |
|
63 | 3 | $offset, |
|
64 | 3 | $limit, |
|
65 | ], |
||
66 | 3 | 'id' |
|
67 | ); |
||
68 | |||
69 | // no results? |
||
70 | 3 | if (empty($items)) { |
|
71 | 1 | return []; |
|
72 | } |
||
73 | |||
74 | // init var |
||
75 | 2 | $link = FrontendNavigation::getUrlForBlock('Blog', 'Detail'); |
|
76 | 2 | $categoryLink = FrontendNavigation::getUrlForBlock('Blog', 'Category'); |
|
77 | 2 | $folders = FrontendModel::get(Thumbnails::class)->getFolders(FRONTEND_FILES_PATH . '/Blog/images', true); |
|
78 | |||
79 | // loop |
||
80 | 2 | foreach ($items as $key => $row) { |
|
81 | // URLs |
||
82 | 2 | $items[$key]['full_url'] = $link . '/' . $row['url']; |
|
83 | 2 | $items[$key]['category_full_url'] = $categoryLink . '/' . $row['category_url']; |
|
84 | |||
85 | // comments |
||
86 | 2 | if ($row['comments_count'] > 0) { |
|
87 | $items[$key]['comments'] = true; |
||
88 | } |
||
89 | 2 | if ($row['comments_count'] > 1) { |
|
90 | $items[$key]['comments_multiple'] = true; |
||
91 | } |
||
92 | |||
93 | // allow comments as boolean |
||
94 | 2 | $items[$key]['allow_comments'] = (bool) $row['allow_comments']; |
|
95 | |||
96 | // reset allow comments |
||
97 | 2 | if (!FrontendModel::get('fork.settings')->get('Blog', 'allow_comments')) { |
|
98 | $items[$key]['allow_comments'] = false; |
||
99 | } |
||
100 | |||
101 | // image? |
||
102 | 2 | if (isset($row['image'])) { |
|
103 | foreach ($folders as $folder) { |
||
104 | 2 | $items[$key]['image_' . $folder['dirname']] = $folder['url'] . '/' . $folder['dirname'] . '/' . $row['image']; |
|
105 | } |
||
106 | } |
||
107 | } |
||
108 | |||
109 | // get all tags |
||
110 | 2 | $tags = FrontendTagsModel::getForMultipleItems('Blog', array_keys($items)); |
|
111 | |||
112 | // loop tags and add to correct item |
||
113 | 2 | foreach ($tags as $postId => $data) { |
|
114 | if (isset($items[$postId])) { |
||
115 | $items[$postId]['tags'] = $data; |
||
116 | } |
||
117 | } |
||
118 | |||
119 | // return |
||
120 | 2 | return $items; |
|
121 | } |
||
122 | |||
123 | 2 | public static function getAllCategories(): array |
|
124 | { |
||
125 | 2 | $return = (array) FrontendModel::getContainer()->get('database')->getRecords( |
|
126 | 2 | 'SELECT c.id, c.title AS label, m.url, COUNT(c.id) AS total, m.data AS meta_data, |
|
127 | m.seo_follow AS meta_seo_follow, m.seo_index AS meta_seo_index |
||
128 | FROM blog_categories AS c |
||
129 | INNER JOIN blog_posts AS i ON c.id = i.category_id AND c.language = i.language |
||
130 | INNER JOIN meta AS m ON c.meta_id = m.id |
||
131 | WHERE c.language = ? AND i.status = ? AND i.hidden = ? AND i.publish_on <= ? |
||
132 | GROUP BY c.id', |
||
133 | 2 | [LANGUAGE, 'active', false, FrontendModel::getUTCDate('Y-m-d H:i')], |
|
134 | 2 | 'id' |
|
135 | ); |
||
136 | |||
137 | // loop items and unserialize |
||
138 | 2 | foreach ($return as &$row) { |
|
139 | 2 | if (isset($row['meta_data'])) { |
|
140 | 2 | $row['meta_data'] = @unserialize($row['meta_data'], ['allowed_classes' => false]); |
|
141 | } |
||
142 | } |
||
143 | |||
144 | 2 | return $return; |
|
145 | } |
||
146 | |||
147 | 4 | public static function getCategory(string $slug): array |
|
148 | { |
||
149 | 4 | $category = (array) FrontendModel::getContainer()->get('database')->getRecord( |
|
150 | 4 | 'SELECT c.id, c.title AS label, m.url, m.id AS meta_id, COUNT(c.id) AS total |
|
151 | FROM blog_categories AS c |
||
152 | INNER JOIN blog_posts AS i ON c.id = i.category_id AND c.language = i.language |
||
153 | INNER JOIN meta AS m ON c.meta_id = m.id AND m.url = ? |
||
154 | WHERE c.language = ? AND i.status = ? AND i.hidden = ? AND i.publish_on <= ? |
||
155 | GROUP BY c.id', |
||
156 | 4 | [$slug, LANGUAGE, 'active', false, FrontendModel::getUTCDate('Y-m-d H:i')], |
|
157 | 4 | 'id' |
|
158 | ); |
||
159 | |||
160 | 4 | if (empty($category)) { |
|
161 | 1 | return []; |
|
162 | } |
||
163 | |||
164 | 3 | $category['meta'] = FrontendModel::get('fork.repository.meta')->find($category['meta_id']); |
|
165 | |||
166 | 3 | return $category; |
|
167 | } |
||
168 | |||
169 | public static function getAllComments(int $limit = 10, int $offset = 0): array |
||
170 | { |
||
171 | $comments = (array) FrontendModel::getContainer()->get('database')->getRecords( |
||
172 | 'SELECT i.id, UNIX_TIMESTAMP(i.created_on) AS created_on, i.author, i.text, |
||
173 | p.id AS post_id, p.title AS post_title, m.url AS post_url, i.email |
||
174 | FROM blog_comments AS i |
||
175 | INNER JOIN blog_posts AS p ON i.post_id = p.id AND i.language = p.language |
||
176 | INNER JOIN meta AS m ON p.meta_id = m.id |
||
177 | WHERE i.status = ? AND i.language = ? |
||
178 | GROUP BY i.id |
||
179 | ORDER BY i.created_on DESC |
||
180 | LIMIT ?, ?', |
||
181 | ['published', LANGUAGE, $offset, $limit] |
||
182 | ); |
||
183 | |||
184 | // loop comments and create gravatar id |
||
185 | foreach ($comments as &$row) { |
||
186 | $row['author'] = htmlspecialchars($row['author']); |
||
187 | $row['text'] = htmlspecialchars($row['text']); |
||
188 | $row['gravatar_id'] = md5($row['email']); |
||
189 | } |
||
190 | |||
191 | return $comments; |
||
192 | } |
||
193 | |||
194 | 3 | public static function getAllCount(): int |
|
195 | { |
||
196 | 3 | return (int) FrontendModel::getContainer()->get('database')->getVar( |
|
197 | 3 | 'SELECT COUNT(i.id) AS count |
|
198 | FROM blog_posts AS i |
||
199 | WHERE i.status = ? AND i.language = ? AND i.hidden = ? AND i.publish_on <= ?', |
||
200 | 3 | ['active', LANGUAGE, false, FrontendModel::getUTCDate('Y-m-d H:i')] |
|
201 | ); |
||
202 | } |
||
203 | |||
204 | 3 | public static function getAllForCategory(string $categoryUrl, int $limit = 10, int $offset = 0): array |
|
205 | { |
||
206 | 3 | $items = (array) FrontendModel::getContainer()->get('database')->getRecords( |
|
207 | 3 | 'SELECT i.id, i.revision_id, i.language, i.title, i.introduction, i.text, i.num_comments AS comments_count, |
|
208 | c.title AS category_title, m2.url AS category_url, i.image, |
||
209 | UNIX_TIMESTAMP(i.publish_on) AS publish_on, i.user_id, i.allow_comments, |
||
210 | m.url |
||
211 | FROM blog_posts AS i |
||
212 | INNER JOIN blog_categories AS c ON i.category_id = c.id |
||
213 | INNER JOIN meta AS m ON i.meta_id = m.id |
||
214 | INNER JOIN meta AS m2 ON c.meta_id = m2.id |
||
215 | WHERE i.status = ? AND i.language = ? AND i.hidden = ? AND i.publish_on <= ? AND m2.url = ? |
||
216 | ORDER BY i.publish_on DESC |
||
217 | LIMIT ?, ?', |
||
218 | [ |
||
219 | 3 | 'active', |
|
220 | 3 | LANGUAGE, |
|
221 | false, |
||
222 | 3 | FrontendModel::getUTCDate('Y-m-d H:i'), |
|
223 | 3 | $categoryUrl, |
|
224 | 3 | $offset, |
|
225 | 3 | $limit, |
|
226 | ], |
||
227 | 3 | 'id' |
|
228 | ); |
||
229 | |||
230 | // no results? |
||
231 | 3 | if (empty($items)) { |
|
232 | return []; |
||
233 | } |
||
234 | |||
235 | // init var |
||
236 | 3 | $link = FrontendNavigation::getUrlForBlock('Blog', 'Detail'); |
|
237 | 3 | $categoryLink = FrontendNavigation::getUrlForBlock('Blog', 'Category'); |
|
238 | 3 | $folders = FrontendModel::get(Thumbnails::class)->getFolders(FRONTEND_FILES_PATH . '/Blog/images', true); |
|
239 | |||
240 | // loop |
||
241 | 3 | foreach ($items as $key => $row) { |
|
242 | // URLs |
||
243 | 3 | $items[$key]['full_url'] = $link . '/' . $row['url']; |
|
244 | 3 | $items[$key]['category_full_url'] = $categoryLink . '/' . $row['category_url']; |
|
245 | |||
246 | // comments |
||
247 | 3 | if ($row['comments_count'] > 0) { |
|
248 | $items[$key]['comments'] = true; |
||
249 | } |
||
250 | 3 | if ($row['comments_count'] > 1) { |
|
251 | $items[$key]['comments_multiple'] = true; |
||
252 | } |
||
253 | |||
254 | // allow comments as boolean |
||
255 | 3 | $items[$key]['allow_comments'] = (bool) $row['allow_comments']; |
|
256 | |||
257 | // reset allow comments |
||
258 | 3 | if (!FrontendModel::get('fork.settings')->get('Blog', 'allow_comments')) { |
|
259 | $items[$key]['allow_comments'] = false; |
||
260 | } |
||
261 | |||
262 | // image? |
||
263 | 3 | if (isset($row['image'])) { |
|
264 | foreach ($folders as $folder) { |
||
265 | $items[$key]['image_' . $folder['dirname']] = $folder['url'] . '/' . $folder['dirname'] . |
||
266 | 3 | '/' . $row['image']; |
|
267 | } |
||
268 | } |
||
269 | } |
||
270 | |||
271 | // get all tags |
||
272 | 3 | $tags = FrontendTagsModel::getForMultipleItems('Blog', array_keys($items)); |
|
273 | |||
274 | // loop tags and add to correct item |
||
275 | 3 | foreach ($tags as $postId => $data) { |
|
276 | $items[$postId]['tags'] = $data; |
||
277 | } |
||
278 | |||
279 | // return |
||
280 | 3 | return $items; |
|
281 | } |
||
282 | |||
283 | 3 | public static function getAllForCategoryCount(string $url): int |
|
284 | { |
||
285 | 3 | return (int) FrontendModel::getContainer()->get('database')->getVar( |
|
286 | 3 | 'SELECT COUNT(i.id) AS count |
|
287 | FROM blog_posts AS i |
||
288 | INNER JOIN blog_categories AS c ON i.category_id = c.id |
||
289 | INNER JOIN meta AS m ON c.meta_id = m.id |
||
290 | WHERE i.status = ? AND i.language = ? AND i.hidden = ? AND i.publish_on <= ? AND m.url = ?', |
||
291 | 3 | ['active', LANGUAGE, false, FrontendModel::getUTCDate('Y-m-d H:i'), $url] |
|
292 | ); |
||
293 | } |
||
294 | |||
295 | /** |
||
296 | * Get all items between a start and end-date |
||
297 | * |
||
298 | * @param int $start The start date as a UNIX-timestamp. |
||
299 | * @param int $end The end date as a UNIX-timestamp. |
||
300 | * @param int $limit The number of items to get. |
||
301 | * @param int $offset The offset. |
||
302 | * |
||
303 | * @return array |
||
304 | */ |
||
305 | 2 | public static function getAllForDateRange(int $start, int $end, int $limit = 10, int $offset = 0): array |
|
306 | { |
||
307 | // get the items |
||
308 | 2 | $items = (array) FrontendModel::getContainer()->get('database')->getRecords( |
|
309 | 2 | 'SELECT i.id, i.revision_id, i.language, i.title, i.introduction, i.text, i.num_comments AS comments_count, |
|
310 | c.title AS category_title, m2.url AS category_url, i.image, |
||
311 | UNIX_TIMESTAMP(i.publish_on) AS publish_on, i.user_id, i.allow_comments, |
||
312 | m.url |
||
313 | FROM blog_posts AS i |
||
314 | INNER JOIN blog_categories AS c ON i.category_id = c.id |
||
315 | INNER JOIN meta AS m ON i.meta_id = m.id |
||
316 | INNER JOIN meta AS m2 ON c.meta_id = m2.id |
||
317 | WHERE i.status = ? AND i.language = ? AND i.hidden = ? AND i.publish_on BETWEEN ? AND ? |
||
318 | ORDER BY i.publish_on DESC |
||
319 | LIMIT ?, ?', |
||
320 | [ |
||
321 | 2 | 'active', |
|
322 | 2 | LANGUAGE, |
|
323 | false, |
||
324 | 2 | FrontendModel::getUTCDate('Y-m-d H:i', $start), |
|
325 | 2 | FrontendModel::getUTCDate('Y-m-d H:i', $end), |
|
326 | 2 | $offset, |
|
327 | 2 | $limit, |
|
328 | ], |
||
329 | 2 | 'id' |
|
330 | ); |
||
331 | |||
332 | // no results? |
||
333 | 2 | if (empty($items)) { |
|
334 | return []; |
||
335 | } |
||
336 | |||
337 | // init var |
||
338 | 2 | $link = FrontendNavigation::getUrlForBlock('Blog', 'Detail'); |
|
339 | 2 | $folders = FrontendModel::get(Thumbnails::class)->getFolders(FRONTEND_FILES_PATH . '/Blog/images', true); |
|
340 | |||
341 | // loop |
||
342 | 2 | foreach ($items as $key => $row) { |
|
343 | // URLs |
||
344 | 2 | $items[$key]['full_url'] = $link . '/' . $row['url']; |
|
345 | |||
346 | // comments |
||
347 | 2 | if ($row['comments_count'] > 0) { |
|
348 | $items[$key]['comments'] = true; |
||
349 | } |
||
350 | 2 | if ($row['comments_count'] > 1) { |
|
351 | $items[$key]['comments_multiple'] = true; |
||
352 | } |
||
353 | |||
354 | // allow comments as boolean |
||
355 | 2 | $items[$key]['allow_comments'] = (bool) $row['allow_comments']; |
|
356 | |||
357 | // reset allow comments |
||
358 | 2 | if (!FrontendModel::get('fork.settings')->get('Blog', 'allow_comments')) { |
|
359 | $items[$key]['allow_comments'] = false; |
||
360 | } |
||
361 | |||
362 | // image? |
||
363 | 2 | if (isset($row['image'])) { |
|
364 | foreach ($folders as $folder) { |
||
365 | $items[$key]['image_' . $folder['dirname']] = $folder['url'] . '/' . $folder['dirname'] . |
||
366 | 2 | '/' . $row['image']; |
|
367 | } |
||
368 | } |
||
369 | } |
||
370 | |||
371 | // get all tags |
||
372 | 2 | $tags = FrontendTagsModel::getForMultipleItems('Blog', array_keys($items)); |
|
373 | |||
374 | // loop tags and add to correct item |
||
375 | 2 | foreach ($tags as $postId => $data) { |
|
376 | $items[$postId]['tags'] = $data; |
||
377 | } |
||
378 | |||
379 | // return |
||
380 | 2 | return $items; |
|
381 | } |
||
382 | |||
383 | /** |
||
384 | * Get the number of items in a date range |
||
385 | * |
||
386 | * @param int $start The start date as a UNIX-timestamp. |
||
387 | * @param int $end The end date as a UNIX-timestamp. |
||
388 | * |
||
389 | * @return int |
||
390 | */ |
||
391 | 4 | public static function getAllForDateRangeCount(int $start, int $end): int |
|
392 | { |
||
393 | // return the number of items |
||
394 | 4 | return (int) FrontendModel::getContainer()->get('database')->getVar( |
|
395 | 4 | 'SELECT COUNT(i.id) |
|
396 | FROM blog_posts AS i |
||
397 | WHERE i.status = ? AND i.language = ? AND i.hidden = ? AND i.publish_on BETWEEN ? AND ?', |
||
398 | [ |
||
399 | 4 | 'active', |
|
400 | 4 | LANGUAGE, |
|
401 | false, |
||
402 | 4 | FrontendModel::getUTCDate('Y-m-d H:i:s', $start), |
|
403 | 4 | FrontendModel::getUTCDate('Y-m-d H:i:s', $end), |
|
404 | ] |
||
405 | ); |
||
406 | } |
||
407 | |||
408 | /** |
||
409 | * Get the statistics for the archive |
||
410 | * |
||
411 | * @return array |
||
412 | */ |
||
413 | public static function getArchiveNumbers(): array |
||
414 | { |
||
415 | // grab stats |
||
416 | $numbers = FrontendModel::getContainer()->get('database')->getPairs( |
||
417 | 'SELECT DATE_FORMAT(i.publish_on, "%Y%m") AS month, COUNT(i.id) |
||
418 | FROM blog_posts AS i |
||
419 | INNER JOIN meta AS m ON i.meta_id = m.id |
||
420 | WHERE i.status = ? AND i.language = ? AND i.hidden = ? AND i.publish_on <= ? |
||
421 | GROUP BY month', |
||
422 | ['active', LANGUAGE, false, FrontendModel::getUTCDate('Y-m-d H:i')] |
||
423 | ); |
||
424 | |||
425 | $stats = []; |
||
426 | $link = FrontendNavigation::getUrlForBlock('Blog', 'Archive'); |
||
427 | $firstYear = (int) date('Y'); |
||
428 | $lastYear = 0; |
||
429 | |||
430 | // loop the numbers |
||
431 | foreach ($numbers as $key => $count) { |
||
432 | $year = mb_substr($key, 0, 4); |
||
433 | $month = mb_substr($key, 4, 2); |
||
434 | |||
435 | // reset |
||
436 | if ($year < $firstYear) { |
||
437 | $firstYear = $year; |
||
438 | } |
||
439 | if ($year > $lastYear) { |
||
440 | $lastYear = $year; |
||
441 | } |
||
442 | |||
443 | // generate timestamp |
||
444 | $timestamp = gmmktime(00, 00, 00, $month, 01, $year); |
||
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||
445 | |||
446 | // initialize if needed |
||
447 | if (!isset($stats[$year])) { |
||
448 | $stats[$year] = [ |
||
449 | 'url' => $link . '/' . $year, |
||
450 | 'label' => $year, |
||
451 | 'total' => 0, |
||
452 | 'months' => null, |
||
453 | ]; |
||
454 | } |
||
455 | |||
456 | // increment the total |
||
457 | $stats[$year]['total'] += (int) $count; |
||
458 | $stats[$year]['months'][$key] = [ |
||
459 | 'url' => $link . '/' . $year . '/' . $month, |
||
460 | 'label' => $timestamp, |
||
461 | 'total' => $count, |
||
462 | ]; |
||
463 | } |
||
464 | |||
465 | // loop years |
||
466 | for ($i = $firstYear; $i <= $lastYear; ++$i) { |
||
467 | // year missing |
||
468 | if (!isset($stats[$i])) { |
||
469 | $stats[$i] = ['url' => null, 'label' => $i, 'total' => 0, 'months' => null]; |
||
470 | } |
||
471 | } |
||
472 | |||
473 | // sort |
||
474 | krsort($stats); |
||
475 | |||
476 | // reset stats |
||
477 | foreach ($stats as &$row) { |
||
478 | // remove url for empty years |
||
479 | if ($row['total'] == 0) { |
||
480 | $row['url'] = null; |
||
481 | } |
||
482 | |||
483 | // any months? |
||
484 | if (!empty($row['months'])) { |
||
485 | // sort months |
||
486 | ksort($row['months']); |
||
487 | } |
||
488 | } |
||
489 | |||
490 | // return |
||
491 | return $stats; |
||
492 | } |
||
493 | |||
494 | 2 | public static function getComments(int $blogPostId): array |
|
495 | { |
||
496 | 2 | $comments = (array) FrontendModel::getContainer()->get('database')->getRecords( |
|
497 | 2 | 'SELECT c.id, UNIX_TIMESTAMP(c.created_on) AS created_on, c.text, c.data, |
|
498 | c.author, c.email, c.website |
||
499 | FROM blog_comments AS c |
||
500 | WHERE c.post_id = ? AND c.status = ? AND c.language = ? |
||
501 | ORDER BY c.id ASC', |
||
502 | 2 | [$blogPostId, 'published', LANGUAGE] |
|
503 | ); |
||
504 | |||
505 | 2 | foreach ($comments as &$row) { |
|
506 | $row['gravatar_id'] = md5($row['email']); |
||
507 | } |
||
508 | |||
509 | 2 | return $comments; |
|
510 | } |
||
511 | |||
512 | /** |
||
513 | * Fetch the list of tags for a list of items |
||
514 | * |
||
515 | * @param array $blogPostIds The ids of the items to grab. |
||
516 | * |
||
517 | * @return array |
||
518 | */ |
||
519 | public static function getForTags(array $blogPostIds): array |
||
520 | { |
||
521 | // fetch items |
||
522 | $items = (array) FrontendModel::getContainer()->get('database')->getRecords( |
||
523 | 'SELECT i.title, i.image, m.url |
||
524 | FROM blog_posts AS i |
||
525 | INNER JOIN meta AS m ON m.id = i.meta_id |
||
526 | WHERE i.status = ? AND i.hidden = ? AND i.id IN (' . implode(',', $blogPostIds) . ') AND i.publish_on <= ? |
||
527 | ORDER BY i.publish_on DESC', |
||
528 | ['active', false, FrontendModel::getUTCDate('Y-m-d H:i')] |
||
529 | ); |
||
530 | |||
531 | // has items |
||
532 | if (!empty($items)) { |
||
533 | // init var |
||
534 | $link = FrontendNavigation::getUrlForBlock('Blog', 'Detail'); |
||
535 | $folders = FrontendModel::get(Thumbnails::class)->getFolders(FRONTEND_FILES_PATH . '/Blog/images', true); |
||
536 | |||
537 | // reset url |
||
538 | foreach ($items as &$row) { |
||
539 | $row['full_url'] = $link . '/' . $row['url']; |
||
540 | |||
541 | // image? |
||
542 | if (isset($row['image'])) { |
||
543 | foreach ($folders as $folder) { |
||
544 | $row['image_' . $folder['dirname']] = $folder['url'] . '/' . $folder['dirname'] . |
||
545 | '/' . $row['image']; |
||
546 | } |
||
547 | } |
||
548 | } |
||
549 | } |
||
550 | |||
551 | // return |
||
552 | return $items; |
||
553 | } |
||
554 | |||
555 | /** |
||
556 | * Get the id of an item by the full URL of the current page. |
||
557 | * Selects the proper part of the full URL to get the item's id from the database. |
||
558 | * |
||
559 | * @param FrontendUrl $url The current URL. |
||
560 | * |
||
561 | * @return int |
||
562 | */ |
||
563 | public static function getIdForTags(FrontendUrl $url): int |
||
564 | { |
||
565 | // select the proper part of the full URL |
||
566 | $itemUrl = (string) $url->getParameter(1); |
||
567 | |||
568 | // return the item |
||
569 | return self::get($itemUrl)['id'] ?? 0; |
||
570 | } |
||
571 | |||
572 | /** |
||
573 | * Get an array with the previous and the next post |
||
574 | * |
||
575 | * @param int $blogPostId The id of the current item. |
||
576 | * |
||
577 | * @return array |
||
578 | */ |
||
579 | 2 | public static function getNavigation(int $blogPostId): array |
|
580 | { |
||
581 | // get database |
||
582 | 2 | $database = FrontendModel::getContainer()->get('database'); |
|
583 | |||
584 | // get date for current item |
||
585 | 2 | $date = (string) $database->getVar( |
|
586 | 2 | 'SELECT i.publish_on |
|
587 | FROM blog_posts AS i |
||
588 | WHERE i.id = ? AND i.status = ?', |
||
589 | 2 | [$blogPostId, 'active'] |
|
590 | ); |
||
591 | |||
592 | // validate |
||
593 | 2 | if ($date === '') { |
|
594 | return []; |
||
595 | } |
||
596 | |||
597 | // init var |
||
598 | 2 | $navigation = []; |
|
599 | 2 | $detailLink = FrontendNavigation::getUrlForBlock('Blog', 'Detail') . '/'; |
|
600 | |||
601 | // get previous post |
||
602 | 2 | $navigation['previous'] = $database->getRecord( |
|
603 | 2 | 'SELECT i.id, i.title, CONCAT(?, m.url) AS url |
|
604 | FROM blog_posts AS i |
||
605 | INNER JOIN meta AS m ON i.meta_id = m.id |
||
606 | WHERE i.id != ? AND i.status = ? AND i.hidden = ? AND i.language = ? AND |
||
607 | ((i.publish_on = ? AND i.id < ?) OR i.publish_on < ?) |
||
608 | ORDER BY i.publish_on DESC, i.id DESC |
||
609 | LIMIT 1', |
||
610 | 2 | [$detailLink, $blogPostId, 'active', false, LANGUAGE, $date, $blogPostId, $date] |
|
611 | ); |
||
612 | |||
613 | // get next post |
||
614 | 2 | $navigation['next'] = $database->getRecord( |
|
615 | 2 | 'SELECT i.id, i.title, CONCAT(?, m.url) AS url |
|
616 | FROM blog_posts AS i |
||
617 | INNER JOIN meta AS m ON i.meta_id = m.id |
||
618 | WHERE i.id != ? AND i.status = ? AND i.hidden = ? AND i.language = ? AND |
||
619 | ((i.publish_on = ? AND i.id > ?) OR (i.publish_on > ? AND i.publish_on <= ?)) |
||
620 | ORDER BY i.publish_on ASC, i.id ASC |
||
621 | LIMIT 1', |
||
622 | [ |
||
623 | 2 | $detailLink, |
|
624 | 2 | $blogPostId, |
|
625 | 2 | 'active', |
|
626 | false, |
||
627 | 2 | LANGUAGE, |
|
628 | 2 | $date, |
|
629 | 2 | $blogPostId, |
|
630 | 2 | $date, |
|
631 | 2 | FrontendModel::getUTCDate('Y-m-d H:i'), |
|
632 | ] |
||
633 | ); |
||
634 | |||
635 | // if empty, unset it |
||
636 | 2 | if (empty($navigation['previous'])) { |
|
637 | 2 | unset($navigation['previous']); |
|
638 | } |
||
639 | 2 | if (empty($navigation['next'])) { |
|
640 | 2 | unset($navigation['next']); |
|
641 | } |
||
642 | |||
643 | // return |
||
644 | 2 | return $navigation; |
|
645 | } |
||
646 | |||
647 | public static function getRecentComments(int $limit = 5): array |
||
648 | { |
||
649 | // init var |
||
650 | $return = []; |
||
651 | |||
652 | // get comments |
||
653 | $comments = (array) FrontendModel::getContainer()->get('database')->getRecords( |
||
654 | 'SELECT c.id, c.author, c.website, c.email, UNIX_TIMESTAMP(c.created_on) AS created_on, c.text, |
||
655 | i.id AS post_id, i.title AS post_title, |
||
656 | m.url AS post_url |
||
657 | FROM blog_comments AS c |
||
658 | INNER JOIN blog_posts AS i ON c.post_id = i.id AND c.language = i.language |
||
659 | INNER JOIN meta AS m ON i.meta_id = m.id |
||
660 | WHERE c.status = ? AND i.status = ? AND i.language = ? AND i.hidden = ? AND i.publish_on <= ? |
||
661 | ORDER BY c.id DESC |
||
662 | LIMIT ?', |
||
663 | ['published', 'active', LANGUAGE, false, FrontendModel::getUTCDate('Y-m-d H:i'), $limit] |
||
664 | ); |
||
665 | |||
666 | // validate |
||
667 | if (empty($comments)) { |
||
668 | return $return; |
||
669 | } |
||
670 | |||
671 | // get link |
||
672 | $link = FrontendNavigation::getUrlForBlock('Blog', 'Detail'); |
||
673 | |||
674 | // loop comments |
||
675 | foreach ($comments as &$row) { |
||
676 | // add some URLs |
||
677 | $row['post_full_url'] = $link . '/' . $row['post_url']; |
||
678 | $row['full_url'] = $link . '/' . $row['post_url'] . '#comment-' . $row['id']; |
||
679 | $row['gravatar_id'] = md5($row['email']); |
||
680 | } |
||
681 | |||
682 | return $comments; |
||
683 | } |
||
684 | |||
685 | public static function getRelated(int $blogPostId, int $limit = 5): array |
||
686 | { |
||
687 | // get the related IDs |
||
688 | $relatedIDs = (array) FrontendTagsModel::getRelatedItemsByTags($blogPostId, 'Blog', 'Blog', $limit); |
||
689 | |||
690 | // no items |
||
691 | if (empty($relatedIDs)) { |
||
692 | return []; |
||
693 | } |
||
694 | |||
695 | // get link |
||
696 | $link = FrontendNavigation::getUrlForBlock('Blog', 'Detail'); |
||
697 | |||
698 | // get items |
||
699 | $items = (array) FrontendModel::getContainer()->get('database')->getRecords( |
||
700 | 'SELECT i.id, i.title, m.url |
||
701 | FROM blog_posts AS i |
||
702 | INNER JOIN meta AS m ON i.meta_id = m.id |
||
703 | WHERE i.status = ? AND i.language = ? AND i.hidden = ? AND i.publish_on <= ? AND i.id IN(' . |
||
704 | implode(',', $relatedIDs) . ') |
||
705 | ORDER BY i.publish_on DESC, i.id DESC |
||
706 | LIMIT ?', |
||
707 | ['active', LANGUAGE, false, FrontendModel::getUTCDate('Y-m-d H:i'), $limit], |
||
708 | 'id' |
||
709 | ); |
||
710 | |||
711 | // loop items |
||
712 | foreach ($items as &$row) { |
||
713 | $row['full_url'] = $link . '/' . $row['url']; |
||
714 | } |
||
715 | |||
716 | return $items; |
||
717 | } |
||
718 | |||
719 | public static function getRevision(string $url, int $revisionId): array |
||
720 | { |
||
721 | $blogPost = (array) FrontendModel::getContainer()->get('database')->getRecord( |
||
722 | 'SELECT i.id, i.revision_id, i.language, i.title, i.introduction, i.text, i.image, |
||
723 | c.title AS category_title, m2.url AS category_url, m.id AS meta_id, |
||
724 | UNIX_TIMESTAMP(i.publish_on) AS publish_on, i.user_id, |
||
725 | i.allow_comments, |
||
726 | m.keywords AS meta_keywords, m.keywords_overwrite AS meta_keywords_overwrite, |
||
727 | m.description AS meta_description, m.description_overwrite AS meta_description_overwrite, |
||
728 | m.title AS meta_title, m.title_overwrite AS meta_title_overwrite, m.custom AS meta_custom, |
||
729 | m.url, |
||
730 | m.data AS meta_data, m.seo_follow AS meta_seo_follow, m.seo_index AS meta_seo_index |
||
731 | FROM blog_posts AS i |
||
732 | INNER JOIN blog_categories AS c ON i.category_id = c.id |
||
733 | INNER JOIN meta AS m ON i.meta_id = m.id |
||
734 | INNER JOIN meta AS m2 ON c.meta_id = m2.id |
||
735 | WHERE i.language = ? AND i.revision_id = ? AND m.url = ? |
||
736 | LIMIT 1', |
||
737 | [LANGUAGE, $revisionId, $url] |
||
738 | ); |
||
739 | |||
740 | return self::completeBlogPost($blogPost); |
||
741 | } |
||
742 | |||
743 | 3 | private static function completeBlogPost(array $blogPost): array |
|
744 | { |
||
745 | 3 | if (isset($blogPost['meta_id'])) { |
|
746 | 2 | $blogPost['meta'] = FrontendModel::get('fork.repository.meta')->find($blogPost['meta_id']); |
|
747 | } |
||
748 | |||
749 | 3 | if (isset($blogPost['meta_data'])) { |
|
750 | $blogPost['meta_data'] = @unserialize($blogPost['meta_data'], ['allowed_classes' => false]); |
||
751 | } |
||
752 | |||
753 | // image? |
||
754 | 3 | if (isset($blogPost['image'])) { |
|
755 | $folders = FrontendModel::get(Thumbnails::class)->getFolders(FRONTEND_FILES_PATH . '/Blog/images', true); |
||
756 | |||
757 | foreach ($folders as $folder) { |
||
758 | $blogPost['image_' . $folder['dirname']] = $folder['url'] . '/' . $folder['dirname'] . '/' . $blogPost['image']; |
||
759 | } |
||
760 | } |
||
761 | |||
762 | 3 | if (isset($blogPost['id'])) { |
|
763 | 2 | $blogPost['tags'] = FrontendTagsModel::getForItem( |
|
764 | 2 | 'Blog', |
|
765 | 2 | $blogPost['id'], |
|
766 | 2 | isset($blogPost['language']) ? Locale::fromString($blogPost['language']) : null |
|
767 | ); |
||
768 | } |
||
769 | |||
770 | 3 | return $blogPost; |
|
771 | } |
||
772 | |||
773 | public static function insertComment(array $comment): int |
||
774 | { |
||
775 | // get database |
||
776 | $database = FrontendModel::getContainer()->get('database'); |
||
777 | |||
778 | // insert comment |
||
779 | $comment['id'] = (int) $database->insert('blog_comments', $comment); |
||
780 | |||
781 | // recalculate if published |
||
782 | if ($comment['status'] == 'published') { |
||
783 | // num comments |
||
784 | $numComments = (int) FrontendModel::getContainer()->get('database')->getVar( |
||
785 | 'SELECT COUNT(i.id) AS comment_count |
||
786 | FROM blog_comments AS i |
||
787 | INNER JOIN blog_posts AS p ON i.post_id = p.id AND i.language = p.language |
||
788 | WHERE i.status = ? AND i.post_id = ? AND i.language = ? AND p.status = ? |
||
789 | GROUP BY i.post_id', |
||
790 | ['published', $comment['post_id'], LANGUAGE, 'active'] |
||
791 | ); |
||
792 | |||
793 | // update num comments |
||
794 | $database->update('blog_posts', ['num_comments' => $numComments], 'id = ?', $comment['post_id']); |
||
795 | } |
||
796 | |||
797 | return $comment['id']; |
||
798 | } |
||
799 | |||
800 | /** |
||
801 | * Get moderation status for an author |
||
802 | * |
||
803 | * @param string $author The name for the author. |
||
804 | * @param string $email The email address for the author. |
||
805 | * |
||
806 | * @return bool |
||
807 | */ |
||
808 | public static function isModerated(string $author, string $email): bool |
||
809 | { |
||
810 | return (bool) FrontendModel::getContainer()->get('database')->getVar( |
||
811 | 'SELECT 1 |
||
812 | FROM blog_comments AS c |
||
813 | WHERE c.status = ? AND c.author = ? AND c.email = ? |
||
814 | LIMIT 1', |
||
815 | ['published', $author, $email] |
||
816 | ); |
||
817 | } |
||
818 | |||
819 | /** |
||
820 | * Notify the admin |
||
821 | * |
||
822 | * @param array $comment The comment that was submitted. |
||
823 | */ |
||
824 | public static function notifyAdmin(array $comment): void |
||
825 | { |
||
826 | // don't notify admin in case of spam |
||
827 | if ($comment['status'] == 'spam') { |
||
828 | return; |
||
829 | } |
||
830 | |||
831 | // get settings |
||
832 | $notifyByMailOnComment = FrontendModel::get('fork.settings')->get( |
||
833 | 'Blog', |
||
834 | 'notify_by_email_on_new_comment', |
||
835 | false |
||
836 | ); |
||
837 | $notifyByMailOnCommentToModerate = FrontendModel::get('fork.settings')->get( |
||
838 | 'Blog', |
||
839 | 'notify_by_email_on_new_comment_to_moderate', |
||
840 | false |
||
841 | ); |
||
842 | |||
843 | // create URLs |
||
844 | $url = SITE_URL . FrontendNavigation::getUrlForBlock('Blog', 'Detail') . '/' . |
||
845 | $comment['post_url'] . '#comment-' . $comment['id']; |
||
846 | $backendUrl = SITE_URL . FrontendNavigation::getBackendUrlForBlock('comments', 'Blog') . '#tabModeration'; |
||
847 | |||
848 | // notify on all comments |
||
849 | if ($notifyByMailOnComment) { |
||
850 | $variables = []; |
||
851 | |||
852 | // comment to moderate |
||
853 | if ($comment['status'] == 'moderation') { |
||
854 | $variables['message'] = vsprintf( |
||
855 | FL::msg('BlogEmailNotificationsNewCommentToModerate'), |
||
856 | [$comment['author'], $url, $comment['post_title'], $backendUrl] |
||
857 | ); |
||
858 | } elseif ($comment['status'] == 'published') { |
||
859 | // comment was published |
||
860 | $variables['message'] = vsprintf( |
||
861 | FL::msg('BlogEmailNotificationsNewComment'), |
||
862 | [$comment['author'], $url, $comment['post_title']] |
||
863 | ); |
||
864 | } |
||
865 | |||
866 | $to = FrontendModel::get('fork.settings')->get('Core', 'mailer_to'); |
||
867 | $from = FrontendModel::get('fork.settings')->get('Core', 'mailer_from'); |
||
868 | $replyTo = FrontendModel::get('fork.settings')->get('Core', 'mailer_reply_to'); |
||
869 | $message = Message::newInstance(FL::msg('NotificationSubject')) |
||
870 | ->setFrom([$from['email'] => $from['name']]) |
||
871 | ->setTo([$to['email'] => $to['name']]) |
||
872 | ->setReplyTo([$replyTo['email'] => $replyTo['name']]) |
||
873 | ->parseHtml( |
||
874 | '/Core/Layout/Templates/Mails/Notification.html.twig', |
||
875 | $variables, |
||
876 | true |
||
877 | ) |
||
878 | ; |
||
879 | FrontendModel::get('mailer')->send($message); |
||
880 | } elseif ($notifyByMailOnCommentToModerate && $comment['status'] == 'moderation') { |
||
881 | // only notify on new comments to moderate and if the comment is one to moderate |
||
882 | // set variables |
||
883 | $variables = []; |
||
884 | $variables['message'] = vsprintf( |
||
885 | FL::msg('BlogEmailNotificationsNewCommentToModerate'), |
||
886 | [$comment['author'], $url, $comment['post_title'], $backendUrl] |
||
887 | ); |
||
888 | |||
889 | $to = FrontendModel::get('fork.settings')->get('Core', 'mailer_to'); |
||
890 | $from = FrontendModel::get('fork.settings')->get('Core', 'mailer_from'); |
||
891 | $replyTo = FrontendModel::get('fork.settings')->get('Core', 'mailer_reply_to'); |
||
892 | $message = Message::newInstance(FL::msg('NotificationSubject')) |
||
893 | ->setFrom([$from['email'] => $from['name']]) |
||
894 | ->setTo([$to['email'] => $to['name']]) |
||
895 | ->setReplyTo([$replyTo['email'] => $replyTo['name']]) |
||
896 | ->parseHtml( |
||
897 | '/Core/Layout/Templates/Mails/Notification.html.twig', |
||
898 | $variables, |
||
899 | true |
||
900 | ) |
||
901 | ; |
||
902 | FrontendModel::get('mailer')->send($message); |
||
903 | } |
||
904 | } |
||
905 | |||
906 | /** |
||
907 | * Parse the search results for this module |
||
908 | * |
||
909 | * Note: a module's search function should always: |
||
910 | * - accept an array of entry id's |
||
911 | * - return only the entries that are allowed to be displayed, with their array's index being the entry's id |
||
912 | * |
||
913 | * |
||
914 | * @param array $ids The ids of the found results. |
||
915 | * |
||
916 | * @return array |
||
917 | */ |
||
918 | 1 | public static function search(array $ids): array |
|
919 | { |
||
920 | 1 | $items = (array) FrontendModel::getContainer()->get('database')->getRecords( |
|
921 | 'SELECT i.id, i.title, i.introduction, i.text, m.url |
||
922 | FROM blog_posts AS i |
||
923 | INNER JOIN meta AS m ON i.meta_id = m.id |
||
924 | WHERE i.status = ? AND i.hidden = ? AND i.language = ? AND i.publish_on <= ? AND i.id IN (' . |
||
925 | 1 | implode(',', $ids) . ')', |
|
926 | 1 | ['active', false, LANGUAGE, date('Y-m-d H:i')], |
|
927 | 1 | 'id' |
|
928 | ); |
||
929 | |||
930 | // prepare items for search |
||
931 | 1 | $detailUrl = FrontendNavigation::getUrlForBlock('Blog', 'Detail'); |
|
932 | 1 | foreach ($items as &$item) { |
|
933 | 1 | $item['full_url'] = $detailUrl . '/' . $item['url']; |
|
934 | } |
||
935 | |||
936 | // return |
||
937 | 1 | return $items; |
|
938 | } |
||
939 | } |
||
940 |