1<?php
2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
3//
4// All Rights Reserved. See copyright.txt for details and a complete list of authors.
5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
6// $Id$
7
8// This script may only be included - so its better to die if called directly.
9if (strpos($_SERVER["SCRIPT_NAME"], basename(__FILE__)) !== false) {
10	header("location: index.php");
11	exit;
12}
13
14/**
15 * Class that handles all blog operations
16 *
17 * @uses TikiDb_Bridge
18 * @package Tiki
19 * @subpackage Blogs
20 * @version
21 * @license LGPL. See licence.txt for more details
22 */
23class BlogLib extends TikiDb_Bridge
24{
25	/**
26	 * List all blogs
27	 *
28	 * @param int $offset
29	 * @param int $maxRecords
30	 * @param string @sort_mode
31	 * @param string $find
32	 * @param string $ref
33	 * @param string $with
34	 *
35	 * @return array
36	 */
37	function list_blogs($offset = 0, $maxRecords = -1, $sort_mode = 'created_desc', $find = '', $ref = '', $with = '')
38	{
39		$tikilib = TikiLib::lib('tiki');
40		$categlib = TikiLib::lib('categ');
41		$bindvars = [];
42		$join = '';
43		$where = '';
44
45		if ($jail = $categlib->get_jail()) {
46			$categlib->getSqlJoin($jail, 'blog', '`tiki_blogs`.`blogId`', $join, $where, $bindvars);
47		}
48
49		if ($find) {
50			$findesc = '%' . $find . '%';
51			$where .= ' and (`tiki_blogs`.`title` like ? or `tiki_blogs`.`description` like ?) ';
52			$bindvars = array_merge($bindvars, [$findesc, $findesc]);
53		}
54		if (isset($with['showlastpost'])) {
55			$query = "SELECT tb.*, tbp.`postId`, tbp.`created` as postCreated, tbp.`user` as postUser, tbp.`title` as postTitle, tbp.`data` as postData FROM `tiki_blogs` tb, `tiki_blog_posts` tbp $join where tb.`blogId` = tbp.`blogId` and tbp.`created` = (select max(`created`) from `tiki_blog_posts` tbp2 where tbp2.`blogId`=tb.`blogId` order by `created` desc) $where order by tb." . $this->convertSortMode($sort_mode);
56		} else {
57			$query = "select * from `tiki_blogs` $join WHERE 1=1 $where order by `tiki_blogs`." . $this->convertSortMode($sort_mode);
58		}
59		$result = $this->fetchAll($query, $bindvars);
60
61		$ret = [];
62		$cant = 0;
63		$nb = 0;
64		$i = 0;
65
66		switch($ref) {
67			case 'post':
68				$perm = ['read_blog', 'blog_post_view_ref'];
69				break;
70			case 'blog':
71				$perm = ['read_blog', 'blog_view_ref'];
72				break;
73			default:
74				$perm = 'read_blog';
75		}
76
77		$result = Perms::filter(['type' => 'blog'], 'object', $result, ['object' => 'blogId'], $perm);
78
79		foreach ($result as $res) {
80			++$cant;
81			if ($maxRecords == - 1 || ($i >= $offset && $nb < $maxRecords)) {
82				$ret[] = $res;
83				++$nb;
84			}
85			++$i;
86		}
87
88		$retval = [];
89		$retval["data"] = $ret;
90		$retval["cant"] = $cant;
91		return $retval;
92	}
93
94
95	/**
96	 * Return all blog information
97	 *
98	 * @param int $blogId
99	 * @return array
100	 */
101	function get_blog($blogId)
102	{
103		global $prefs, $user;
104		$tikilib = TikiLib::lib('tiki');
105		$categlib = TikiLib::lib('categ');
106		$bindvars = [];
107
108		if ($jail = $categlib->get_jail()) {
109			$categlib->getSqlJoin($jail, 'blog', '`tiki_blogs`.`blogId`', $join, $where, $bindvars);
110		} else {
111			$join = '';
112			$where = '';
113		}
114		array_push($bindvars, $blogId);
115		if (! empty($where)) {
116			$where = '1=1 ' . $where . ' AND ';
117		}
118		$query = "SELECT * FROM `tiki_blogs` $join WHERE $where `blogId`=?";
119		$result = $this->query($query, $bindvars);
120		if ($result->numRows()) {
121			$res = $result->fetchRow();
122		} else {
123			return false;
124		}
125
126		if ($user != $res['user']) {
127			TikiLib::events()->trigger(
128				'tiki.blog.view',
129				[
130					'type' => 'blog',
131					'object' => $blogId,
132					'user' => $user,
133					'author' => $res['user'],
134				]
135			);
136		}
137
138		return $res;
139	}
140
141	/**
142	 * Return a blog by its title
143	 *
144	 * @param string $blogTitle
145	 * @return array or false if no blog is found
146	 */
147	function get_blog_by_title($blogTitle)
148	{
149		 // Avoiding select by name so as to avoid SQL injection problems.
150		$query = "select `title`, `blogId` from `tiki_blogs`";
151		$result = $this->fetchAll($query);
152		if (! empty($result)) {
153			foreach ($result as $res) {
154				if (TikiLib::strtolower($res['title']) == TikiLib::strtolower($blogTitle)) {
155					return $this->get_blog($res['blogId']);
156				}
157			}
158		}
159
160		return false;
161	}
162
163	/**
164	 * Returns an array of blogs that belong to the user with the given name,
165	 * or which are public, if $include_public is set to true.
166	 * A blog is represented by an array like a tiki_blogs record.
167	 *
168	 * @param string $user
169	 * @param bool $include_public whether or include public blogs (that belongs to other users)
170	 * @return array
171	 */
172	function list_user_blogs($user, $include_public = false)
173	{
174		$tikilib = TikiLib::lib('tiki');
175
176		$query = "select * from `tiki_blogs` where `user`=? ";
177		$bindvars = [$user];
178		if ($include_public) {
179			$query .= " or `public`=?";
180			$bindvars[] = 'y';
181		}
182		$query .= "order by `title` asc";
183		$result = $this->fetchAll($query, $bindvars);
184		$ret = [];
185
186		//FIXME Perm::filter ?
187		foreach ($result as $res) {
188			if ($tikilib->user_has_perm_on_object($user, $res['blogId'], 'blog', 'tiki_p_read_blog')) {
189				$ret[] = $res;
190			}
191		}
192		return $ret;
193	}
194
195	/**
196	 * Return a list of blogs that the user has permission to post
197	 *
198	 * @return array
199	 */
200	function list_blogs_user_can_post()
201	{
202		global $tiki_p_blog_admin, $user;
203		$tikilib = TikiLib::lib('tiki');
204
205		$query = "select * from `tiki_blogs` order by `title` asc";
206		$result = $this->fetchAll($query);
207		$ret = [];
208
209		//FIXME Perm:filter ?
210		foreach ($result as $res) {
211			if ((! empty($user) and $user == $res['user']) || $tiki_p_blog_admin == 'y' || $tikilib->user_has_perm_on_object($user, $res['blogId'], 'blog', 'tiki_p_blog_admin') || ($res['public'] == 'y' && $tikilib->user_has_perm_on_object($user, $res['blogId'], 'blog', 'tiki_p_blog_post'))) {
212				$ret[] = $res;
213			}
214		}
215		return $ret;
216	}
217
218	/**
219	 * List all posts
220	 *
221	 * @param int $offset
222	 * @param int $maxRecords
223	 * @param string $sort_mode
224	 * @param string $find
225	 * @param int $filterByBlogId
226	 * @param string $author
227	 * @param string $ref
228	 * @param int $date_min
229	 * @param int $data_max
230	 * @return array
231	 */
232	function list_posts($offset = 0, $maxRecords = -1, $sort_mode = 'created_desc', $find = '', $filterByBlogId = -1, $author = '', $ref = '', $date_min = 0, $date_max = 0)
233	{
234		$tikilib = TikiLib::lib('tiki');
235		$filterByBlogIds = explode(':', $filterByBlogId);
236
237		$authorized_blogs = $this->list_blogs(0, -1, 'created_desc', '', $ref);
238		$permit_blogs = [];
239		for ($i = 0; $i < $authorized_blogs["cant"]; $i++) {
240			$permit_blogs[] = $authorized_blogs["data"][$i]['blogId'];
241		}
242
243		if (count($filterByBlogIds) == 1 && $filterByBlogId >= 0) {	// There is one single blog Id
244			// get posts for a given blogId:
245			$mid = " where ( `blogId` = ? ) ";
246			$bindvars = [$filterByBlogId];
247		} elseif (count($filterByBlogIds) > 1) {	// There is more than one blog Id
248			$multimid = [];
249			foreach ($filterByBlogIds as $blogId) {
250				if ($blogId > 0) {
251					$multimid[] = ' `blogId` = ? ';
252					$bindvars[] = (int)$blogId;
253				}
254			}
255			if (count($multimid) > 1) {
256				$mid = ' WHERE ( ' . implode(' or ', $multimid) . ' ) ';
257			} elseif (count($multimid) == 1) {
258				$mid = ' where ( ' . $multimid[0] . ' ) ';
259			}
260		} else {
261			// get posts from all blogs
262			$mid = '';
263			$bindvars = [];
264		}
265
266		if ($find) {
267			$findesc = '%' . $find . '%';
268			if ($mid == "") {
269				$mid = " where ";
270			} else {
271				$mid .= " and ";
272			}
273			$mid .= " ( `data` like ? ) ";
274			$bindvars[] = $findesc;
275		}
276		if ($date_min !== 0 || $date_max !== 0) {
277			if ($date_max <= 0) {
278				// show articles published today
279				$date_max = $tikilib->now;
280			}
281			if ($mid == '') {
282				$mid = ' where ';
283			} else {
284				$mid .= ' and ';
285			}
286			$mid .= '(`created`>=? and `created`<=?)';
287			$bindvars[] = $date_min;
288			$bindvars[] = $date_max;
289		}
290		if (! empty($author)) {
291			if ($mid == '') {
292				$mid = ' where ';
293			} else {
294				$mid .= ' and ';
295			}
296			$mid .= 'user =?';
297			$bindvars[] = $author;
298		}
299
300		$query = "select * from `tiki_blog_posts` $mid order by " . $this->convertSortMode($sort_mode);
301		$query_cant = "select count(*) from `tiki_blog_posts` $mid";
302		$result = $this->fetchAll($query, $bindvars, $maxRecords, $offset);
303		$cant = $this->getOne($query_cant, $bindvars);
304		$ret = [];
305
306		foreach ($result as $res) {
307			$blogId = $res["blogId"];
308
309			if (! in_array($blogId, $permit_blogs)) {
310				continue;
311			}
312			$query = "select `title` from `tiki_blogs` where `blogId`=?";
313			$cant_com = $this->getOne(
314				"select count(*) from `tiki_comments` where `object`=? and `objectType` = ?",
315				[(string) $res["postId"],'blog']
316			);
317			$res["comments"] = $cant_com;
318			$res["blogTitle"] = $this->getOne($query, [(int)$blogId]);
319			$res["size"] = strlen($res["data"]);
320			$ret[] = $res;
321		}
322		$retval = [];
323		$retval["data"] = $ret;
324		$retval["cant"] = $cant;
325		return $retval;
326	}
327
328	/**
329	 * get_number_of_pages Returns the number of pages
330	 *
331	 * @param string $data
332	 * @access public
333	 * @return int number of pages
334	 */
335	function get_number_of_pages($data)
336	{
337		$parts = explode("...page...", $data);
338		return count($parts);
339	}
340
341	/**
342	 * get_page Returns a spcific page of a post
343	 *
344	 * @param string $data
345	 * @param int $i
346	 * @access public
347	 * @return string the page $i of the post
348	 */
349	function get_page($data, $i)
350	{
351		$parts = explode("...page...", $data);
352
353		$ret = $parts[$i - 1];
354		if (substr($parts[$i - 1], 1, 5) == "<br/>") {
355			$ret = substr($parts[$i - 1], 6);
356		}
357		if (substr($parts[$i - 1], 1, 6) == "<br />") {
358			$ret = substr($parts[$i - 1], 7);
359		}
360
361		return $ret;
362	}
363
364	/**
365	 * add_blog_hit Add a hit for the blog $blogId
366	 *
367	 * @param int $blogId
368	 * @access public
369	 * @return boolean unconditionnal true
370	 */
371	function add_blog_hit($blogId)
372	{
373		global $prefs, $user;
374		if (StatsLib::is_stats_hit()) {
375			$query = "update `tiki_blogs` set `hits` = `hits`+1 where `blogId`=?";
376			$result = $this->query($query, [(int) $blogId]);
377		}
378		return true;
379	}
380
381	/**
382	 * add_blog_post_hit Add a hit for the blog post $postId
383	 *
384	 * @param int $postId
385	 * @access public
386	 * @return boolean unconditionnal true
387	 */
388	function add_blog_post_hit($postId)
389	{
390		global $prefs, $user;
391		if (StatsLib::is_stats_hit()) {
392			$query = "update `tiki_blog_posts` set `hits` = `hits`+1 where `postId`=?";
393			$result = $this->query($query, [(int) $postId]);
394		}
395		return true;
396	}
397
398
399	/**
400	 * get_post_image Returns the image $imgId
401	 *
402	 * @param mixed $imgId
403	 * @access public
404	 * @return array all fields that are associated with an image in tiki_blog_post_images database table
405	 */
406	function get_post_image($imgId)
407	{
408		$query = "select * from `tiki_blog_posts_images` where `imgId`=?";
409		$result = $this->query($query, [$imgId]);
410		$res = $result->fetchRow();
411		return $res;
412	}
413
414	/**
415	 * get_post_images Returns all the images joined to a post
416	 *
417	 * @param int $postId
418	 * @access public
419	 * @return array with the permalink and the absolute link for each image
420	 */
421	function get_post_images($postId)
422	{
423		$tikilib = TikiLib::lib('tiki');
424		$query = "select `postId`,`filename`,`filesize`,`imgId` from `tiki_blog_posts_images` where `postId`=?";
425
426		$result = $this->query($query, [(int) $postId]);
427		$ret = [];
428
429		while ($res = $result->fetchRow()) {
430			$imgId = $res['imgId'];
431			$res['link'] = "<img src='tiki-view_blog_post_image.php?imgId=$imgId' alt='image' />";
432			$parts = parse_url($_SERVER['REQUEST_URI']);
433			$path = str_replace('tiki-blog_post.php', 'tiki-view_blog_post_image.php', $parts['path']);
434			$res['absolute'] = $tikilib->httpPrefix() . $path . "?imgId=$imgId";
435			$ret[] = $res;
436		}
437
438		return $ret;
439	}
440
441	/**
442	 * remove_post_image Removes an image
443	 *
444	 * @param int $imgId
445	 * @access public
446	 * @return void
447	 */
448	function remove_post_image($imgId)
449	{
450		$query = "delete from `tiki_blog_posts_images` where `imgId`=?";
451
452		$this->query($query, [$imgId]);
453	}
454
455	/**
456	 * replace_blog Change the attributes of a blog
457	 *
458	 * @param string $title
459	 * @param string $description
460	 * @param string $user
461	 * @param $public
462	 * @param int $maxPosts
463	 * @param int $blogId
464	 * @param string $heading
465	 * @param $use_title
466	 * @param $use_title_in_post
467	 * @param $use_description
468	 * @param $use_breadcrumbs
469	 * @param $use_author
470	 * @param $add_date
471	 * @param $use_find
472	 * @param $allow_comments
473	 * @param $show_avatar
474	 * @param $alwaysOwner
475	 * @param string $post_heading
476	 * @param $show_related
477	 * @param int $related_max control the maximum number of related posts displayed per post
478	 * @param int $use_excerpt use a post excerpt instead of the main content when listing posts of a blog
479	 * @param int $created if 0 use $tikilib->now
480	 * @param int $lastModif if 0 use $tikilib->now
481	 * @return int blogId
482	 * @throws Exception
483	 * @access public
484	 */
485	function replace_blog(
486		$title,
487		$description,
488		$user,
489		$public,
490		$maxPosts,
491		$blogId,
492		$heading,
493		$use_title,
494		$use_title_in_post,
495		$use_description,
496		$use_breadcrumbs,
497		$use_author,
498		$add_date,
499		$use_find,
500		$allow_comments,
501		$show_avatar,
502		$alwaysOwner,
503		$post_heading,
504		$show_related,
505		$related_max,
506		$use_excerpt,
507		$created = 0,
508		$lastModif = 0
509	) {
510
511		//TODO: all the display parameters can be one single array parameter
512		global $prefs;
513		$tikilib = TikiLib::lib('tiki');
514
515		if ($lastModif == 0) {
516			$lastModif = $tikilib->now;
517		}
518
519		if ($blogId) {
520			$query = "update `tiki_blogs` set `title`=? ,`description`=?,`user`=?,`public`=?,`lastModif`=?,`maxPosts`=?,`heading`=?,`use_title`=?,`use_title_in_post`=?,`use_description`=?,`use_breadcrumbs`=?,`use_author`=?,`add_date`=?,`use_find`=?,`allow_comments`=?,`show_avatar`=?,`always_owner`=?, `post_heading`=?, `show_related`=?, `related_max`=?, `use_excerpt`=? where `blogId`=?";
521
522			$result = $this->query($query, [$title, $description, $user, $public, $lastModif, $maxPosts, $heading, $use_title, $use_title_in_post, $use_description, $use_breadcrumbs, $use_author, $add_date, $use_find, $allow_comments, $show_avatar, $alwaysOwner, $post_heading, $show_related, $related_max, $use_excerpt, $blogId]);
523			$tikilib->object_post_save(['type' => 'blog', 'object' => $blogId], ['content' => $heading]);
524			$query2 = "UPDATE `tiki_objects` SET `name`=? ,`description`=? WHERE `itemId`=? AND `type`='blog'";
525			$this->query($query2, [$title, $description, $blogId]);
526		} else {
527			if ($created == 0) {
528				$created = $tikilib->now;
529			}
530
531			$query = "insert into `tiki_blogs`(`created`,`lastModif`,`title`,`description`,`user`,`public`,`posts`,`maxPosts`,`hits`,`heading`,`use_title`,`use_title_in_post`,`use_description`,`use_breadcrumbs`,`use_author`,`add_date`,`use_find`,`allow_comments`,`show_avatar`,`always_owner`,`post_heading`, `show_related`, `related_max`, `use_excerpt`) values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
532
533			$result = $this->query($query, [$created, $lastModif, $title, $description, $user, $public, 0, (int) $maxPosts, 0, $heading, $use_title, $use_title_in_post, $use_description, $use_breadcrumbs, $use_author, $add_date, $use_find, $allow_comments, $show_avatar, $alwaysOwner, $post_heading, $show_related, $related_max, $use_excerpt]);
534			$query2 = "select max(`blogId`) from `tiki_blogs` where `lastModif`=?";
535			$blogId = $this->getOne($query2, [$lastModif]);
536
537			TikiLib::events()->trigger(
538				'tiki.blog.create',
539				[
540					'type' => 'blog',
541					'object' => $blogId,
542					'user' => $user,
543				]
544			);
545
546			$tikilib->object_post_save(['type' => 'blog', 'object' => $blogId, 'description' => $description, 'name' => $title, 'href' => "tiki-view_blog.php?blogId=$blogId"], [ 'content' => $heading ]);
547		}
548
549		require_once(__DIR__ . '/../search/refresh-functions.php');
550		refresh_index('blogs', $blogId);
551
552		return $blogId;
553	}
554
555	/**
556	 * list_blog_posts Returns all the posts for the blog $blogId
557	 * $blogId can express more than one blog like "1:11"
558	 * but then own draft posts (private posts) are not shown (FIXME)
559	 *
560	 * @param int $blogId
561	 * @param bool $allowDrafts
562	 * @param int $offset
563	 * @param int $maxRecords
564	 * @param string $sort_mode
565	 * @param string $find
566	 * @param string $date_min
567	 * @param string $date_max
568	 * @param string $approved
569	 * @access public
570	 * @return array posts
571	 */
572	function list_blog_posts(
573		$blogId = 0,
574		$allowDrafts = false,
575		$offset = 0,
576		$maxRecords = -1,
577		$sort_mode = 'created_desc',
578		$find = '',
579		$date_min = '',
580		$date_max = '',
581		$approved = 'y'
582	) {
583
584		global $tiki_p_admin, $tiki_p_blog_admin, $tiki_p_blog_post, $user, $prefs;
585
586		$parserlib = TikiLib::lib('parser');
587		$categlib = TikiLib::lib('categ');
588		$tikilib = TikiLib::lib('tiki');
589
590		$mid = [];
591		$bindvars = [];
592		$blogIds = explode(':', $blogId);
593
594		$ownsblog = 'n';
595		if (count($blogIds) == 1 && $blogIds[0] > 0) {	// There is one single blog Id
596			$mid[] = "tbp.`blogId`=?";
597			$bindvars[] = (int)$blogId;
598
599			$blog_data = $this->get_blog($blogId);
600			if ($user && $user == $blog_data["user"]) {
601				$ownsblog = 'y';
602			}
603		} elseif (count($blogIds) > 1) {	// There is more than one blog Id
604			$multimid = [];
605			foreach ($blogIds as $objId) {
606				if ($objId > 0) {
607					$multimid[] = ' tbp.`blogId`=? ';
608					$bindvars[] = (int)$objId;
609					// Note: when there is more than one blog Id, own draft posts (private posts) are not shown (FIXME)
610				}
611			}
612			if (count($multimid) > 1) {
613				$mid[] = ' ( ' . implode(' or ', $multimid) . ' ) ';
614			} elseif (count($multimid) == 1) {
615				$mid[] = $multimid[0];
616			}
617		}
618		$mid[] = "tbp.blogId = tb.blogId";
619
620		if (! $allowDrafts) {
621			$mid[] = "`priv`!='y'";
622		} else {
623			/* Private posts can be accessed on the following conditions:
624				user has tiki_p_admin or tiki_p_blog_admin or has written the post
625				If blog is configured with 'Allow other user to post in this blog', then also if user has tiki_p_blog_post or is owner of this blog
626
627			Basically, if the user can post to the post's blog. */
628			if (! (
629					$tiki_p_blog_admin == 'y'
630					|| (isset($blog_data["public"]) && $blog_data["public"] == 'y' && ($tiki_p_blog_post == 'y' || $ownsblog == 'y'))
631				)
632			) {
633				if (isset($user)) { // Is this needed? How can a user have a post in a blog without owning the blog nor tiki_p_blog_post? Chealer9 2017-12-07
634					$mid[] = "(tbp.`priv`!='y' or tbp.`user`=?)";
635					$bindvars[] = "$user";
636				} else {
637					$mid[] = "tbp.`priv`!='y'";
638				}
639			}
640		}
641
642		if ($find) {
643			$findesc = '%' . $find . '%';
644			$mid[] = "(tbp.`data` like ? or tbp.`title` like ?)";
645			$bindvars[] = $findesc;
646			$bindvars[] = $findesc;
647		}
648
649		if ($date_min) {
650			$mid[] = "tbp.`created`>=?";
651			$bindvars[] = (int)$date_min;
652		}
653		if ($date_max) {
654			$mid[] = "tbp.`created`<=?";
655			$bindvars[] = (int)$date_max;
656		}
657
658		$mid = empty($mid) ? '' : 'where ' . implode(' and ', $mid);
659
660		$join = '';
661		if ($jail = $categlib->get_jail()) {
662			$categlib->getSqlJoin($jail, 'blog post', '`tbp`.`postId`', $join, $mid, $bindvars);
663		}
664
665		$query = "select tbp.*,tb.title as blogTitle from `tiki_blog_posts` as tbp, `tiki_blogs` as tb $join $mid order by " . $this->convertSortMode($sort_mode);
666		$query_cant = "select count(*) from `tiki_blog_posts` as tbp, `tiki_blogs` as tb $join $mid";
667		$result = $this->query($query, $bindvars, $maxRecords, $offset);
668		$cant = $this->getOne($query_cant, $bindvars);
669		$ret = [];
670
671		while ($res = $result->fetchRow()) {
672			$res["comments"] = TikiLib::lib('comments')->count_comments('blog post:' . $res['postId']);
673			$res['pages'] = $this->get_number_of_pages($res['data']);
674			$res['avatar'] = $tikilib->get_user_avatar($res['user']);
675
676			$is_html = $res['wysiwyg'] === 'y' && $prefs['wysiwyg_htmltowiki'] !== 'y';
677
678			$res['parsed_excerpt'] = $parserlib->parse_data($res['excerpt'], ['is_html' => $is_html]);
679			$res['parsed_data'] = $parserlib->parse_data($res['data'], ['is_html' => $is_html]);
680
681			if ($prefs['feature_freetags'] == 'y') { // And get the Tags for the posts
682				$res['freetags'] = TikiLib::lib('freetag')->get_tags_on_object($res['postId'], 'blog post');
683			} else {
684				$res['freetags'] = [];
685			}
686
687			$ret[] = $res;
688		}
689
690		$ret = Perms::filter([ 'type' => 'blog post' ], 'object', $ret, [ 'object' => 'postId' ], ['read_blog', 'blog_post_view_ref']);
691
692		$retval = [];
693		$retval['data'] = $ret;
694		$retval['cant'] = $cant;
695
696		return $retval;
697	}
698
699	/**
700	 * list_blog_post_comments List all the comments in posts for all the blogs
701	 *
702	 * @param string $approved
703	 * @param int $maxRecords
704	 * @access public
705	 * @return void
706	 */
707	function list_blog_post_comments($approved = 'y', $maxRecords = -1)
708	{
709		global $user, $tiki_p_admin, $tiki_p_blog_admin, $tiki_p_blog_post;
710		$userlib = TikiLib::lib('user');
711		$tikilib = TikiLib::lib('tiki');
712
713		// TODO: use commentslib instead of querying database directly
714		// Blog Recent Comments
715		$query = "SELECT b.`title`, b.`postId`, b.`priv`, blog.`user`, blog.`public`, c.`threadId`, c.`title` as commentTitle, c.`website`, `commentDate`, `userName` FROM `tiki_comments` c, `tiki_blog_posts` b, `tiki_blogs` blog WHERE `objectType`='blog post' AND b.`postId`=c.`object` AND blog.`blogId`=b.`blogId`";
716
717		$bindvars = [];
718		$globalperms = Perms::get();
719		if (! $globalperms->admin_comment) {
720			$query .= ' AND `approved`=?';
721			$bindvars[] = $approved;
722		} else {
723			$approved = null;
724		}
725
726		$query .= " ORDER BY `commentDate` desc";
727		$result = $this->fetchAll($query, $bindvars, $maxRecords);
728		$result = Perms::filter(['type' => 'blog post'], 'object', $result, ['object' => 'postId'], ['read_blog', 'blog_post_view_ref']);
729
730		$ret = [];
731		foreach ($result as $res) {
732			// Private posts can be accessed on the following conditions:
733			// user has tiki_p_admin or tiki_p_blog_admin or has written the post
734			// If blog is configured with 'Allow other user to post in this blog', then also if user has tiki_p_blog_post or is owner of this blog
735			if (($res['priv'] != 'y')
736					or ($tiki_p_admin == 'y' )
737					or ($tiki_p_blog_admin == 'y')
738					or ( ($res["public"] == 'y') && ($user && $user == $res["user"]) )
739					or ( ($res["public"] == 'y') && ($tiki_p_blog_post == 'y') ) ) {
740				$ret[] = $res;
741			}
742		}
743
744		// just to distinct between user and anonymous (should be done in commentslib and not here)
745		foreach ($ret as $key => $comment) {
746			if (! $userlib->user_exists($comment['userName'])) {
747				$ret[$key]['anonymous_name'] = $comment['userName'];
748			}
749		}
750
751		return ['data' => $ret, 'cant' => count($ret)];
752	}
753
754	/**
755	 * list_all_blog_posts Returns all the posts filtered by $date and $find
756	 *
757	 * @param int $offset
758	 * @param int $maxRecords
759	 * @param string $sort_mode
760	 * @param string $find
761	 * @param string $date
762	 * @access public
763	 * @return void
764	 */
765	function list_all_blog_posts($offset = 0, $maxRecords = -1, $sort_mode = 'created_desc', $find = '', $date = '')
766	{
767
768		if ($find) {
769			$findesc = '%' . $find . '%';
770
771			$mid = " where (`data` like ?) ";
772			$bindvars = [$findesc];
773		} else {
774			$mid = "";
775			$bindvars = [];
776		}
777
778		if ($date) {
779			$bindvars[] = $date;
780			if ($mid) {
781				$mid .= " and `created`<=? ";
782			} else {
783				$mid .= " where `created`<=? ";
784			}
785		}
786
787		$query = "select * from `tiki_blog_posts` $mid order by " . $this->convertSortMode($sort_mode);
788		$query_cant = "select count(*) from `tiki_blog_posts` $mid";
789		$result = $this->fetchAll($query, $bindvars, $maxRecords, $offset);
790		$cant = $this->getOne($query_cant, $bindvars);
791		$ret = [];
792
793		$result = Perms::filter(['type' => 'blog post'], 'object', $result, ['object' => 'postId'], ['read_blog', 'blog_post_view_ref']);
794
795		foreach ($result as $res) {
796			$query2 = "select `title` from `tiki_blogs` where `blogId`=?";
797			$title = $this->getOne($query2, [$res["blogId"]]);
798			$res["blogtitle"] = $title;
799			$ret[] = $res;
800		}
801
802		$retval = [];
803		$retval["data"] = $ret;
804		$retval["cant"] = $cant;
805		return $retval;
806	}
807
808	/**
809	 * blog_post Stores a blog post
810	 *
811	 * @param int $blogId
812	 * @param string $data
813	 * @param string $excerpt
814	 * @param string $user
815	 * @param string $title
816	 * @param string $contributions
817	 * @param string $priv
818	 * @param bool $is_wysiwyg
819	 * @access public
820	 * @return int postId
821	 */
822	function blog_post($blogId, $data, $excerpt, $user, $title = '', $contributions = '', $priv = 'n', $created = 0, $is_wysiwyg = false)
823	{
824		// update tiki_blogs and call activity functions
825		global $prefs;
826		$tikilib = TikiLib::lib('tiki');
827		$smarty = TikiLib::lib('smarty');
828
829		if ($is_wysiwyg) {
830			$data = TikiFilter::get('purifier')->filter($data);
831			$excerpt = TikiFilter::get('purifier')->filter($excerpt);
832		}
833
834		$wysiwyg = $is_wysiwyg == true ? 'y' : 'n';
835		if (! $created) {
836			$created = $tikilib->now;
837		}
838
839		$query = "insert into `tiki_blog_posts`(`blogId`,`data`,`excerpt`,`created`,`user`,`title`,`priv`,`wysiwyg`) values(?,?,?,?,?,?,?,?)";
840		$result = $this->query($query, [(int) $blogId, $data, $excerpt, (int) $created, $user, $title, $priv, $wysiwyg]);
841		$query = "select max(`postId`) from `tiki_blog_posts` where `created`=? and `user`=?";
842		$id = $this->getOne($query, [(int) $created, $user]);
843		$query = "update `tiki_blogs` set `lastModif`=?,`posts`=`posts`+1 where `blogId`=?";
844		$result = $this->query($query, [(int) $created, (int) $blogId]);
845		$this->add_blog_activity($blogId);
846
847		$wikilib = TikiLib::lib('wiki');
848		$wikilib->update_wikicontent_relations($data, 'post', $id);
849		$wikilib->update_wikicontent_links($data, 'post', $id);
850
851		if ($prefs['feature_user_watches'] == 'y' or $prefs['feature_group_watches'] == 'y') {
852			$nots = $tikilib->get_event_watches('blog_post', $blogId);
853			if (! isset($_SERVER["SERVER_NAME"])) {
854				$_SERVER["SERVER_NAME"] = $_SERVER["HTTP_HOST"];
855			}
856
857			if ($prefs['user_blog_watch_editor'] != "y") {
858				for ($i = count($nots) - 1; $i >= 0; --$i) {
859					if ($nots[$i]['user'] == $user) {
860						unset($nots[$i]);
861						break;
862					}
863				}
864			}
865
866			if ($prefs['feature_daily_report_watches'] == 'y') {
867				$query = "select `title` from `tiki_blogs` where `blogId`=?";
868				$blogTitle = $this->getOne($query, [(int)$blogId]);
869				$reportsManager = Reports_Factory::build('Reports_Manager');
870				$reportsManager->addToCache($nots, ["event" => 'blog_post', "blogId" => $blogId, "blogTitle" => $blogTitle, "postId" => $id, "user" => $user]);
871			}
872
873			if (count($nots)) {
874				include_once("lib/notifications/notificationemaillib.php");
875				$smarty->assign('mail_site', $_SERVER["SERVER_NAME"]);
876				$smarty->assign('mail_title', $this->get_title($blogId));
877				$smarty->assign('mail_post_title', $title);
878				$smarty->assign('mail_blogid', $blogId);
879				$smarty->assign('mail_postid', $id);
880				$smarty->assign('mail_user', $user);
881				$smarty->assign('mail_data', $data);
882
883				if ($prefs['feature_contribution'] == 'y' && ! empty($contributions)) {
884					$contributionlib = TikiLib::lib('contribution');
885					$smarty->assign('mail_contributions', $contributionlib->print_contributions($contributions));
886				}
887				sendEmailNotification($nots, "watch", "user_watch_blog_post_subject.tpl", $_SERVER["SERVER_NAME"], "user_watch_blog_post.tpl");
888			}
889		}
890
891		TikiLib::events()->trigger(
892			'tiki.blogpost.create',
893			[
894				'type' => 'blog post',
895				'object' => $id,
896				'blog' => $blogId,
897				'user' => $user,
898			]
899		);
900
901		if ($prefs['feature_actionlog'] == 'y') {
902			$logslib = TikiLib::lib('logs');
903			$logslib->add_action('Posted', $blogId, 'blog', "blogId=$blogId&amp;postId=$id&amp;add=" . strlen($data) . "#postId$id", '', '', '', '', $contributions);
904		}
905
906		require_once(__DIR__ . '/../search/refresh-functions.php');
907		refresh_index('blog_posts', $id);
908
909		$tikilib->object_post_save(['type' => 'blog post', 'object' => $id, 'description' => substr($data, 0, 200), 'name' => $title, 'href' => "tiki-view_blog_post.php?postId=$id"], ['content' => $data]);
910		return $id;
911	}
912
913	/**
914	 * remove_blog Removes a blog and all the posts of a blog
915	 *
916	 * @param int $blogId
917	 * @access public
918	 * @return boolean unconditionnal true
919	 */
920	function remove_blog($blogId)
921	{
922		global $user;
923		$tikilib = TikiLib::lib('tiki');
924
925		$query = "delete from `tiki_blogs` where `blogId`=?";
926
927		$result = $this->query($query, [(int) $blogId]);
928
929		$query = "select `postId` from `tiki_blog_posts` where `blogId`=?";
930		$result = $this->query($query, [(int) $blogId]);
931		if ($res = $result->fetchRow()) {
932			$tikilib->remove_object('post', $res['postId']);
933		}
934
935		$query = "delete from `tiki_blog_posts` where `blogId`=?";
936		$result = $this->query($query, [(int) $blogId]);
937		$tikilib->remove_object('blog', $blogId);
938
939		TikiLib::events()->trigger(
940			'tiki.blog.delete',
941			[
942				'type' => 'blog',
943				'object' => $blogId,
944				'user' => $user,
945			]
946		);
947
948		return true;
949	}
950
951	/**
952	 * remove_post Removes a post identified by $postId
953	 *
954	 * @param int $postId
955	 * @access public
956	 * @return boolean inconditionnal true
957	 */
958	function remove_post($postId)
959	{
960		$tikilib = TikiLib::lib('tiki');
961		$objectlib = TikiLib::lib('object');
962
963		$query = "select `blogId`, `data` from `tiki_blog_posts` where `postId`=?";
964		$result = $this->query($query, [(int) $postId]);
965		if ($res = $result->fetchRow()) {
966			$blogId = $res['blogId'];
967		} else {
968			$blogId = 0;
969		}
970
971		global $prefs;
972		if ($prefs['feature_actionlog'] == 'y') {
973			$logslib = TikiLib::lib('logs');
974			$param = "blogId=$blogId&amp;postId=$postId";
975			if ($blogId) {
976				$param .= "&amp;del=" . strlen($res['data']);
977			}
978			$logslib->add_action('Removed', $blogId, 'blog', $param);
979		}
980		if ($blogId) {
981			$tikilib->remove_object('post', (int)$postId);
982			$query = "delete from `tiki_blog_posts` where `postId`=?";
983
984			$result = $this->query($query, [(int) $postId]);
985			$query = "update `tiki_blogs` set `posts`=`posts`-1 where `blogId`=?";
986			$result = $this->query($query, [(int) $blogId]);
987		}
988
989		/*
990		 * TODO: this should be a method in freetaglib or maybe even better $tikilib->remove_object() should
991		 * remove the relation between the object and the tags, no?
992		 */
993		// When a post is deleted, all freetags asociated must also be deleted
994		$objectId = $objectlib->get_object_id('blog post', $postId);
995		$query = "DELETE FROM `tiki_freetagged_objects` WHERE `objectId` = ?";
996		$this->query($query, [(int) $objectId]);
997
998		$query = "delete from `tiki_blog_posts_images` where `postId`=?";
999		$this->query($query, [(int) $postId]);
1000
1001		$tikilib->remove_object('blog post', $postId);
1002
1003		TikiLib::events()->trigger(
1004			'tiki.blogpost.delete',
1005			[
1006				'type' => 'blog post',
1007				'object' => $postId,
1008				'blog' => $blogId,
1009				'user' => $GLOBALS['user'],
1010			]
1011		);
1012
1013		return true;
1014	}
1015
1016	/**
1017	 * get_post Returns the post identfied by $postId
1018	 *		Returns false if the post does not exist
1019	 *
1020	 * @param mixed $postId
1021	 * @param bool $adjacent whether to return or not adjacent posts
1022	 * @access public
1023	 * @return The post
1024	 */
1025	function get_post($postId, $adjacent = false)
1026	{
1027		$tikilib = TikiLib::lib('tiki');
1028
1029		$query = "select * from `tiki_blog_posts` where `postId`=?";
1030		$result = $this->query($query, [(int) $postId]);
1031		if ($result->numRows()) {
1032			$res = $result->fetchRow();
1033			$res['avatar'] = $tikilib->get_user_avatar($res['user']);
1034
1035			if ($adjacent) {
1036				$res['adjacent'] = $this->_get_adjacent_posts($res['blogId'], $res['created']);
1037			}
1038		} else {
1039			return false;
1040		}
1041		return $res;
1042	}
1043
1044	/**
1045	 * Get post related content using $freetaglib->get_similar()
1046	 *
1047	 * @param int $postId
1048	 * @param int $maxResults
1049	 * @return array
1050	 */
1051	function get_related_posts($postId, $maxResults = 5)
1052	{
1053		$freetaglib = TikiLib::lib('freetag');
1054		$related_posts = $freetaglib->get_similar('blog post', $postId, $maxResults);
1055
1056		// extract 'postId' from href to be able to use {self_link}
1057		foreach ($related_posts as $key => $post) {
1058			$related_posts[$key]['postId'] = str_replace('tiki-view_blog_post.php?postId=', '', $post['href']);
1059		}
1060
1061		return $related_posts;
1062	}
1063
1064	/**
1065	 * Get adjacent posts (previous and next by created date)
1066	 *
1067	 * @param int $blogId which blog the post belongs to
1068	 * @param int $created when the post was created
1069	 * @return array
1070	 */
1071	function _get_adjacent_posts($blogId, $created, $publishDate = null, $user = null, $allowprivate = null)
1072	{
1073		$res = [];
1074
1075		$next_query = 'SELECT postId, title FROM `tiki_blog_posts` WHERE `blogId` = ? AND `created` > ? ';
1076		$bindvars = [$blogId, $created];
1077		if ($publishDate) {
1078			$next_query .= 'AND (`created` <= ? OR `user` = ?)';
1079			$bindvars[] = $publishDate;
1080			$bindvars[] = $user;
1081		}
1082		if ($allowprivate == 'n') {
1083			$next_query .= ' AND `priv` = "n"';
1084		}
1085		$next_query .= ' ORDER BY created ASC';
1086		$result = $this->fetchAll($next_query, $bindvars, 1);
1087		$res['next'] = ! empty($result[0]) ? $result[0] : null;
1088
1089		$prev_query = 'SELECT postId, title FROM `tiki_blog_posts` WHERE `blogId` = ? AND `created` < ? ';
1090		$bindvars = [$blogId, $created];
1091		if ($publishDate) {
1092			$prev_query .= 'AND (`created` <= ? OR `user` = ?)';
1093			$bindvars[] = $publishDate;
1094			$bindvars[] = $user;
1095		}
1096		if ($allowprivate == 'n') {
1097			$prev_query .= ' AND `priv` = "n"';
1098		}
1099		$prev_query .= ' ORDER BY created DESC';
1100		$result = $this->fetchAll($prev_query, $bindvars, 1);
1101		$res['prev'] = ! empty($result[0]) ? $result[0] : null;
1102
1103		return $res;
1104	}
1105
1106	/**
1107	 * Updates a blog post
1108	 *
1109	 * @param int $postId
1110	 * @param int $blogId
1111	 * @param string $data
1112	 * @param string $excerpt
1113	 * @param string $user
1114	 * @param string $title
1115	 * @param string $contributions
1116	 * @param string $priv
1117	 * @param bool $is_wysiwyg
1118	 * @access public
1119	 * @return void
1120	 */
1121	function update_post(
1122		$postId,
1123		$blogId,
1124		$data,
1125		$excerpt,
1126		$user,
1127		$title = '',
1128		$contributions = '',
1129		$priv = 'n',
1130		$created = 0,
1131		$is_wysiwyg = false
1132	) {
1133
1134		global $prefs;
1135		$tikilib = TikiLib::lib('tiki');
1136
1137		if ($is_wysiwyg) {
1138			$data = TikiFilter::get('purifier')->filter($data);
1139			$excerpt = TikiFilter::get('purifier')->filter($excerpt);
1140		}
1141
1142		$wysiwyg = $is_wysiwyg == true ? 'y' : 'n';
1143		if ($prefs['feature_blog_edit_publish_date'] == 'y') {
1144			if (! $created) {
1145				$created = $tikilib->now;
1146			}
1147			$query = "update `tiki_blog_posts` set `blogId`=?,`data`=?,`excerpt`=?,`created`=?,`user`=?,`title`=?, `priv`=?, `wysiwyg`=? where `postId`=?";
1148			$result = $this->query($query, [$blogId, $data, $excerpt, $created,$user, $title, $priv, $wysiwyg, $postId]);
1149		} else {
1150			$query = "update `tiki_blog_posts` set `blogId`=?,`data`=?,`excerpt`=?,`user`=?,`title`=?, `priv`=?, `wysiwyg`=? where `postId`=?";
1151			$result = $this->query($query, [$blogId, $data, $excerpt, $user, $title, $priv, $wysiwyg, $postId]);
1152		}
1153
1154		$wikilib = TikiLib::lib('wiki');
1155		$wikilib->update_wikicontent_relations($data, 'post', $postId);
1156		$wikilib->update_wikicontent_links($data, 'post', $postId);
1157
1158		if ($prefs['feature_actionlog'] == 'y') {
1159			$logslib = TikiLib::lib('logs');
1160			$logslib->add_action('Updated', $blogId, 'blog', "blogId=$blogId&amp;postId=$postId#postId$postId", '', '', '', '', $contributions);
1161		}
1162
1163		require_once('lib/search/refresh-functions.php');
1164		refresh_index('blog_posts', $postId);
1165
1166		$tikilib->object_post_save(['type' => 'blog post', 'object' => $postId], ['content' => $data]);
1167	}
1168
1169	/**
1170	 * list_user_posts Returns all the posts from a user
1171	 *
1172	 * @param string $user login name of the user
1173	 * @param int $offset
1174	 * @param int $maxRecords
1175	 * @param string $sort_mode
1176	 * @param string $find
1177	 * @access public
1178	 * @return void
1179	 */
1180	function list_user_posts($user, $offset = 0, $maxRecords = -1, $sort_mode = 'created_desc', $find = '')
1181	{
1182
1183		if ($find) {
1184			$findesc = '%' . $find . '%';
1185
1186			$mid = " where `user`=? and (`data` like ?) ";
1187			$bindvars = [$user, $findesc];
1188		} else {
1189			$mid = ' where `user`=? ';
1190			$bindvars = [$user];
1191		}
1192
1193		$query = "select * from `tiki_blog_posts` $mid order by " . $this->convertSortMode($sort_mode);
1194		$query_cant = "select count(*) from `tiki_blog_posts` $mid";
1195		$result = $this->query($query, $bindvars, $maxRecords, $offset);
1196		$cant = $this->getOne($query_cant, $bindvars);
1197		$ret = [];
1198
1199		while ($res = $result->fetchRow()) {
1200			$ret[] = $res;
1201		}
1202
1203		$retval = [];
1204		$retval["data"] = $ret;
1205		$retval["cant"] = $cant;
1206		return $retval;
1207	}
1208
1209	/**
1210	 * add_blog_activity
1211	 *
1212	 * @param mixed $blogId
1213	 * @access public
1214	 * @return void
1215	 */
1216	function add_blog_activity($blogId)
1217	{
1218		$tikilib = TikiLib::lib('tiki');
1219
1220		//Caclulate activity, update tiki_blogs and purge activity table
1221		$today = $tikilib->make_time(0, 0, 0, $tikilib->date_format("%m"), $tikilib->date_format("%d"), $tikilib->date_format("%Y"));
1222
1223		$day0 = $today - (24 * 60 * 60);
1224		$day1 = $today - (2 * 24 * 60 * 60);
1225		$day2 = $today - (3 * 24 * 60 * 60);
1226		// Purge old activity
1227		$query = "delete from `tiki_blog_activity` where `day`<?";
1228		$result = $this->query($query, [(int) $day2]);
1229
1230		// Register new activity
1231		$query = "select count(*) from `tiki_blog_activity` where `blogId`=? and `day`=?";
1232		$result = $this->getOne($query, [(int) $blogId, (int)$today]);
1233
1234		if ($result) {
1235			$query = "update `tiki_blog_activity` set `posts`=`posts`+1 where `blogId`=? and `day`=?";
1236		} else {
1237			$query = "insert into `tiki_blog_activity`(`blogId`,`day`,`posts`) values(?,?,1)";
1238		}
1239
1240		$result = $this->query($query, [(int) $blogId, (int) $today]);
1241		// Calculate activity
1242		$query = "select `posts` from `tiki_blog_activity` where `blogId`=? and `day`=?";
1243		$vtoday = $this->getOne($query, [(int) $blogId, (int) $today]);
1244		$day0 = $this->getOne($query, [(int) $blogId, (int) $day0]);
1245		$day1 = $this->getOne($query, [(int) $blogId, (int) $day1]);
1246		$day2 = $this->getOne($query, [(int) $blogId, (int) $day2]);
1247		$activity = (2 * $vtoday) + ($day0) + (0.5 * $day1) + (0.25 * $day2);
1248		// Update tiki_blogs with activity information
1249		$query = "update `tiki_blogs` set `activity`=? where `blogId`=?";
1250		$result = $this->query($query, [$activity, (int) $blogId]);
1251	}
1252
1253	/**
1254	 * Returns the title of the blog "blogId"
1255	 *
1256	 * @param int $blogId
1257	 * @access public
1258	 * @return string the title of the blog
1259	 */
1260	function get_title($blogId)
1261	{
1262		$query = 'select `title` from `tiki_blogs` where `blogId`=?';
1263		return $this->getOne($query, [(int)$blogId]);
1264	}
1265
1266	/**
1267	 * Return true if blog exist or false if not
1268	 *
1269	 * @param int $blogId
1270	 * @return bool true or false depending if blog exist or not
1271	 */
1272	function blog_exists($blogId)
1273	{
1274		$query = 'SELECT `blogId` FROM `tiki_blogs` WHERE `blogId`=?';
1275
1276		if (is_null($this->getOne($query, [$blogId]))) {
1277			return false;
1278		} else {
1279			return true;
1280		}
1281	}
1282
1283	/**
1284	 * Check if a blog exists
1285	 *
1286	 * @param int $blogId
1287	 * @return bool true or false if blog exists or not
1288	 */
1289	function check_blog_exists($blogId)
1290	{
1291		$smarty = TikiLib::lib('smarty');
1292
1293		if (! $this->blog_exists($blogId)) {
1294			$msg = tra('Blog cannot be found');
1295			$smarty->assign('msg', $msg);
1296			$smarty->display('error.tpl');
1297			die;
1298		}
1299	}
1300
1301	/**
1302	 * Returns a list of posts that belongs to a particular blog
1303	 *
1304	 * @param int $blogId
1305	 * @return array list of post ids
1306	 */
1307	function get_blog_posts_ids($blogId)
1308	{
1309		$query = 'SELECT `postId` FROM `tiki_blog_posts` WHERE `blogId`=?';
1310		$result = $this->fetchMap($query, [$blogId]);
1311
1312		return array_keys($result);
1313	}
1314	function top_bloggers($limit, $blogId = null)
1315	{
1316		$mid = '';
1317		$bindvars = [];
1318		if ($blogId) {
1319			$mid = 'where `blogId`=?';
1320			$bindvars = [$blogId];
1321		}
1322		$query = 'select distinct(`user`), count(`user`) as posts from `tiki_blog_posts` ' . $mid . ' group by `user` order by `posts` desc';
1323		$result = $this->query($query, $bindvars, $limit, 0);
1324		$ret = [];
1325		while ($res = $result->fetchRow()) {
1326			$ret[] = $res;
1327		}
1328		$query = 'select count(distinct(`user`))  from `tiki_blog_posts`';
1329		$nb = $this->getOne($query);
1330		return ['data' => $ret, 'count' => $nb];
1331	}
1332	function mod_blog_posts(&$blogItems, $charCount, $wordBoundary = 'y', $ellipsis = 'y', $more = 'y')
1333	{
1334		$smarty = TikiLib::lib('smarty');
1335
1336		/* The function takes an argument asking if the break should occur on a
1337		   word boundary. The truncate function asks if words can be broken.
1338		   The same question is asked inversely so the supplied parameter needs
1339		   to be reversed to remain accurate. */
1340		$breakword = ($wordBoundary == 'y') ? false : true;
1341
1342		$etc = ($ellipsis == 'y') ? ' ... ' : ' ';
1343		$numBlogs = count($blogItems["data"]);
1344		for ($i = 0; $i < $numBlogs; $i++) {
1345			$pos = 0;
1346			$counter = 'char';
1347			$arrString = [];
1348			$tally = [ "char" => 0, "tags" => 0 ];
1349//			preg_match_all("[\s|\w]",$blogItems['data'][$i]['parsed_data'],$arrString);
1350			$arrString = $blogItems['data'][$i]['parsed_data'];
1351
1352			/* We don't want to stop this loop until we've counted sufficient displaying
1353			   characters, not just string positions */
1354			for ($pos = 0; $tally['char'] < $charCount; $pos++) {
1355				if ($arrString[$pos] == '<' && $counter == 'char') {
1356					$counter = 'tags';
1357				} elseif ($arrString[$pos] == '>' && $counter == 'tags') {
1358					$counter = 'char';
1359				}
1360				$tally[$counter]++;
1361			}
1362			$segLength = $charCount + $tally['tags'];
1363			$smarty->loadPlugin('smarty_modifier_truncate');
1364			$blogItems['data'][$i]['parsed_data'] =
1365				smarty_modifier_truncate(
1366					$blogItems['data'][$i]['parsed_data'],
1367					$segLength,
1368					$etc,
1369					$breakword
1370				);
1371
1372			if ($more == 'y') {
1373				$blogItems['data'][$i]['parsed_data'] .=
1374					'<a class="link" href="tiki-view_blog_post.php?postId=' .
1375					$blogItems['data'][$i]['postId'] . '"> &#60more&#62</a>';
1376			}
1377			unset($arrString);
1378		}
1379		return( $blogItems );
1380	}
1381
1382	/**
1383	 * Return blogId from postId
1384	 *
1385	 * @param int $postId
1386	 *
1387	 * @return int $blogId or false
1388	 */
1389	function get_blogId_from_post($postId)
1390	{
1391		$query = "SELECT `blogId` FROM `tiki_blog_posts` WHERE postId=?";
1392		$result = $this->query($query, [$postId]);
1393		if ($result->numRows()) {
1394			$res = $result->fetchRow();
1395			return $res['blogId'];
1396		} else {
1397			return false;
1398		}
1399	}
1400}
1401