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
14class ArtLib extends TikiLib
15{
16	//Special parsing for multipage articles
17	function get_number_of_pages($data)
18	{
19		$parts = explode('...page...', $data);
20		return count($parts);
21	}
22
23	function get_page($data, $i)
24	{
25		// Get slides
26		$parts = explode('...page...', $data);
27
28		if (! isset($parts[$i - 1])) {
29			$i = 1;
30		}
31		$ret = $parts[$i - 1];
32		if (substr($parts[$i - 1], 1, 5) == '<br/>') {
33			$ret = substr($parts[$i - 1], 6);
34		}
35
36		if (substr($parts[$i - 1], 1, 6) == '<br />') {
37			$ret = substr($parts[$i - 1], 7);
38		}
39
40		return $ret;
41	}
42
43	function approve_submission($subId)
44	{
45		$data = $this->get_submission($subId);
46
47		if (! $data) {
48			return false;
49		}
50
51		if (! $data['image_x']) {
52			$data['image_x'] = 0;
53		}
54
55		if (! $data['image_y']) {
56			$data['image_y'] = 0;
57		}
58
59		$articleId = $this->replace_article(
60			$data['title'],
61			$data['authorName'],
62			$data['topicId'],
63			$data['useImage'],
64			$data['image_name'],
65			$data['image_size'],
66			$data['image_type'],
67			$data['image_data'],
68			$data['heading'],
69			$data['body'],
70			$data['publishDate'],
71			$data['expireDate'],
72			$data['author'],
73			0,
74			$data['image_x'],
75			$data['image_y'],
76			$data['type'],
77			$data['topline'],
78			$data['subtitle'],
79			$data['linkto'],
80			$data['image_caption'],
81			$data['lang'],
82			$data['rating'],
83			$data['isfloat']
84		);
85		$this->transfer_attributes_from_submission($subId, $articleId);
86
87		$query = "update `tiki_objects` set `type`= ?, `itemId`= ?, `href`=? where `itemId` = ? and `type`= ?";
88		$this->query($query, ['article', (int)$articleId, "tiki-read_article.php?articleId=$articleId", (int)$subId, 'submission']);
89		$query = 'update `tiki_objects` set `href`=?, `type`=? where `href`=?';
90		$this->query($query, ["'tiki-read_article.php?articleId=$articleId", 'article', "tiki-edit_submission.php?subId=$subId"]);
91
92		$this->remove_submission($subId);
93	}
94
95	function add_article_hit($articleId)
96	{
97		if (StatsLib::is_stats_hit()) {
98			$query = "update `tiki_articles` set `nbreads`=`nbreads`+1 where `articleId`=?";
99			$this->query($query, [$articleId]);
100		}
101	}
102
103	function remove_article($articleId, $article_data = '')
104	{
105		global $user, $prefs;
106		$smarty = TikiLib::lib('smarty');
107		$tikilib = TikiLib::lib('tiki');
108
109		if ($articleId) {
110			if (empty($article_data)) {
111				$article_data = $this->get_article($articleId);
112			}
113			$query = 'delete from `tiki_articles` where `articleId`=?';
114
115			$result = $this->query($query, [$articleId]);
116			$this->remove_object('article', $articleId);
117
118			$multilinguallib = TikiLib::lib('multilingual');
119			$multilinguallib->detachTranslation('article', $articleId);
120
121			TikiLib::events()->trigger(
122				'tiki.article.delete',
123				[
124					'type' => 'article',
125					'object' => $articleId,
126					'user' => $user,
127				]
128			);
129
130			// TODO refactor
131			$nots = $tikilib->get_event_watches('article_deleted', '*');
132			if (! empty($article_data['topicId'])) {
133				$nots2 = $tikilib->get_event_watches('topic_article_deleted', $article_data['topicId']);
134			} else {
135				$nots2 = [];
136			}
137			$smarty->assign('mail_action', 'Delete');
138
139			$nots3 = [];
140			foreach ($nots as $n) {
141				$nots3[] = $n['email'];
142			}
143			foreach ($nots2 as $n) {
144				if (! in_array($n['email'], $nots3)) {
145					$nots[] = $n;
146				}
147			}
148
149			if ($prefs['user_article_watch_editor'] != "y") {
150				for ($i = count($nots) - 1; $i >= 0; --$i) {
151					if ($nots[$i]['user'] == $user) {
152						unset($nots[$i]);
153						break;
154					}
155				}
156			}
157
158			if (! isset($_SERVER['SERVER_NAME'])) {
159				$_SERVER['SERVER_NAME'] = $_SERVER["HTTP_HOST"];
160			}
161
162			if ($prefs['feature_user_watches'] == 'y' && $prefs['feature_daily_report_watches'] == 'y') {
163				$reportsManager = Reports_Factory::build('Reports_Manager');
164				$reportsManager->addToCache(
165					$nots,
166					[
167						'event'				=> 'article_deleted',
168						'articleId'			=> $articleId,
169						'articleTitle'		=> $article_data['title'],
170						'authorName'		=> $article_data['authorName'],
171						'user'				=> $user
172					]
173				);
174			}
175
176			if (count($nots) || (! empty($emails) && is_array($emails))) {
177				include_once('lib/notifications/notificationemaillib.php');
178
179				$smarty->assign('mail_site', $_SERVER['SERVER_NAME']);
180				$smarty->assign('mail_title', 'articleId=' . $articleId);
181				$smarty->assign('mail_postid', $articleId);
182				$smarty->assign('mail_user', $user);
183				$smarty->assign('mail_current_data', $article_data['heading'] . "\n----------------------\n" . $article_data['body']);
184
185				// the strings below are used to localize messages in the template file
186				//get_strings tr('New article post:') tr('Edited article post:') tr('Deleted article post:')
187				sendEmailNotification($nots, 'watch', 'user_watch_article_post_subject.tpl', $_SERVER['SERVER_NAME'], 'user_watch_article_post.tpl');
188			}
189
190			return true;
191		}
192	}
193
194	function remove_submission($subId)
195	{
196		if ($subId) {
197			$query = 'delete from `tiki_submissions` where `subId`=?';
198			$result = $this->query($query, [(int) $subId]);
199			$this->remove_object('submission', $subId);
200			return true;
201		}
202	}
203
204	function delete_expired_submissions($maxrows = 1000)
205	{
206		$tiki_submissions = TikiDb::get()->table('tiki_submissions');
207
208		$expired = $tiki_submissions->fetchColumn(
209			'subId',
210			['expireDate' => $tiki_submissions->lesserThan($this->now)],
211			$maxrows
212		);
213
214		$transaction = $this->begin();
215
216		foreach ($expired as $subId) {
217			$tiki_submissions->delete(['subId' => $subId]);
218
219			$this->remove_object('submission', $subId);
220		}
221
222		$transaction->commit();
223
224
225		return true;
226	}
227
228	function replace_submission($title, $authorName, $topicId, $useImage, $imgname, $imgsize, $imgtype, $imgdata, $heading, $body, $publishDate, $expireDate, $user, $subId, $image_x, $image_y, $type, $topline, $subtitle, $linkto, $image_caption, $lang, $rating = 0, $isfloat = 'n'
229														)
230	{
231		global $tiki_p_autoapprove_submission, $prefs;
232		$smarty = TikiLib::lib('smarty');
233		$tikilib = TikiLib::lib('tiki');
234
235		if ($expireDate < $publishDate) {
236			$expireDate = $publishDate;
237		}
238
239		if (empty($imgdata)) {
240			$imgdata = '';
241		}
242
243		$notificationlib = TikiLib::lib('notification');
244		$query = 'select `name` from `tiki_topics` where `topicId` = ?';
245		$topicName = $this->getOne($query, [(int) $topicId]);
246		$size = strlen($body);
247
248		$info = [
249			'title' => $title,
250			'authorName' => $authorName,
251			'topicId' => (int) $topicId,
252			'topicName' => $topicName,
253			'size' => (int) $size,
254			'useImage' => $useImage,
255			'image_name' => $imgname,
256			'image_type' => $imgtype,
257			'image_size' => (int) $imgsize,
258			'image_data' => $imgdata,
259			'isfloat' => $isfloat,
260			'image_x' => (int) $image_x,
261			'image_y' => (int) $image_y,
262			'heading' => $heading,
263			'body' => $body,
264			'publishDate' => (int) $publishDate,
265			'expireDate' => (int) $expireDate,
266			'author' => $user,
267			'type' => $type,
268			'rating' => (float) $rating,
269			'topline' => $topline,
270			'subtitle' => $subtitle,
271			'linkto' => $linkto,
272			'image_caption' => $image_caption,
273			'lang' => $lang,
274		];
275
276		$article_table = $this->table('tiki_submissions');
277		if ($subId) {
278			$article_table->update($info, [
279				'subId' => (int) $subId,
280			]);
281		} else {
282			$info['created'] = (int) $this->now;
283			$info['nbreads'] = 0;
284			$info['votes'] = 0;
285			$info['points'] = 0;
286			$id = $article_table->insert($info);
287		}
288
289		if ($tiki_p_autoapprove_submission != 'y') {
290			$nots = $tikilib->get_event_watches('article_submitted', '*');
291			$nots2 = $tikilib->get_event_watches('topic_article_created', $topicId);
292			$nots3 = [];
293			foreach ($nots as $n) {
294				$nots3[] = $n['email'];
295			}
296			foreach ($nots2 as $n) {
297				if (! in_array($n['emails'], $nots3)) {
298					$nots[] = $n;
299				}
300			}
301			if (! isset($_SERVER['SERVER_NAME'])) {
302				$_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST'];
303			}
304
305			if ($prefs['user_article_watch_editor'] != "y") {
306				for ($i = count($nots) - 1; $i >= 0; --$i) {
307					if ($nots[$i]['user'] == $user) {
308						unset($nots[$i]);
309						break;
310					}
311				}
312			}
313
314			if (count($nots)) {
315				include_once('lib/notifications/notificationemaillib.php');
316				$smarty->assign('mail_site', $_SERVER['SERVER_NAME']);
317				$smarty->assign('mail_user', $user);
318				$smarty->assign('mail_title', $title);
319				$smarty->assign('mail_heading', $heading);
320				$smarty->assign('mail_body', $body);
321				$smarty->assign('mail_subId', $id);
322				sendEmailNotification($nots, 'watch', 'submission_notification_subject.tpl', $_SERVER['SERVER_NAME'], 'submission_notification.tpl');
323			}
324		}
325		$tikilib = TikiLib::lib('tiki');
326		$tikilib->object_post_save(
327			[
328				'type' => 'submission',
329				'object' => $id,
330				'description' => substr($heading, 0, 200),
331				'name' => $title,
332				'href' => "tiki-edit_submission.php?subId=$id",
333			],
334			[ 'content' => $heading . "\n" . $body ]
335		);
336
337		return $id;
338	}
339
340	function replace_article( $title, $authorName, $topicId, $useImage, $imgname, $imgsize, $imgtype, $imgdata, $heading, $body, $publishDate, $expireDate, $user, $articleId, $image_x, $image_y, $type, $topline, $subtitle, $linkto, $image_caption, $lang, $rating = 0, $isfloat = 'n', $emails = '', $from = '', $list_image_x = '', $list_image_y = '', $ispublished = 'y', $fromurl = false
341												)
342	{
343
344		$tikilib = TikiLib::lib('tiki');
345		$smarty = TikiLib::lib('smarty');
346
347		if ($expireDate < $publishDate) {
348			$expireDate = $publishDate;
349		}
350		if (empty($imgdata) || $useImage === 'n') {	// remove image data if not using it
351			$imgdata = '';
352		}
353
354		$query = 'select `name` from `tiki_topics` where `topicId` = ?';
355		$topicName = $this->getOne($query, [$topicId]);
356		$size = $body ? mb_strlen($body) : mb_strlen($heading);
357
358		$info = [
359			'title' => $title,
360			'authorName' => $authorName,
361			'topicId' => (int) $topicId,
362			'topicName' => $topicName,
363			'size' => (int) $size,
364			'useImage' => $useImage,
365			'image_name' => $imgname,
366			'image_type' => $imgtype,
367			'image_size' => (int) $imgsize,
368			'image_data' => $imgdata,
369			'isfloat' => $isfloat,
370			'image_x' => (int) $image_x,
371			'image_y' => (int) $image_y,
372			'list_image_x' => (int) $list_image_x,
373			'list_image_y' => (int) $list_image_y,
374			'heading' => $heading,
375			'body' => $body,
376			'publishDate' => (int) $publishDate,
377			'expireDate' => (int) $expireDate,
378			'author' => $user,
379			'type' => $type,
380			'rating' => (float) $rating,
381			'topline' => $topline,
382			'subtitle' => $subtitle,
383			'linkto' => $linkto,
384			'image_caption' => $image_caption,
385			'lang' => $lang,
386			'ispublished' => $ispublished,
387		];
388
389		$article_table = $this->table('tiki_articles');
390		if ($articleId) {
391			$oldArticle = $this->get_article($articleId);
392			$article_table->update($info, [
393				'articleId' => (int) $articleId,
394			]);
395			// Clear article image cache because image may just have been changed
396			$this->delete_image_cache('article', $articleId);
397
398			$event = 'article_edited';
399			$nots = $tikilib->get_event_watches('article_edited', $articleId);
400			$nots2 = $tikilib->get_event_watches('topic_article_edited', $topicId);
401			$smarty->assign('mail_action', 'Edit');
402			$smarty->assign('mail_old_title', $oldArticle['title']);
403			$smarty->assign('mail_old_publish_date', $oldArticle['publishDate']);
404			$smarty->assign('mail_old_expiration_date', $oldArticle['expireDate']);
405			$smarty->assign('mail_old_data', $oldArticle['heading'] . "\n----------------------\n" . $oldArticle['body']);
406		} else {
407			$info['created'] = (int) $this->now;
408			$info['nbreads'] = 0;
409			$info['votes'] = 0;
410			$info['points'] = 0;
411
412			$articleId = $article_table->insert($info);
413
414			global $prefs;
415			TikiLib::events()->trigger(
416				'tiki.article.create',
417				[
418					'type' => 'article',
419					'object' => $articleId,
420					'user' => $user,
421				]
422			);
423			$event = 'article_submitted';
424			$nots = $tikilib->get_event_watches('article_submitted', $articleId);
425			$nots2 = $tikilib->get_event_watches('topic_article_created', $topicId);
426			$smarty->assign('mail_action', 'New');
427
428			// Create tracker item as well if feature is enabled
429			if (! $fromurl && $prefs['tracker_article_tracker'] == 'y' && $trackerId = $prefs['tracker_article_trackerId']) {
430				$trklib = TikiLib::lib('trk');
431				$definition = Tracker_Definition::get($trackerId);
432				if ($fieldId = $definition->getArticleField()) {
433					$addit = [];
434					$addit[] = [
435						'fieldId' => $fieldId,
436						'type' => 'articles',
437						'value' => $articleId,
438					];
439					$itemId = $trklib->replace_item($trackerId, 0, ['data' => $addit]);
440					TikiLib::lib('relation')->add_relation('tiki.article.attach', 'trackeritem', $itemId, 'article', $articleId);
441				}
442			}
443		}
444
445		$wikilib = TikiLib::lib('wiki');
446		$wikilib->update_wikicontent_relations(
447			$heading . "\n" . $body,
448			'article',
449			(int)$articleId
450		);
451		$wikilib->update_wikicontent_links(
452			$heading . "\n" . $body,
453			'article',
454			(int)$articleId
455		);
456
457		$nots3 = [];
458		foreach ($nots as $n) {
459			$nots3[] = $n['email'];
460		}
461		foreach ($nots2 as $n) {
462			if (! in_array($n['email'], $nots3)) {
463				$nots[] = $n;
464			}
465		}
466		if (is_array($emails) && (empty($from) || $from == $prefs['sender_email'])) {
467			foreach ($emails as $n) {
468				if (! in_array($n, $nots3)) {
469					$nots[] = ['email' => $n, 'language' => $prefs['site_language']];
470				}
471			}
472		}
473		global $prefs;
474
475		if ($prefs['user_article_watch_editor'] != "y") {
476			for ($i = count($nots) - 1; $i >= 0; --$i) {
477				if ($nots[$i]['user'] == $user) {
478					unset($nots[$i]);
479					break;
480				}
481			}
482		}
483
484		if (! isset($_SERVER['SERVER_NAME'])) {
485			$_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST'];
486		}
487
488		if ($prefs['feature_user_watches'] == 'y' && $prefs['feature_daily_report_watches'] == 'y') {
489			$reportsManager = Reports_Factory::build('Reports_Manager');
490			$reportsManager->addToCache(
491				$nots,
492				[
493					'event' => $event,
494					'articleId' => $articleId,
495					'articleTitle' => $title,
496					'authorName' => $authorName,
497					'user' => $user
498				]
499			);
500		}
501
502		if (count($nots) || is_array($emails)) {
503			include_once('lib/notifications/notificationemaillib.php');
504
505			$smarty->assign('mail_site', $_SERVER['SERVER_NAME']);
506			$smarty->assign('mail_title', $title);
507			$smarty->assign('mail_postid', $articleId);
508			$smarty->assign('mail_user', $user);
509			$smarty->assign('mail_current_publish_date', $publishDate);
510			$smarty->assign('mail_current_expiration_date', $expireDate);
511			$smarty->assign('mail_current_data', $heading . "\n----------------------\n" . $body);
512			sendEmailNotification($nots, 'watch', 'user_watch_article_post_subject.tpl', $_SERVER['SERVER_NAME'], 'user_watch_article_post.tpl');
513			if (is_array($emails) && ! empty($from) && $from != $prefs['sender_email']) {
514				$nots = [];
515				foreach ($emails as $n) {
516					$nots[] = ['email' => $n, 'language' => $prefs['site_language']];
517				}
518				sendEmailNotification($nots, 'watch', 'user_watch_article_post_subject.tpl', $_SERVER['SERVER_NAME'], 'user_watch_article_post.tpl', $from);
519			}
520		}
521
522
523		require_once('lib/search/refresh-functions.php');
524		refresh_index('articles', $articleId);
525
526		$tikilib = TikiLib::lib('tiki');
527		$tikilib->object_post_save(
528			[
529				'type' => 'article',
530				'object' => $articleId,
531				'description' => substr($heading, 0, 200),
532				'name' => $title,
533				'href' => "tiki-read_article.php?articleId=$articleId"
534			],
535			[ 'content' => $body . "\n" . $heading ]
536		);
537
538		return $articleId;
539	}
540
541	function add_topic($name, $imagename, $imagetype, $imagesize, $imagedata)
542	{
543		$query = 'insert into `tiki_topics`(`name`,`image_name`,`image_type`,`image_size`,`image_data`,`active`,`created`) values(?,?,?,?,?,?,?)';
544		$result = $this->query($query, [$name, $imagename, $imagetype, (int) $imagesize, $imagedata, 'y', (int) $this->now]);
545
546		$query = 'select max(`topicId`) from `tiki_topics` where `created`=? and `name`=?';
547		$topicId = $this->getOne($query, [(int) $this->now, $name]);
548		return $topicId;
549	}
550
551	function remove_topic($topicId, $all = 0)
552	{
553		$query = 'delete from `tiki_topics` where `topicId`=?';
554
555		$result = $this->query($query, [$topicId]);
556
557		if ($all == 1) {
558			$query = 'delete from `tiki_articles` where `topicId`=?';
559			$result = $this->query($query, [$topicId]);
560		} else {
561			$query = 'update `tiki_articles` set `topicId`=?, `topicName`=? where `topicId`=?';
562			$result = $this->query($query, [null, null, $topicId]);
563		}
564
565		return true;
566	}
567
568	function replace_topic_name($topicId, $name)
569	{
570		$query = 'update `tiki_topics` set `name` = ? where `topicId` = ?';
571		$result = $this->query($query, [$name, (int)$topicId]);
572
573		$query = 'update `tiki_articles` set `topicName` = ? where `topicId`= ?';
574		$result = $this->query($query, [$name, (int)$topicId]);
575		return true;
576	}
577
578	function replace_topic_image($topicId, $imagename, $imagetype, $imagesize, $imagedata)
579	{
580		$topicId = (int)$topicId;
581		$query = 'update `tiki_topics` set `image_name` = ?, `image_type` = ?, `image_size` = ?, `image_data` = ? where `topicId` = ?';
582		$result = $this->query($query, [$imagename, $imagetype, $imagesize, $imagedata, $topicId]);
583
584		return true;
585	}
586
587	function activate_topic($topicId)
588	{
589		$query = 'update `tiki_topics` set `active`=? where `topicId`=?';
590
591		$result = $this->query($query, ['y', $topicId]);
592	}
593
594	function deactivate_topic($topicId)
595	{
596		$query = 'update `tiki_topics` set `active`=? where `topicId`=?';
597
598		$result = $this->query($query, ['n', $topicId]);
599	}
600
601	function get_topic($topicId)
602	{
603		$query = 'select `topicId`,`name`,`image_name`,`image_size`,`image_type` from `tiki_topics` where `topicId`=?';
604
605		$result = $this->query($query, [$topicId]);
606
607		$res = $result->fetchRow();
608		return $res;
609	}
610
611	function get_topicId($name)
612	{
613		$query = 'select `topicId` from `tiki_topics` where `name`=?';
614		return $this->getOne($query, [$name]);
615	}
616
617	function list_topics()
618	{
619		$query = 'select `topicId`,`name`,`image_name`,`image_size`,`image_type`,`active` from `tiki_topics` order by `name`';
620
621		$result = $this->query($query, []);
622
623		$ret = [];
624
625		while ($res = $result->fetchRow()) {
626			$res['subs'] = $this->getOne('select count(*) from `tiki_submissions` where `topicId`=?', [$res['topicId']]);
627
628			$res['arts'] = $this->getOne('select count(*) from `tiki_articles` where `topicId`=?', [$res['topicId']]);
629			$ret[$res['topicId']] = $res;
630		}
631
632		return $ret;
633	}
634
635	function list_active_topics()
636	{
637		$query = 'select * from `tiki_topics` where `active`=?';
638
639		$result = $this->query($query, ['y']);
640
641		$ret = [];
642
643		while ($res = $result->fetchRow()) {
644			$ret[] = $res;
645		}
646
647		return $ret;
648	}
649
650	// Article Type functions
651	function add_type($type)
652	{
653		$result = $this->query('insert into `tiki_article_types`(`type`) values(?)', [$type]);
654
655		return true;
656	}
657
658	function edit_type( $type, $use_ratings, $show_pre_publ, $show_post_expire, $heading_only, $allow_comments, $comment_can_rate_article, $show_image, $show_avatar, $show_author, $show_pubdate, $show_expdate, $show_reads, $show_size, $show_topline, $show_subtitle, $show_linkto, $show_image_caption, $creator_edit
659										)
660	{
661		if ($use_ratings == 'on') {
662			$use_ratings = 'y';
663		} else {
664			$use_ratings = 'n';
665		}
666
667		if ($show_pre_publ == 'on') {
668			$show_pre_publ = 'y';
669		} else {
670			$show_pre_publ = 'n';
671		}
672
673		if ($show_post_expire == 'on') {
674			$show_post_expire = 'y';
675		} else {
676			$show_post_expire = 'n';
677		}
678
679		if ($heading_only == 'on') {
680			$heading_only = 'y';
681		} else {
682			$heading_only = 'n';
683		}
684
685		if ($allow_comments == 'on') {
686			$allow_comments = 'y';
687		} else {
688			$allow_comments = 'n';
689		}
690
691		if ($comment_can_rate_article == 'on') {
692			$comment_can_rate_article = 'y';
693		} else {
694			$comment_can_rate_article = 'n';
695		}
696
697		if ($show_image == 'on') {
698			$show_image = 'y';
699		} else {
700			$show_image = 'n';
701		}
702
703		if ($show_avatar == 'on') {
704			$show_avatar = 'y';
705		} else {
706			$show_avatar = 'n';
707		}
708
709		if ($show_author == 'on') {
710			$show_author = 'y';
711		} else {
712			$show_author = 'n';
713		}
714
715		if ($show_pubdate == 'on') {
716			$show_pubdate = 'y';
717		} else {
718			$show_pubdate = 'n';
719		}
720
721		if ($show_expdate == 'on') {
722			$show_expdate = 'y';
723		} else {
724			$show_expdate = 'n';
725		}
726
727		if ($show_reads == 'on') {
728			$show_reads = 'y';
729		} else {
730			$show_reads = 'n';
731		}
732
733		if ($show_size == 'on') {
734			$show_size = 'y';
735		} else {
736			$show_size = 'n';
737		}
738
739		if ($show_topline == 'on') {
740			$show_topline = 'y';
741		} else {
742			$show_topline = 'n';
743		}
744		if ($show_subtitle == 'on') {
745			$show_subtitle = 'y';
746		} else {
747			$show_subtitle = 'n';
748		}
749
750		if ($show_linkto == 'on') {
751			$show_linkto = 'y';
752		} else {
753			$show_linkto = 'n';
754		}
755
756		if ($show_image_caption == 'on') {
757			$show_image_caption = 'y';
758		} else {
759			$show_image_caption = 'n';
760		}
761
762		if ($creator_edit == 'on') {
763			$creator_edit = 'y';
764		} else {
765			$creator_edit = 'n';
766		}
767		$query = "update `tiki_article_types` set
768			`use_ratings` = ?,
769			`show_pre_publ` = ?,
770			`show_post_expire` = ?,
771			`heading_only` = ?,
772			`allow_comments` = ?,
773			`comment_can_rate_article` = ?,
774			`show_image` = ?,
775			`show_avatar` = ?,
776			`show_author` = ?,
777			`show_pubdate` = ?,
778			`show_expdate` = ?,
779			`show_reads` = ?,
780			`show_size` = ?,
781			`show_topline` = ?,
782			`show_subtitle` = ?,
783			`show_linkto` = ?,
784			`show_image_caption` = ?,
785			`creator_edit` = ?
786			where `type` = ?";
787
788		$result = $this->query(
789			$query,
790			[
791				$use_ratings,
792				$show_pre_publ,
793				$show_post_expire,
794				$heading_only,
795				$allow_comments,
796				$comment_can_rate_article,
797				$show_image,
798				$show_avatar,
799				$show_author,
800				$show_pubdate,
801				$show_expdate,
802				$show_reads,
803				$show_size,
804				$show_topline,
805				$show_subtitle,
806				$show_linkto,
807				$show_image_caption,
808				$creator_edit,
809				$type
810			]
811		);
812	}
813
814	function remove_type($type)
815	{
816		$query = 'delete from `tiki_article_types` where `type`=?';
817		$result = $this->query($query, [$type]);
818		// remove attributes set for this type too
819		$query = "delete from `tiki_object_relations` where `source_type` = 'articletype' and `source_itemId`=?";
820		$result = $this->query($query, [$type]);
821	}
822
823	function get_type($type)
824	{
825		$query = 'select * from `tiki_article_types` where `type`=?';
826
827		$result = $this->query($query, [$type]);
828
829		$res = $result->fetchRow();
830		return $res;
831	}
832
833	function list_types()
834	{
835		$query = 'select * from `tiki_article_types`';
836		$result = $this->query($query, []);
837		$ret = [];
838
839		while ($res = $result->fetchRow()) {
840			$res['article_cnt'] = $this->getOne('select count(*) from `tiki_articles` where `type` = ?', [$res['type']]);
841			$ret[] = $res;
842		}
843
844		return $ret;
845	}
846
847	function list_types_byname()
848	{
849		$query = "select * from `tiki_article_types` order by `type` asc";
850		$result = $this->query($query, []);
851		$ret = [];
852
853		while ($res = $result->fetchRow()) {
854			$ret[$res['type']] = $res;
855		}
856
857		return $ret;
858	}
859
860	function get_user_articles($user, $max)
861	{
862		$query = 'select `articleId` ,`title` from `tiki_articles` where `author`=? order by `publishDate` desc';
863
864		$articles = $this->fetchAll($query, [$user], $max);
865
866		return Perms::filter(['type' => 'article'], 'object', $articles, ['object' => 'articleId'], 'read_article');
867	}
868
869	function import_csv($fileName, &$msgs, $csvDelimiter = ',')
870	{
871		global $user, $prefs, $tikilib;
872		$fhandle = fopen($fileName, 'r');
873		if (($fds = fgetcsv($fhandle, 4096, $csvDelimiter)) === false || empty($fds[0])) {
874			$msgs[] = tra('The file has incorrect syntax or is not a CSV file');
875			return false;
876		}
877		for ($i = 0, $icount_fds = count($fds); $i < $icount_fds; $i++) {
878			$fields[trim($fds[$i])] = $i;
879		}
880		if (! isset($fields['title'])) {
881			$fields['title']				= $i++;
882		}
883		if (! isset($fields['authorName'])) {
884			$fields['authorName']		= $i++;
885		}
886		if (! isset($fields['topicId'])) {
887			$fields['topicId']			= $i++;
888		}
889		if (! isset($fields['useImage'])) {
890			$fields['useImage']			= $i++;
891		}
892		if (! isset($fields['imgname'])) {
893			$fields['imgname']			= $i++;
894		}
895		if (! isset($fields['imgsize'])) {
896			$fields['imgsize']			= $i++;
897		}
898		if (! isset($fields['imgtype'])) {
899			$fields['imgtype']			= $i++;
900		}
901		if (! isset($fields['imgdata'])) {
902			$fields['imgdata']			= $i++;
903		}
904		if (! isset($fields['heading'])) {
905			$fields['heading']			= $i++;
906		}
907		if (! isset($fields['body'])) {
908			$fields['body']					= $i++;
909		}
910		if (! isset($fields['publishDate'])) {
911			$fields['publishDate']	= $i++;
912		}
913		if (! isset($fields['expireDate'])) {
914			$fields['expireDate']		= $i++;
915		}
916		if (! isset($fields['user'])) {
917			$fields['user']					= $i++;
918		}
919		if (! isset($fields['image_x'])) {
920			$fields['image_x']			= $i++;
921		}
922		if (! isset($fields['image_y'])) {
923			$fields['image_y']			= $i++;
924		}
925		if (! isset($fields['type'])) {
926			$fields['type']					= $i++;
927		}
928		if (! isset($fields['topline'])) {
929			$fields['topline']			= $i++;
930		}
931		if (! isset($fields['subtitle'])) {
932			$fields['subtitle']			= $i++;
933		}
934		if (! isset($fields['linkto'])) {
935			$fields['linkto']				= $i++;
936		}
937		if (! isset($fields['image_caption'])) {
938			$fields['image_caption'] = $i++;
939		}
940		if (! isset($fields['lang'])) {
941			$fields['lang']					= $i++;
942		}
943		if (! isset($fields['rating'])) {
944			$fields['rating']				= $i++;
945		}
946		if (! isset($fields['isfloat'])) {
947			$fields['isfloat']			= $i++;
948		}
949		if (! isset($fields['emails'])) {
950			$fields['emails']				= $i++;
951		}
952		$line = 1;
953		while (($data = fgetcsv($fhandle, 4096, $csvDelimiter)) !== false) {
954			++$line;
955			if (! isset($data[$fields['title']])) {
956				$data[$fields['title']]					= '';
957			}
958			if (! isset($data[$fields['authorName']])) {
959				$data[$fields['authorName']]		= '';
960			}
961			if (! isset($data[$fields['topicId']])) {
962				$data[$fields['topicId']]				= 0;
963			}
964			if (! isset($data[$fields['useImage']])) {
965				$data[$fields['useImage']]			= 'n';
966			}
967			if (! isset($data[$fields['imgname']])) {
968				$data[$fields['imgname']]				= '';
969			}
970			if (! isset($data[$fields['imgsize']])) {
971				$data[$fields['imgsize']]				= '';
972			}
973			if (! isset($data[$fields['imgtype']])) {
974				$data[$fields['imgtype']]				= '';
975			}
976			if (! isset($data[$fields['imgdata']])) {
977				$data[$fields['imgdata']]				= '';
978			}
979			if (! isset($data[$fields['heading']])) {
980				$data[$fields['heading']]				= '';
981			}
982			if (! isset($data[$fields['body']])) {
983				$data[$fields['body']]					= '';
984			}
985			if (! isset($data[$fields['publishDate']])) {
986				$data[$fields['publishDate']]		= $tikilib->now;
987			}
988			if (! isset($data[$fields['expireDate']])) {
989				$data[$fields['expireDate']]		= $tikilib->now + 365 * 24 * 60 * 60;
990			}
991			if (! isset($data[$fields['user']])) {
992				$data[$fields['user']]					= $user;
993			}
994			if (! isset($data[$fields['image_x']])) {
995				$data[$fields['image_x']]				= 0;
996			}
997			if (! isset($data[$fields['image_y']])) {
998				$data[$fields['image_y']]				= 0;
999			}
1000			if (! isset($data[$fields['type']])) {
1001				$data[$fields['type']]					= 'Article';
1002			}
1003			if (! isset($data[$fields['topline']])) {
1004				$data[$fields['topline']]				= '';
1005			}
1006			if (! isset($data[$fields['subtitle']])) {
1007				$data[$fields['subtitle']]			= '';
1008			}
1009			if (! isset($data[$fields['linkto']])) {
1010				$data[$fields['linkto']]				= '';
1011			}
1012			if (! isset($data[$fields['image_caption']])) {
1013				$data[$fields['image_caption']] = '';
1014			}
1015			if (! isset($data[$fields['lang']])) {
1016				$data[$fields['lang']]					= $prefs['language'];
1017			}
1018			if (! isset($data[$fields['rating']])) {
1019				$data[$fields['rating']]				= 7;
1020			}
1021			if (! isset($data[$fields['isfloat']])) {
1022				$data[$fields['isfloat']]				= 'n';
1023			}
1024			if (! isset($data[$fields['emails']])) {
1025				$data[$fields['emails']]				= '';
1026			}
1027
1028			$articleId = $this->replace_article(
1029				$data[$fields['title']],
1030				$data[$fields['authorName']],
1031				$data[$fields['topicId']],
1032				$data[$fields['useImage']],
1033				$data[$fields['imgname']],
1034				$data[$fields['imgsize']],
1035				$data[$fields['imgtype']],
1036				$data[$fields['imgdata']],
1037				$data[$fields['heading']],
1038				$data[$fields['body']],
1039				$data[$fields['publishDate']],
1040				$data[$fields['expireDate']],
1041				$data[$fields['user']],
1042				0,
1043				$data[$fields['image_x']],
1044				$data[$fields['image_y']],
1045				$data[$fields['type']],
1046				$data[$fields['topline']],
1047				$data[$fields['subtitle']],
1048				$data[$fields['linkto']],
1049				$data[$fields['image_caption']],
1050				$data[$fields['lang']],
1051				$data[$fields['rating']],
1052				$data[$fields['isfloat']],
1053				$data[$fields['emails']]
1054			);
1055			if (empty($articleId)) {
1056				$msgs[] = sprintf(tr('Error line: %d'), $line);
1057				return false;
1058			}
1059		}
1060		if (isset($articleId) && $articleId) {
1061			return true;
1062		} else {
1063			$msgs[] = tr('Import failed due to data format. Make sure the file has Unix-style line breaks.');
1064			return false;
1065		}
1066	}
1067
1068	function delete_image_cache($image_type, $imageId)
1069	{
1070		global $prefs, $tikidomain;
1071		// Input validation: imageId must be a number, and not 0
1072		if (! ctype_digit("$imageId") || ! ($imageId > 0)) {
1073			return false;
1074		}
1075		switch ($image_type) {
1076			case 'article':
1077				$image_cache_prefix = 'article';
1078				break;
1079			case 'submission':
1080				$image_cache_prefix = 'article_submission';
1081				break;
1082			case 'preview':
1083				$image_cache_prefix = 'article_preview';
1084				break;
1085			default:
1086				return false;
1087		}
1088		$article_image_cache = $prefs['tmpDir'];
1089		if ($tikidomain) {
1090			$article_image_cache .= "/$tikidomain";
1091		}
1092		$article_image_cache .= "/$image_cache_prefix." . $imageId;
1093		if (@unlink($article_image_cache)) {
1094			return true;
1095		} else {
1096			return false;
1097		}
1098	}
1099
1100	function get_title($articleId)
1101	{
1102		$query = 'select `title` from `tiki_articles` where `articleId`=?';
1103		return $this->getOne($query, [(int)$articleId]);
1104	}
1105
1106	function fetchtopicId($topic)
1107	{
1108		$topicId = '';
1109		$query = 'select `topicId` from `tiki_topics` where `name` = ?';
1110		$topicId = $this->getOne($query, [$topic]);
1111		return $topicId;
1112	}
1113
1114	function get_most_recent_article_id()
1115	{
1116		$maxRecords = 1;
1117		$sort_mode = 'publishDate_desc';
1118		$date_min = 0;
1119		$date_max = $this->now;
1120		$query = 'SELECT `tiki_articles`.`articleId` FROM `tiki_articles` INNER JOIN `tiki_article_types` on `tiki_articles`.`type` = `tiki_article_types`.`type` ' .
1121				 'WHERE `tiki_articles`.`publishDate`>=\'0\' AND (`tiki_articles`.`publishDate`<=? OR `tiki_article_types`.`show_pre_publ`=\'y\') AND ' .
1122				 '(`tiki_articles`.`expireDate`>? OR `tiki_article_types`.`show_post_expire`=\'y\') AND `tiki_articles`.`ispublished`=\'y\' ' .
1123				 'ORDER BY `publishDate` DESC';
1124		$bindvars = [ $date_max, $date_max ];
1125		$id = $this->getOne($query, $bindvars);
1126		return $id;
1127	}
1128
1129	function list_articles( $offset = 0, $maxRecords = -1, $sort_mode = 'publishDate_desc', $find = '', $date_min = 0, $date_max = 0, $user = false, $type = '', $topicId = '', $visible_only = 'y', $topic = '', $categId = '', $creator = '', $group = '', $lang = '', $min_rating = '', $max_rating = '', $override_dates = false, $ispublished = '', $filter = ''
1130												)
1131	{
1132
1133		global $user, $prefs;
1134		$userlib = TikiLib::lib('user');
1135
1136		$mid = $join = '';
1137		$bindvars = [];
1138		$fromSql = '';
1139
1140		if (! empty($filter)) {
1141			foreach ($filter as $typeF => $val) {
1142				if ($typeF == 'translationOrphan') {
1143					$multilinguallib = TikiLib::lib('multilingual');
1144					$multilinguallib->sqlTranslationOrphan('article', '`tiki_articles`', 'articleId', $val, $join, $mid, $bindvars);
1145					$mid = ' where ' . $mid;
1146				}
1147				if ($typeF == 'articleId' || $typeF == 'notArticleId') {
1148					$mid .= empty($mid) ? ' where ' : ' and ';
1149					$mid .= '`articleId` ' . ($typeF == 'notArticleId' ? 'not in ' : 'in') . ' (' . implode(',', array_fill(0, count($val), '?')) . ')';
1150					$bindvars = array_merge($bindvars, $val);
1151				}
1152			}
1153		}
1154
1155		if ($find) {
1156			$findesc = '%' . $find . '%';
1157			$mid .= empty($mid) ? ' where ' : ' and ';
1158			$mid .= " (`title` like ? or `heading` like ? or `body` like ? or `author` like ? or `authorName` like ?) ";
1159			$bindvars = [$findesc, $findesc, $findesc, $findesc, $findesc];
1160		}
1161
1162		// type=>[!]a+b+c+d+...
1163		if ($type) {
1164			$invert = '';
1165			$connect = ' or ';
1166			// parameter list negated?
1167			if (substr($type, 0, 1) == '!') {
1168				$type = substr($type, 1);
1169				$invert = '!';
1170				$connect = ' and ';
1171			}
1172			$add = '';
1173			$rest = explode('+', $type);
1174			foreach ($rest as $type) {
1175				if ($add == '') {
1176					if ($mid) {
1177						$mid .= ' and ';
1178					} else {
1179						$mid = ' where ';
1180					}
1181				} else {
1182					$add .= $connect;
1183				}
1184				$add .= " `tiki_articles`.`type`$invert=? ";
1185				$bindvars[] = $type;
1186			}
1187			if ($add <> '') {
1188				$mid .= ' ( ' . $add . ' ) ';
1189			}
1190		}
1191
1192		// topicId=>[!]a+b+c+d+...
1193		if (($topicId) || ($topicId == '0')) {
1194			$invert = '';
1195			$connect = ' or ';
1196			// parameter list negated?
1197			if (substr($topicId, 0, 1) == '!') {
1198				$topicId = substr($topicId, 1);
1199				$invert = '!';
1200				$connect = ' and ';
1201			}
1202			$add = '';
1203			$rest = explode('+', $topicId);
1204			foreach ($rest as $topicId) {
1205				if ($add == '') {
1206					if ($mid) {
1207						$mid .= ' and ';
1208					} else {
1209						$mid = ' where ';
1210					}
1211				} else {
1212					$add .= $connect;
1213				}
1214				$add .= " `tiki_articles`.`topicId`$invert=? ";
1215				$bindvars[] = $topicId;
1216			}
1217			if ($add <> '') {
1218				$mid .= ' ( ' . $add . ' ) ';
1219			}
1220		}
1221
1222		// topic=>[!]a+b+c+d+...
1223		if ($topic) {
1224			$invert = '';
1225			// parameter list negated?
1226			if (substr($topic, 0, 1) == '!') {
1227				$topic = substr($topic, 1);
1228				$invert = '!';
1229			}
1230			$rest = explode('\+', $topic);
1231
1232			if ($mid) {
1233				$mid .= ' and ';
1234			} else {
1235				$mid = ' where ';
1236			}
1237			$add = $this->in('tiki_articles.topicName', $rest, $bindvars);
1238			if ($add <> '') {
1239				$add = ($invert ? ' NOT' : '') . ' ( ' . $add . ' ) ';
1240				if ($invert) {
1241					$add = 'COALESCE(' . $add . ', TRUE)';
1242				}
1243				$mid .= $add;
1244			}
1245		}
1246		if (($visible_only) && ($visible_only <> 'n')) {
1247			if ($date_max <= 0) {
1248				// show articles published today
1249				$date_max = $this->now;
1250			}
1251			$bindvars[] = (int)$date_min;
1252			$bindvars[] = (int)$date_max;
1253			if ($override_dates) {
1254				$condition = "`tiki_articles`.`publishDate`>=? and `tiki_articles`.`publishDate`<=?";
1255			} else {
1256				$bindvars[] = (int)$this->now;
1257				$condition = "`tiki_articles`.`publishDate`>=? and (`tiki_articles`.`publishDate`<=? or `tiki_article_types`.`show_pre_publ`='y')"
1258										. " and (`tiki_articles`.`expireDate`>? or `tiki_article_types`.`show_post_expire`='y')"
1259										;
1260			}
1261			$mid .= ( $mid ? ' and ' : ' where ' ) . $condition;
1262		}
1263		if (! empty($lang)) {
1264			$condition = '`tiki_articles`.`lang`=?';
1265			$mid .= ($mid) ? ' and ' : ' where ';
1266			$mid .= $condition . ' ';
1267			$bindvars[] = $lang;
1268		}
1269		if (! empty($ispublished)) {
1270			$condition = '`tiki_articles`.`ispublished`=?';
1271			$mid .= ($mid) ? ' and ' : ' where ';
1272			$mid .= $condition . ' ';
1273			$bindvars[] = $ispublished;
1274		}
1275		if ($mid) {
1276			$mid2 = ' and 1 = 1 ';
1277		} else {
1278			$mid2 = ' where 1 = 1 ';
1279		}
1280
1281		if ($creator != '') {
1282			$mid2 .= ' and `tiki_articles`.`author` like ? ';
1283			$bindvars[] = "%$creator%";
1284		}
1285
1286		if ($min_rating || $max_rating) {
1287			$min_rating = isset($min_rating) ? $min_rating : '0.0';
1288			$max_rating = isset($max_rating) ? $max_rating : '10.0';
1289			$mid2 .= ' and (`tiki_articles`.`rating` >= ? and `tiki_articles`.`rating` <= ? )';
1290			$bindvars[] = $min_rating;
1291			$bindvars[] = $max_rating;
1292		}
1293
1294		$categlib = TikiLib::lib('categ');
1295		if ($categId) {
1296			$jail = $categId;
1297		} else {
1298			$jail = $categlib->get_jail();
1299		}
1300		if ($jail) {
1301			$categlib->getSqlJoin($jail, 'article', '`tiki_articles`.`articleId`', $fromSql, $mid2, $bindvars);
1302		}
1303
1304		if (empty($sort_mode)) {
1305			$sort_mode = 'publishDate_desc';
1306		}
1307
1308		if ($prefs['rating_advanced'] == 'y') {
1309			$ratinglib = TikiLib::lib('rating');
1310			$fromSql .= $ratinglib->convert_rating_sort($sort_mode, 'article', '`articleId`');
1311		}
1312
1313		$fromSql .= ' inner join `tiki_article_types` on `tiki_articles`.`type` = `tiki_article_types`.`type` ';
1314
1315		$query = "select distinct `tiki_articles`.*,
1316			`tiki_article_types`.`use_ratings`,
1317			`tiki_article_types`.`show_pre_publ`,
1318			`tiki_article_types`.`show_post_expire`,
1319			`tiki_article_types`.`heading_only`,
1320			`tiki_article_types`.`allow_comments`,
1321			`tiki_article_types`.`comment_can_rate_article`,
1322			`tiki_article_types`.`show_image`,
1323			`tiki_article_types`.`show_avatar`,
1324			`tiki_article_types`.`show_author`,
1325			`tiki_article_types`.`show_pubdate`,
1326			`tiki_article_types`.`show_expdate`,
1327			`tiki_article_types`.`show_reads`,
1328			`tiki_article_types`.`show_size`,
1329			`tiki_article_types`.`show_topline`,
1330			`tiki_article_types`.`show_subtitle`,
1331			`tiki_article_types`.`show_linkto`,
1332			`tiki_article_types`.`show_image_caption`,
1333			`tiki_article_types`.`creator_edit`
1334			from `tiki_articles`
1335			$fromSql
1336			$join
1337			$mid $mid2 order by " .
1338			$this->convertSortMode(
1339				$sort_mode,
1340				[
1341					'title',
1342					'state',
1343					'authorName',
1344					'topicId',
1345					'topicName',
1346					'publishDate',
1347					'expireDate',
1348					'created',
1349					'author',
1350					'rating',
1351					'nbreads',
1352				]
1353			);
1354
1355		$result = $this->fetchAll($query, $bindvars, $maxRecords, $offset);
1356		$query_cant = "select distinct count(*) from `tiki_articles` $fromSql $join $mid $mid2";
1357		$cant = $this->getOne($query_cant, $bindvars);
1358		$ret = [];
1359		$articleIds = array_map(
1360			function($res){
1361				return $res['articleId'];
1362			}, $result
1363		);
1364		Perms::bulk(['type' => 'article'], 'object', $articleIds);
1365		foreach ($result as $res) {
1366			$res['perms'] = $this->get_perm_object($res['articleId'], 'article', [], false);
1367			// Determine if unpublished article should be listed
1368			if ($res['ispublished'] != 'y' && $res['perms']['tiki_p_edit_article'] != 'y') {
1369				$res['disp_article'] = 'n';
1370			} else {
1371				// no need to do all of the following if we are not adding this article to the array
1372				if ($res['perms']['tiki_p_read_article'] == 'y' || $res['perms']['tiki_p_articles_read_heading'] == 'y') {
1373					$res['entrating'] = floor($res['rating']);
1374					if (empty($res['body'])) {
1375						$res['isEmpty'] = 'y';
1376					} else {
1377						$res['isEmpty'] = 'n';
1378					}
1379					if (strlen($res['image_data']) > 0) {
1380						$res['hasImage'] = 'y';
1381					} else {
1382						$res['hasImage'] = 'n';
1383					}
1384					$res['count_comments'] = 0;
1385
1386					// Determine if the article would be displayed in the view page
1387					$res['disp_article'] = 'y';
1388					if (($res['show_pre_publ'] != 'y') and ($this->now < $res['publishDate']) && ! $override_dates) {
1389						$res['disp_article'] = 'n';
1390					}
1391					if (($res['show_post_expire'] != 'y') and ($this->now > $res['expireDate']) && ! $override_dates) {
1392						$res['disp_article'] = 'n';
1393					}
1394					$ret[] = $res;
1395				}
1396			}
1397		}
1398		$retval = [];
1399		$retval['data'] = $ret;
1400		$retval['cant'] = $cant;
1401		return $retval;
1402	}
1403
1404	/**
1405	 * Work out if body (or heading) should be parsed as html or not
1406	 * Currently (tiki 11) tries the prefs but also checks for html in body in case wysiwyg_htmltowiki wasn't enabled previously
1407	 *
1408	 * @param array $article of article data
1409	 * @param bool $check_heading	use heading or (default) body
1410	 * @return bool
1411	 */
1412	function is_html($article, $check_heading = false)
1413	{
1414		global $prefs;
1415
1416		$text = $check_heading ? $article['heading'] : $article['body'];
1417
1418		return ($prefs['feature_wysiwyg'] === 'y' || $prefs['mobile_mode'] === 'y') &&	// this now assumes that if in mobile mode any html in articles should not be encoded
1419				$prefs['wysiwyg_htmltowiki'] !== 'y' ||
1420						preg_match('/(<\/p>|<\/span>|<\/div>|<\/?br>)/', $text);
1421	}
1422
1423	function list_submissions($offset = 0, $maxRecords = -1, $sort_mode = 'publishDate_desc', $find = '', $date = '', $type = '', $topicId = '', $lang = '')
1424	{
1425		if ($find) {
1426			$findPattern = '%' . $find . '%';
1427			$mid = " where (`title` like ? or `heading` like ? or `body` like ? or `author` like ? or `authorName` like ?) ";
1428			$bindvars = [$findPattern, $findPattern, $findPattern, $findPattern, $findPattern];
1429		} else {
1430			$mid = '';
1431			$bindvars = [];
1432		}
1433
1434		if ($date) {
1435			if ($mid) {
1436				$mid .= ' and `publishDate` <= ? ';
1437			} else {
1438				$mid = ' where `publishDate` <= ? ';
1439			}
1440			$bindvars[] = $date;
1441		}
1442
1443		if ($type) {
1444			$mid .= $mid ? ' AND ' : ' WHERE ';
1445			$mid .= ' `type` = ? ';
1446			$bindvars[] = $type;
1447		}
1448
1449		if ($topicId) {
1450			$mid .= $mid ? ' AND ' : ' WHERE ';
1451			$mid .= ' `topicId` = ? ';
1452			$bindvars[] = $topicId;
1453		}
1454
1455		if ($lang) {
1456			$mid .= $mid ? ' AND ' : ' WHERE ';
1457			$mid .= ' `lang` = ? ';
1458			$bindvars[] = $lang;
1459		}
1460
1461		$query = "select * from `tiki_submissions` $mid order by " . $this->convertSortMode($sort_mode);
1462		$query_cant = "select count(*) from `tiki_submissions` $mid";
1463		$result = $this->query($query, $bindvars, $maxRecords, $offset);
1464		$cant = $this->getOne($query_cant, $bindvars);
1465		$ret = [];
1466
1467		while ($res = $result->fetchRow()) {
1468			$res['entrating'] = floor($res['rating']);
1469
1470			if (empty($res['body'])) {
1471				$res['isEmpty'] = 'y';
1472			} else {
1473				$res['isEmpty'] = 'n';
1474			}
1475
1476			if (strlen($res['image_data']) > 0) {
1477				$res['hasImage'] = 'y';
1478			} else {
1479				$res['hasImage'] = 'n';
1480			}
1481
1482			$ret[] = $res;
1483		}
1484
1485		$retval = [];
1486		$retval['data'] = $ret;
1487		$retval['cant'] = $cant;
1488		return $retval;
1489	}
1490
1491	function get_article($articleId, $checkPerms = true)
1492	{
1493		global $user, $prefs;
1494		$query = "select `tiki_articles`.*,
1495								`users_users`.`avatarLibName`,
1496								`tiki_article_types`.`use_ratings`,
1497								`tiki_article_types`.`show_pre_publ`,
1498								`tiki_article_types`.`show_post_expire`,
1499								`tiki_article_types`.`heading_only`,
1500								`tiki_article_types`.`allow_comments`,
1501								`tiki_article_types`.`comment_can_rate_article`,
1502								`tiki_article_types`.`show_image`,
1503								`tiki_article_types`.`show_avatar`,
1504								`tiki_article_types`.`show_author`,
1505								`tiki_article_types`.`show_pubdate`,
1506								`tiki_article_types`.`show_expdate`,
1507								`tiki_article_types`.`show_reads`,
1508								`tiki_article_types`.`show_size`,
1509								`tiki_article_types`.`show_topline`,
1510								`tiki_article_types`.`show_subtitle`,
1511								`tiki_article_types`.`show_linkto`,
1512								`tiki_article_types`.`show_image_caption`,
1513								`tiki_article_types`.`creator_edit`
1514						from `tiki_articles`
1515						left join `tiki_article_types` ON `tiki_articles`.`type` = `tiki_article_types`.`type`
1516						left join `users_users` on `tiki_articles`.`author` = `users_users`.`login`
1517						where `tiki_articles`.`articleId`=?"
1518						;
1519
1520		$result = $this->query($query, [(int)$articleId]);
1521		if ($result->numRows()) {
1522			$res = $result->fetchRow();
1523			$res['entrating'] = floor($res['rating']);
1524		} else {
1525			return '';
1526		}
1527		if ($checkPerms) {
1528			$perms = Perms::get('article', $articleId);
1529
1530			$permsok = $perms->admin_cms || $perms->read_article || $perms->articles_read_heading;
1531
1532			if (! $permsok) {
1533				return false;
1534			}
1535		}
1536
1537		if ($res['author'] != $user) {
1538			TikiLib::events()->trigger(
1539				'tiki.article.view',
1540				[
1541					'type' => 'article',
1542					'object' => $articleId,
1543					'user' => $user,
1544					'author' => $res['author'],
1545				]
1546			);
1547		}
1548
1549		return $res;
1550	}
1551
1552	function get_submission($subId)
1553	{
1554		$query = 'select * from `tiki_submissions` where `subId`=?';
1555		$result = $this->query($query, [(int) $subId]);
1556		if ($result->numRows()) {
1557			$res = $result->fetchRow();
1558			$res['entrating'] = floor($res['rating']);
1559		} else {
1560			return false;
1561		}
1562		return $res;
1563	}
1564
1565	function get_topic_image($topicId)
1566	{
1567		$query = 'select `image_name` ,`image_size`,`image_type`, `image_data` from `tiki_topics` where `topicId`=?';
1568		$result = $this->query($query, [(int) $topicId]);
1569		$res = $result->fetchRow();
1570		return $res;
1571	}
1572
1573	function get_article_image($id)
1574	{
1575		$query = 'select `image_name` ,`image_size`,`image_type`, `image_data` from `tiki_articles` where `articleId`=?';
1576		$result = $this->query($query, [(int) $id]);
1577		$res = $result->fetchRow();
1578		return $res;
1579	}
1580
1581	function add_article_type_attribute($artType, $attributeName)
1582	{
1583		$relationlib = TikiLib::lib('relation');
1584		$attributelib = TikiLib::lib('attribute');
1585
1586		$fullAttributeName = TikiFilter::get('attribute_type')->filter(trim('tiki.article.' . $attributeName));
1587		$relationId = $relationlib->add_relation('tiki.article.attribute', 'articletype', $artType, 'attribute', $fullAttributeName);
1588		if (! $relationId) {
1589			return 0;
1590		} else {
1591			$attributelib->set_attribute('relation', $relationId, 'tiki.relation.target', $attributeName);
1592			return $relationId;
1593		}
1594	}
1595
1596	function delete_article_type_attribute($artType, $relationId)
1597	{
1598		$relationlib = TikiLib::lib('relation');
1599		// double check relation is associated with article type before deleting
1600		$currentAttributes = $relationlib->get_relations_from('articletype', $artType, 'tiki.article.attribute');
1601		foreach ($currentAttributes as $att) {
1602			if ($att['relationId'] == $relationId) {
1603				$relationlib->remove_relation($att['relationId']);
1604			}
1605		}
1606		return true;
1607	}
1608
1609	function get_article_type_attributes($artType, $orderby = '')
1610	{
1611		$relationlib = TikiLib::lib('relation');
1612		$attributelib = TikiLib::lib('attribute');
1613
1614		$attributes = $relationlib->get_relations_from('articletype', $artType, 'tiki.article.attribute', $orderby);
1615		$ret = [];
1616		foreach ($attributes as $att) {
1617			$relationAtt = $attributelib->get_attributes('relation', $att['relationId']);
1618			if (isset($relationAtt['tiki.relation.target'])) {
1619				$ret[$relationAtt['tiki.relation.target']] = $att;
1620			}
1621		}
1622		return $ret;
1623	}
1624
1625	function set_article_attributes($articleId, $attributeArray, $isSubmission = false)
1626	{
1627		// expects attributeArray in the form of $key=>$val where $key is tiki.article.xxxx and $val is value
1628		$attributelib = TikiLib::lib('attribute');
1629		if ($isSubmission) {
1630			$type = 'submission';
1631		} else {
1632			$type = 'article';
1633		}
1634		$currentAtt = $this->get_article_attributes($articleId);
1635		foreach ($attributeArray as $name => $value) {
1636			if (! in_array($name, array_keys($currentAtt)) || $value != $currentAtt[$name]['value']) {
1637				$attributelib->set_attribute($type, $articleId, $name, $value);
1638			}
1639		}
1640		return true;
1641	}
1642
1643	function get_article_attributes($articleId, $isSubmission = false)
1644	{
1645		$attributelib = TikiLib::lib('attribute');
1646
1647		if ($isSubmission) {
1648			$type = 'submission';
1649		} else {
1650			$type = 'article';
1651		}
1652
1653		$allAttributes = $attributelib->get_attributes($type, $articleId);
1654		$ret = [];
1655		foreach ($allAttributes as $k => $att) {
1656			if (substr($k, 0, 13) == 'tiki.article.') {
1657				$ret[$k] = $att;
1658			}
1659		}
1660		return $ret;
1661	}
1662
1663	function transfer_attributes_from_submission($subId, $articleId)
1664	{
1665		$this->query(
1666			'UPDATE `tiki_object_attributes` set `type` = ?, `itemId` = ? where `type` = ? and `itemId` = ?',
1667			[ 'article', $articleId, 'submission', $subId ]
1668		);
1669	}
1670
1671	/**
1672	 * Get related articles using $freetaglib->get_similar()
1673	 *
1674	 * @param int $articleId
1675	 * @param int $maxResults
1676	 * @return array
1677	 */
1678	function get_related_articles($articleId, $maxResults = 5)
1679	{
1680		$freetaglib = TikiLib::lib('freetag');
1681		$relatedArticles = $freetaglib->get_similar('article', $articleId);
1682
1683		foreach ($relatedArticles as $key => $article) {
1684			$relatedArticles[$key]['articleId'] = $relatedId = str_replace('tiki-read_article.php?articleId=', '', $article['href']);
1685
1686			$relatedArticle = $this->get_article($relatedId);
1687
1688			// exclude articles from the list if they are not published or if no permission to view them
1689			if (! $relatedArticle || $relatedArticle['ispublished'] != 'y') {
1690				unset($relatedArticles[$key]);
1691			}
1692		}
1693
1694		$relatedArticles = array_splice($relatedArticles, 0, $maxResults);
1695
1696		return $relatedArticles;
1697	}
1698}
1699
1700$artlib = new ArtLib;
1701