<?php
/*
 * bookmarks for NextGeneration CMS (http://ngcms.org/)
 * Copyright (C) 2010 Alexey N. Zhukov (http://digitalplace.ru)
 * http://digitalplace.ru
 *
 * code based on kt2k's plugin
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */
# protect against hack attempts
if (!defined('NGCMS')) die('HAL');

// Ensure ng-helpers is loaded
if (!function_exists('Plugins\\logger')) {
	$ngHelpersPath = __DIR__ . '/../ng-helpers/ng-helpers.php';
	if (file_exists($ngHelpersPath)) {
		require_once $ngHelpersPath;
	}
}

use function Plugins\{cache_get, cache_put, logger, sanitize, get_ip, str_limit, array_get, clamp};

add_act('index', 'bookmarks_view');
register_plugin_page('bookmarks', 'modify', 'bookmarks_t', 0);
register_plugin_page('bookmarks', '', 'bookmarksPage', 0);
global $lang;
LoadPluginLang('bookmarks', 'main', '', '', ':');
$bookmarks_script = '
<script type="text/javascript">
	function bookmarks(url, news, action, isFullNews) {
		var ajaxBookmarks = new sack();
		ajaxBookmarks.onShow("");
		ajaxBookmarks.onComplete = function() {
			if(ajaxBookmarks.response == "limit") {
				showToast("' . addslashes($lang['bookmarks:err_add_limit']) . '", {type: "warning", title: "' . addslashes($lang['bookmarks:msg_title']) . '"});
			}
			else if(ajaxBookmarks.response == "notlogged"){
				showToast("' . addslashes($lang['bookmarks:err_notlogged']) . '", {type: "error", title: "' . addslashes($lang['bookmarks:msg_title']) . '"});
			}
			else if(ajaxBookmarks.response == "err_add"){
				showToast("' . addslashes($lang['bookmarks:err_add']) . '", {type: "error", title: "' . addslashes($lang['bookmarks:msg_title']) . '"});
			} else {
				var elementObj = document.getElementById("bookmarks_" + news);
				if(elementObj) {
					elementObj.innerHTML = ajaxBookmarks.response;
				}
				if(ajaxBookmarks.response.indexOf("<!-- add -->") != -1){
					showToast("' . addslashes($lang['bookmarks:msg_add']) . '", {type: "success", title: "' . addslashes($lang['bookmarks:msg_title']) . '", timeout: 3000});
				}
				else if(ajaxBookmarks.response.indexOf("<!-- delete -->") != -1){
					showToast("' . addslashes($lang['bookmarks:msg_delete']) . '", {type: "success", title: "' . addslashes($lang['bookmarks:msg_title']) . '", timeout: 3000});
				}
			}
		};
		ajaxBookmarks.setVar("news", news);
		ajaxBookmarks.setVar("action", action);
		ajaxBookmarks.setVar("ajax", true);
		ajaxBookmarks.setVar("isFullNews", isFullNews ? "1" : "0");
		ajaxBookmarks.requestFile = url;
		ajaxBookmarks.method = "GET";
		ajaxBookmarks.runAJAX();
	}
</script>';
register_htmlvar('plain', $bookmarks_script);
$tpath = locatePluginTemplates(array(':bookmarks.css'), 'bookmarks', intval(pluginGetVariable('bookmarks', 'localsource')));
register_stylesheet($tpath['url::bookmarks.css'] . '/bookmarks.css');
/* declare variables to be global
 * bookmarksLoaded - flag is bookmarks already loaded
 * bookmarksList   - result of $mysql -> select
 */
global $bookmarksLoaded, $bookmarksList;
$bookmarksLoaded = 0;
$bookmarksList = array();
# generate links for add/remove bookmark
class BookmarksNewsFilter extends NewsFilter
{
	public function showNews($newsID, $SQLnews, &$tvars, $mode = [])
	{
		global $lang, $bookmarksLoaded, $bookmarksList, $userROW, $tpl, $mysql, $twig;
		# определяем тип отображения - короткая новость или полная
		# используем $mode['style'] для определения типа отображения
		$isFullNews = (isset($mode['style']) && $mode['style'] == 'full');
		$isShortNews = !$isFullNews;
		# определяем пути к шаблонам
		$tpath = locatePluginTemplates(array('add.remove.links.style', 'not.logged.links'), 'bookmarks', pluginGetVariable('bookmarks', 'localsource'));
		# если пользователь не авторизован
		if (!is_array($userROW)) {
			if (pluginGetVariable('bookmarks', 'counter')) {
				$tVars['counter'] = $mysql->result('SELECT COUNT(*) FROM ' . prefix . '_bookmarks WHERE news_id=' . $newsID);
				$tVars['counter'] = $tVars['counter'] ? $tVars['counter'] : '';
				$tVars['isFullNews'] = $isFullNews;
				$tVars['isShortNews'] = $isShortNews;
				$xg = $twig->loadTemplate($tpath['not.logged.links'] . 'not.logged.links.tpl');
				$tvars['vars']['plugin_bookmarks_news'] = $xg->render($tVars);
			} else {
				$tvars['vars']['plugin_bookmarks_news'] = '';
			}
			return;
		}
		# загружаем закладки пользователя
		if (!$bookmarksLoaded)
			bookmarks_sql();
		# проверяем, есть ли новость в закладках
		$found = 0;
		foreach ($bookmarksList as $brow) {
			if ($brow['id'] == $newsID) {
				$found = 1;
				break;
			}
		}
		# генерируем ссылку
		$link = generatePluginLink('bookmarks', 'modify', array(), array('news' => $newsID, 'action' => ($found ? 'delete' : 'add')));
		$url = generatePluginLink('bookmarks', 'modify');
		$tVars = array(
			'news' => $newsID,
			'action' => ($found ? 'delete' : 'add'),
			'link' => $link,
			'found' => $found,
			'url' => $url,
			'link_title' => ($found ? $lang['bookmarks:title_delete'] : $lang['bookmarks:title_add']),
			'isFullNews' => $isFullNews,
			'isShortNews' => $isShortNews
		);
		# генерируем счетчик
		if (pluginGetVariable('bookmarks', 'counter')) {
			$tVars['counter'] = $mysql->result('SELECT COUNT(*) FROM ' . prefix . '_bookmarks WHERE news_id=' . $newsID);
			$tVars['counter'] = $tVars['counter'] ? $tVars['counter'] : '';
		} else {
			$tVars['counter'] = '';
		}
		$xg = $twig->loadTemplate($tpath['add.remove.links.style'] . 'add.remove.links.style.tpl');
		$tvars['vars']['plugin_bookmarks_news'] = $xg->render($tVars);
	}
}
register_filter('news', 'bookmarks', new BookmarksNewsFilter);
# function for fetching SQL bookmarks data
function bookmarks_sql()
{
	global $mysql, $config, $userROW, $bookmarksLoaded, $bookmarksList;
	$bookmarksLoaded = 1;
	if ($userROW['id']) {
		$bookmarksList = $mysql->select("SELECT n.id, n.title, n.alt_name, n.catid, n.postdate, n.content FROM " . prefix . "_bookmarks AS b LEFT JOIN " . prefix . "_news n ON n.id = b.news_id WHERE b.user_id = " . db_squote($userROW['id']));
	}
}
class BookmarksCoreFilter extends CoreFilter
{
	function showUserMenu(&$tVars)
	{
		global $mysql, $userROW;
		if (!is_array($userROW)) return;
		$count = $mysql->result('SELECT COUNT(*) FROM ' . prefix . '_bookmarks WHERE user_id = ' . db_squote($userROW['id']));
		$tVars['p']['bookmarks'] = [
			'count' => $count ?: 0,
			'link'  => generatePluginLink('bookmarks', null)
		];
	}
}
register_filter('core.userMenu', 'bookmarks', new BookmarksCoreFilter);
# view bookmarks on sidebar
function bookmarks_view()
{
	global $template, $tpl, $lang, $mysql, $config, $parse, $userROW, $bookmarksLoaded, $bookmarksList, $twig;
	if (!pluginGetVariable('bookmarks', 'sidebar')) {
		$template['vars']['plugin_bookmarks'] = '';
		return;
	}
	$cacheKey = 'bookmarks:' . $config['theme'] . ':' . $config['default_lang'] . ':' . $userROW['id'];
	if (pluginGetVariable('bookmarks', 'cache')) {
		$cacheData = cache_get($cacheKey);
		if ($cacheData !== null) {
			$template['vars']['plugin_bookmarks'] = $cacheData;
			return;
		}
	}
	$tpath = locatePluginTemplates(array('entries', 'bookmarks'), 'bookmarks', pluginGetVariable('bookmarks', 'localsource'));
	$maxlength = clamp(intval(pluginGetVariable('bookmarks', 'maxlength')), 10, 500) ?: 100;
	if (!$bookmarksLoaded && pluginGetVariable('bookmarks', 'sidebar'))
		bookmarks_sql();
	$result = array();
	$count = 0;
	foreach ($bookmarksList as $row) {
		$count++;
		if ($count > intval(pluginGetVariable('bookmarks', 'max_sidebar'))) break;
		// Извлекаем первое изображение из BBCode [img]
		$image = '';
		if (preg_match('/\[img\=["\']?([^\]"\']+)["\']?[^\]]*\](?:[^\[]+)?\[\/img\]/i', $row['content'], $matches)) {
			$image = $matches[1];
			// Если путь относительный, добавляем базовый URL
			if (strpos($image, 'http') !== 0) {
				$image = $config['home_url'] . $image;
			}
		}
		$title = (strlen($row['title']) > $maxlength) ?
			substr(secure_html($row['title']), 0, $maxlength) . "..." :
			secure_html($row['title']);
		$result[] = array(
			'link' => newsGenerateLink($row),
			'title' => $title,
			'image' => $image // Только URL изображения или пустая строка
		);
	}
	if ((!$count) && pluginGetVariable('bookmarks', 'hide_empty')) {
		if (pluginGetVariable('bookmarks', 'cache')) {
			cache_put($cacheKey, ' ', pluginGetVariable('bookmarks', 'cacheExpire'));
		}
		$template['vars']['plugin_bookmarks'] = '';
		return;
	}
	$tVars = array(
		'tpl_url' => tpl_url,
		'entries' => $result,
		'bookmarks_page' => generatePluginLink('bookmarks', null),
		'count' => $count
	);
	$xt = $twig->loadTemplate($tpath['bookmarks'] . 'bookmarks.tpl');
	$output = $xt->render($tVars);
	if (pluginGetVariable('bookmarks', 'cache')) {
		cache_put($cacheKey, $output, pluginGetVariable('bookmarks', 'cacheExpire'));
	}
	logger('View sidebar: userID=' . $userROW['id'] . ', count=' . $count . ', IP=' . get_ip(), 'info', 'bookmarks.log');
	$template['vars']['plugin_bookmarks'] = $output;
}
# personal plugin pages for add/remove bookmarks
function bookmarks_t()
{
	global $mysql, $config, $userROW, $HTTP_REFERER, $SUPRESS_TEMPLATE_SHOW, $tpl, $lang, $bookmarksList, $bookmarksLoaded, $template, $twig, $handler;
	# news ID
	$newsID = clamp(intval(sanitize(array_get($_GET, 'news', 0), 'int')), 0, 999999999);
	$ajax = array_get($_GET, 'ajax', false);
	# при определении $isFullNews используйте:
	$isFullNews = intval(array_get($_GET, 'isFullNews', 0));
	# process bookmarks only for logged in users
	if (!is_array($userROW)) {
		if ($ajax) die('notlogged');
		# Redirect UNREG users far away :)
		header('Location: ' . $config['home_url'] . '');
		return;
	}
	if (!$bookmarksLoaded)
		$count_list = $mysql->result('SELECT COUNT(*) FROM ' . prefix . '_bookmarks WHERE user_id = ' . db_squote($userROW['id']));
	else
		$count_list = count($bookmarksList);
	# for return reverse action
	$action = '';
	# add action
	if ($_GET['action'] == 'add') {
		# check limits
		if (intval(pluginGetVariable('bookmarks', 'bookmarks_limit')) < $count_list + 1) {
			if ($ajax) die("limit");
			else {
				# determine paths for template files
				$tpath = locatePluginTemplates(array('bookmarks.page'), 'bookmarks', pluginGetVariable('bookmarks', 'localsource'));
				$tvars['vars']['all_bookmarks'] = '';
				$tvars['vars']['no_bookmarks'] = $lang['bookmarks:err_add_limit'];
				$tpl->template('bookmarks.page', $tpath['bookmarks.page']);
				$tpl->vars('bookmarks.page', $tvars);
				$template['vars']['mainblock'] = $tpl->show('bookmarks.page');
				return;
			}
		}
		# check that this news exists & we didn't bookmarked this news earlier
		if (
			count($mysql->select("SELECT id FROM " . prefix . "_news WHERE id = " . $newsID)) &&
			(!count($mysql->select("SELECT * FROM " . prefix . '_bookmarks WHERE user_id = ' . db_squote($userROW['id']) . " AND news_id=" . $newsID)))
		) {
			# ok, bookmark it
			$mysql->query("INSERT INTO `" . prefix . "_bookmarks` (`user_id`,`news_id`) VALUES (" . db_squote($userROW['id']) . "," . db_squote($newsID) . ")");
			$action = 'delete';
			logger('Add bookmark: userID=' . $userROW['id'] . ', newsID=' . $newsID . ', IP=' . get_ip(), 'info', 'bookmarks.log');
		} else die('err_add');
		# delete action
	} elseif ($_GET['action'] == 'delete') {
		$mysql->query("DELETE FROM `" . prefix . "_bookmarks` WHERE `user_id`=" . db_squote($userROW['id']) . " AND `news_id`=" . db_squote($newsID));
		$action = 'add';
		logger('Delete bookmark: userID=' . $userROW['id'] . ', newsID=' . $newsID . ', IP=' . get_ip(), 'info', 'bookmarks.log');
	}
	# if cache is activated - truncate cache file [ to clear cache ]
	if (pluginGetVariable('bookmarks', 'cache')) {
		$cacheKey = 'bookmarks:' . $config['theme'] . ':' . $config['default_lang'] . ':' . $userROW['id'];
		cache_put($cacheKey, '', pluginGetVariable('bookmarks', 'cacheExpire'));
	}
	# make redirection back if not-ajax mode
	if (!$ajax) {
		header("Location: " . ($HTTP_REFERER ? $HTTP_REFERER : $config['home_url']));
		return;
	}
	$SUPRESS_TEMPLATE_SHOW = 1;
	# generate link
	$link = generatePluginLink('bookmarks', 'modify', array(), array('news' => $newsID, 'action' => $action));
	$url = generatePluginLink('bookmarks', 'modify');
	# determine paths for template files
	$tpath = locatePluginTemplates(array('ajax.add.remove.links.style'), 'bookmarks', pluginGetVariable('bookmarks', 'localsource'));
	$tVars = array(
		'news' => $newsID,
		'action' => $action,
		'link' => $link,
		'url' => $url,
		'link_title' => ($action == 'delete' ? $lang['bookmarks:title_delete'] : $lang['bookmarks:title_add']),
		'isFullNews' => $isFullNews,
		'isShortNews' => !$isFullNews,
		'counter' => pluginGetVariable('bookmarks', 'counter') ? $mysql->result('SELECT COUNT(*) FROM ' . prefix . '_bookmarks WHERE news_id=' . $newsID) : ''
	);
	$isFullNews = isset($_GET['isFullNews']) ? intval($_GET['isFullNews']) : (isHandler('news:news') ? 1 : 0);
	$tVars['isFullNews'] = $isFullNews;
	# generate counter [if requested]
	if (pluginGetVariable('bookmarks', 'counter')) {
		$tVars['counter'] = $mysql->result('SELECT COUNT(*) FROM ' . prefix . '_bookmarks WHERE news_id=' . $newsID);
		$tVars['counter'] = $tVars['counter'] ? $tVars['counter'] : '';
	} else $tVars['counter'] = '';
	$xt = $twig->loadTemplate($tpath['ajax.add.remove.links.style'] . 'ajax.add.remove.links.style.tpl');
	header("Content-Type: text/html; charset=utf-8");
	echo $xt->render($tVars) . ($action == 'delete' ? '<!-- add -->' : '<!-- delete -->');
}
# personal plugin pages for display all user's bookmarks
function bookmarksPage()
{
	global $SYSTEM_FLAGS, $lang, $userROW, $bookmarksLoaded, $bookmarksList, $template, $config, $tpl, $twig, $mysql;
	if (!is_array($userROW)) {
		header('Location: ' . $config['home_url']);
		return;
	}
	if (!$bookmarksLoaded)
		bookmarks_sql();
	$tpath = locatePluginTemplates(array('bookmarks.page', 'news.short'), 'bookmarks', pluginGetVariable('bookmarks', 'localsource'));
	$SYSTEM_FLAGS['info']['title']['group'] = $lang['bookmarks:pp_title'];
	if (!count($bookmarksList)) {
		$template['vars']['mainblock'] = $lang['bookmarks:nobookmarks'];
		return;
	}
	include_once root . 'includes/news.php';
	load_extras('news');
	// Получаем ID новостей для фильтра
	$ids = array();
	foreach ($bookmarksList as $brow) {
		$ids[] = $brow['id'];
	}
	// Настройки для отображения новостей
	$callingParams = array(
		'style' => 'short',
		'plugin' => 'bookmarks',
		'extractEmbeddedItems' => true, // Включаем извлечение изображений
		'overrideTemplatePath' => pluginGetVariable('bookmarks', 'news_short') ? $tpath['news.short'] : null,
		'page' => isset($_GET['page']) ? intval($_GET['page']) : 1
	);
	$paginationParams = array(
		'pluginName' => 'bookmarks',
		'xparams' => array(),
		'params' => array(),
		'paginator' => array('page', 1, false)
	);
	// Фильтр по ID новостей
	$filter = array('DATA', 'ID', 'IN', $ids);
	// Получаем список новостей с изображениями
	$newslist = news_showlist($filter, $paginationParams, $callingParams);
	// Если нужно обработать данные перед выводом
	if (isset($newslist['data'])) {
		foreach ($newslist['data'] as &$news) {
			// Добавляем дополнительные данные, если нужно
			if (!isset($news['image'])) {
				// Извлекаем первое изображение из контента (BBCode)
				if (preg_match('/\[img\=["\']?([^\]"\']+)["\']?[^\]]*\](?:[^\[]+)?\[\/img\]/i', $news['content'], $matches)) {
					$news['image'] = $matches[1];
					if (strpos($news['image'], 'http') !== 0) {
						$news['image'] = $config['home_url'] . $news['image'];
					}
				} else {
					$news['image'] = tpl_url . '/img/img-none.png';
				}
			}
		}
	}
	$tVars = array(
		'all_bookmarks' => $newslist,
		'count' => count($bookmarksList),
		'tpl_url' => tpl_url
	);
	logger('View page: userID=' . $userROW['id'] . ', count=' . count($bookmarksList) . ', IP=' . get_ip(), 'info', 'bookmarks.log');
	$xt = $twig->loadTemplate($tpath['bookmarks.page'] . 'bookmarks.page.tpl');
	$template['vars']['mainblock'] = $xt->render($tVars);
}
