PageRenderTime 42ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/phpmyfaq/faq.php

http://github.com/thorsten/phpMyFAQ
PHP | 400 lines | 345 code | 32 blank | 23 comment | 28 complexity | 97177b3e03aada540ffad732c6626f03 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-2.1, LGPL-3.0
  1. <?php
  2. /**
  3. * Shows the page with the FAQ record and - when available - the user comments.
  4. *
  5. * This Source Code Form is subject to the terms of the Mozilla Public License,
  6. * v. 2.0. If a copy of the MPL was not distributed with this file, You can
  7. * obtain one at http://mozilla.org/MPL/2.0/.
  8. *
  9. * @package phpMyFAQ
  10. * @author Thorsten Rinne <thorsten@phpmyfaq.de>
  11. * @author Lars Tiedemann <larstiedemann@yahoo.de>
  12. * @copyright 2002-2021 phpMyFAQ Team
  13. * @license http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
  14. * @link https://www.phpmyfaq.de
  15. * @since 2002-08-27
  16. */
  17. use phpMyFAQ\Attachment\AttachmentException;
  18. use phpMyFAQ\Attachment\AttachmentFactory;
  19. use phpMyFAQ\Captcha;
  20. use phpMyFAQ\Comments;
  21. use phpMyFAQ\Date;
  22. use phpMyFAQ\Entity\CommentType;
  23. use phpMyFAQ\Faq\FaqPermission;
  24. use phpMyFAQ\Filter;
  25. use phpMyFAQ\Glossary;
  26. use phpMyFAQ\Helper\AttachmentHelper;
  27. use phpMyFAQ\Helper\CaptchaHelper;
  28. use phpMyFAQ\Helper\FaqHelper as HelperFaq;
  29. use phpMyFAQ\Helper\LanguageHelper;
  30. use phpMyFAQ\Helper\SearchHelper;
  31. use phpMyFAQ\Link;
  32. use phpMyFAQ\LinkVerifier;
  33. use phpMyFAQ\Rating;
  34. use phpMyFAQ\Relation;
  35. use phpMyFAQ\Search\SearchResultSet;
  36. use phpMyFAQ\Services;
  37. use phpMyFAQ\Strings;
  38. use phpMyFAQ\Tags;
  39. use phpMyFAQ\User\CurrentUser;
  40. use phpMyFAQ\Utils;
  41. use phpMyFAQ\Visits;
  42. if (!defined('IS_VALID_PHPMYFAQ')) {
  43. http_response_code(400);
  44. exit();
  45. }
  46. $captcha = new Captcha($faqConfig);
  47. $oGlossary = new Glossary($faqConfig);
  48. $faqTagging = new Tags($faqConfig);
  49. $faqRelation = new Relation($faqConfig);
  50. $faqRating = new Rating($faqConfig);
  51. $faqComment = new Comments($faqConfig);
  52. $markDown = new \ParsedownExtra();
  53. $faqHelper = new HelperFaq($faqConfig);
  54. $faqPermission = new FaqPermission($faqConfig);
  55. $attachmentHelper = new AttachmentHelper();
  56. if (is_null($user)) {
  57. $user = new CurrentUser($faqConfig);
  58. }
  59. $faqSearchResult = new SearchResultSet($user, $faqPermission, $faqConfig);
  60. $captcha->setSessionId($sids);
  61. if (!is_null($showCaptcha)) {
  62. $captcha->drawCaptchaImage();
  63. exit;
  64. }
  65. $currentCategory = $cat;
  66. $recordId = Filter::filterInput(INPUT_GET, 'id', FILTER_VALIDATE_INT);
  67. $solutionId = Filter::filterInput(INPUT_GET, 'solution_id', FILTER_VALIDATE_INT);
  68. // Get all data from the FAQ record
  69. if (0 === (int)$solutionId) {
  70. $faq->getRecord($recordId);
  71. } else {
  72. $faq->getRecordBySolutionId($solutionId);
  73. }
  74. if (isset($faq->faqRecord['id'])) {
  75. $recordId = $faq->faqRecord['id'];
  76. }
  77. try {
  78. $faqSession->userTracking('article_view', $recordId);
  79. } catch (Exception $e) {
  80. // @todo handle the exception
  81. }
  82. $faqVisits = new Visits($faqConfig);
  83. $faqVisits->logViews((int) $recordId);
  84. $question = $faq->getRecordTitle($recordId);
  85. if ($faqConfig->get('main.enableMarkdownEditor')) {
  86. $answer = $markDown->text($faq->faqRecord['content']);
  87. } else {
  88. $answer = $faqHelper->renderMarkupContent($faq->faqRecord['content']);
  89. }
  90. // Rewrite URL fragments
  91. $currentUrl = htmlspecialchars("//{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}", ENT_QUOTES, 'UTF-8');
  92. $answer = $faqHelper->rewriteUrlFragments($answer, $currentUrl);
  93. // Add Glossary entries for answers only
  94. $answer = $oGlossary->insertItemsIntoContent($answer);
  95. // Set the path of the current category
  96. $categoryName = $category->getPath($currentCategory, ' &raquo; ', true, '');
  97. $highlight = Filter::filterInput(INPUT_GET, 'highlight', FILTER_UNSAFE_RAW);
  98. if (
  99. !is_null($highlight) && $highlight != '/' && $highlight != '<' && $highlight != '>' && Strings::strlen(
  100. $highlight
  101. ) > 3
  102. ) {
  103. $highlight = str_replace("'", 'ยด', $highlight);
  104. $highlight = str_replace(['^', '.', '?', '*', '+', '{', '}', '(', ')', '[', ']'], '', $highlight);
  105. $highlight = preg_quote($highlight, '/');
  106. $searchItems = explode(' ', $highlight);
  107. foreach ($searchItems as $item) {
  108. if (Strings::strlen($item) > 2) {
  109. $question = Utils::setHighlightedString($question, $item);
  110. $answer = Utils::setHighlightedString($answer, $item);
  111. }
  112. }
  113. }
  114. $linkVerifier = new LinkVerifier($faqConfig);
  115. $linkArray = $linkVerifier->getUrlPool();
  116. if (isset($linkArray['href'])) {
  117. foreach (array_unique($linkArray['href']) as $_url) {
  118. $xpos = strpos($_url, 'index.php?action=faq');
  119. if (!($xpos === false)) {
  120. // Get the FaqHelper link title
  121. $matches = [];
  122. preg_match('/id=([\d]+)/ism', $_url, $matches);
  123. $_id = $matches[1];
  124. $_title = $faq->getRecordTitle($_id);
  125. $_link = substr($_url, $xpos + 9);
  126. if (strpos($_url, '&amp;') === false) {
  127. $_link = str_replace('&', '&amp;', $_link);
  128. }
  129. $oLink = new Link($faqConfig->getDefaultUrl() . $_link, $faqConfig);
  130. $oLink->itemTitle = $oLink->tooltip = $_title;
  131. $newFaqPath = $oLink->toString();
  132. $answer = str_replace($_url, $newFaqPath, $answer);
  133. }
  134. }
  135. }
  136. // List all faq attachments
  137. if ($faqConfig->get('records.disableAttachments') && 'yes' == $faq->faqRecord['active']) {
  138. try {
  139. $attList = AttachmentFactory::fetchByRecordId($faqConfig, $recordId);
  140. $answer .= $attachmentHelper->renderAttachmentList($attList);
  141. } catch (AttachmentException $e) {
  142. // handle exception
  143. }
  144. }
  145. // List all categories for this faq
  146. $htmlAllCategories = '';
  147. $multiCategories = $category->getCategoriesFromFaq($recordId);
  148. if (count($multiCategories) > 1) {
  149. foreach ($multiCategories as $multiCat) {
  150. $path = $category->getPath($multiCat['id'], ' &raquo; ', true, 'breadcrumb-related-categories');
  151. if ('' === trim($path)) {
  152. continue;
  153. }
  154. $htmlAllCategories .= $path;
  155. }
  156. }
  157. // Related FAQs
  158. $faqSearchResult->reviewResultSet(
  159. $faqRelation->getAllRelatedByQuestion(
  160. $faq->faqRecord['title'],
  161. $faq->faqRecord['keywords']
  162. )
  163. );
  164. $searchHelper = new SearchHelper($faqConfig);
  165. $relatedFaqs = $searchHelper->renderRelatedFaqs($faqSearchResult, $recordId);
  166. // Show link to edit the faq?
  167. $editThisEntry = '';
  168. if ($user->perm->hasPermission($user->getUserId(), 'edit_faq')) {
  169. $editThisEntry = sprintf(
  170. '<i aria-hidden="true" class="fa fa-pencil"></i> <a class="data" href="./admin/index.php?action=editentry&id=%d&lang=%s">%s</a>',
  171. $recordId,
  172. $lang,
  173. $PMF_LANG['ad_entry_edit_1'] . ' ' . $PMF_LANG['ad_entry_edit_2']
  174. );
  175. }
  176. // Is the faq expired?
  177. $expired = (date('YmdHis') > $faq->faqRecord['dateEnd']);
  178. // Number of comments
  179. $numComments = $faqComment->getNumberOfComments();
  180. // Does the user have the right to add a comment?
  181. if (
  182. (-1 === $user->getUserId() && !$faqConfig->get('records.allowCommentsForGuests')) ||
  183. ($faq->faqRecord['active'] === 'no') || ('n' === $faq->faqRecord['comment']) || $expired
  184. ) {
  185. $commentMessage = $PMF_LANG['msgWriteNoComment'];
  186. } else {
  187. $commentMessage = sprintf(
  188. '%s<a href="#" class="show-comment-form">%s</a>',
  189. $PMF_LANG['msgYouCan'],
  190. $PMF_LANG['msgWriteComment']
  191. );
  192. $template->parseBlock(
  193. 'mainPageContent',
  194. 'enableComments',
  195. [
  196. 'numberOfComments' => sprintf(
  197. '%d %s',
  198. isset($numComments[$recordId]) ? $numComments[$recordId] : 0,
  199. $PMF_LANG['ad_start_comments']
  200. ),
  201. ]
  202. );
  203. }
  204. $translationUrl = sprintf(
  205. str_replace(
  206. '%',
  207. '%%',
  208. Link::getSystemRelativeUri('index.php')
  209. ) . 'index.php?%saction=translate&amp;cat=%s&amp;id=%d&amp;srclang=%s',
  210. $sids,
  211. $currentCategory,
  212. $recordId,
  213. $lang
  214. );
  215. $availableLanguages = $faqConfig->getLanguage()->languageAvailable($faq->faqRecord['id']);
  216. if (!empty($availableLanguages) && count($availableLanguages) > 1) {
  217. $template->parseBlock(
  218. 'mainPageContent',
  219. 'switchLanguage',
  220. [
  221. 'msgChangeLanguage' => $PMF_LANG['msgLanguageSubmit'],
  222. ]
  223. );
  224. }
  225. if (
  226. $user->perm->hasPermission($user->getUserId(), 'addtranslation') &&
  227. !empty($availableLanguages) && count($availableLanguages) > 1
  228. ) {
  229. $template->parseBlock(
  230. 'mainPageContent',
  231. 'addTranslation',
  232. [
  233. 'msgTranslate' => $PMF_LANG['msgTranslate'],
  234. ]
  235. );
  236. }
  237. if ($user->perm->hasPermission($user->getUserId(), 'edit_faq') && !empty($faq->faqRecord['notes'])) {
  238. $template->parseBlock(
  239. 'mainPageContent',
  240. 'privateNotes',
  241. [
  242. 'notesHeader' => $PMF_LANG['ad_admin_notes'],
  243. 'notes' => $faq->faqRecord['notes']
  244. ]
  245. );
  246. }
  247. if ('-' !== $faqTagging->getAllLinkTagsById($recordId)) {
  248. $template->parseBlock(
  249. 'mainPageContent',
  250. 'tagsAvailable',
  251. [
  252. 'renderTags' => $PMF_LANG['msg_tags'] . ': ' . $faqTagging->getAllLinkTagsById($recordId),
  253. ]
  254. );
  255. }
  256. if ('' !== $htmlAllCategories) {
  257. $template->parseBlock(
  258. 'mainPageContent',
  259. 'relatedCategories',
  260. [
  261. 'renderRelatedCategoriesHeader' => $PMF_LANG['msgArticleCategories'],
  262. 'renderRelatedCategories' => $htmlAllCategories,
  263. ]
  264. );
  265. }
  266. if ('' !== $relatedFaqs) {
  267. $template->parseBlock(
  268. 'mainPageContent',
  269. 'relatedFaqs',
  270. [
  271. 'renderRelatedArticlesHeader' => $PMF_LANG['msg_related_articles'],
  272. 'renderRelatedArticles' => $relatedFaqs,
  273. ]
  274. );
  275. }
  276. $date = new Date($faqConfig);
  277. $captchaHelper = new CaptchaHelper($faqConfig);
  278. // We need some Links from social networks
  279. $faqServices = new Services($faqConfig);
  280. $faqServices->setCategoryId($cat);
  281. $faqServices->setFaqId($id);
  282. $faqServices->setLanguage($lang);
  283. $faqServices->setQuestion($faq->getRecordTitle($id));
  284. // Check if category ID and FAQ ID are linked together
  285. if (!$category->categoryHasLinkToFaq($recordId, $currentCategory)) {
  286. $http->setStatus(404);
  287. }
  288. // Check if author name should be visible according to GDPR option
  289. if ($user->getUserVisibilityByEmail($faq->faqRecord['email'])) {
  290. $author = $faq->faqRecord['author'];
  291. } else {
  292. $author = 'n/a';
  293. }
  294. $template->parse(
  295. 'mainPageContent',
  296. [
  297. 'baseHref' => $faqSystem->getSystemUri($faqConfig),
  298. 'solutionId' => $faq->faqRecord['solution_id'],
  299. 'solutionIdLink' => Link::getSystemRelativeUri() . '?solution_id=' . $faq->faqRecord['solution_id'],
  300. 'question' => $question,
  301. 'answer' => $answer,
  302. 'faqDate' => $date->format($faq->faqRecord['date']),
  303. 'faqAuthor' => $author,
  304. 'editThisEntry' => $editThisEntry,
  305. 'msgPdf' => $PMF_LANG['msgPDF'],
  306. 'msgPrintFaq' => $PMF_LANG['msgPrintArticle'],
  307. 'sendToFriend' => $faqHelper->renderSendToFriend($faqServices->getSuggestLink()),
  308. 'shareOnTwitter' => $faqHelper->renderTwitterShareLink($faqServices->getShareOnTwitterLink()),
  309. 'linkToPdf' => $faqServices->getPdfLink(),
  310. 'translationUrl' => $translationUrl,
  311. 'languageSelection' => LanguageHelper::renderSelectLanguage(
  312. $faqLangCode,
  313. false,
  314. $availableLanguages,
  315. 'translation'
  316. ),
  317. 'msgTranslateSubmit' => $PMF_LANG['msgTranslateSubmit'],
  318. 'saveVotingPATH' => sprintf(
  319. str_replace(
  320. '%',
  321. '%%',
  322. $faqConfig->getDefaultUrl()
  323. ) . 'index.php?%saction=savevoting',
  324. $sids
  325. ),
  326. 'saveVotingID' => $recordId,
  327. 'saveVotingIP' => $_SERVER['REMOTE_ADDR'],
  328. 'msgAverageVote' => $PMF_LANG['msgAverageVote'],
  329. 'renderVotingStars' => '',
  330. 'printVotings' => $faqRating->getVotingResult($recordId),
  331. 'switchLanguage' => $faqHelper->renderChangeLanguageSelector($faq, $currentCategory),
  332. 'msgVoteUsability' => $PMF_LANG['msgVoteUsability'],
  333. 'msgVoteBad' => $PMF_LANG['msgVoteBad'],
  334. 'msgVoteGood' => $PMF_LANG['msgVoteGood'],
  335. 'msgVoteSubmit' => $PMF_LANG['msgVoteSubmit'],
  336. 'writeCommentMsg' => $commentMessage,
  337. 'msgWriteComment' => $PMF_LANG['msgWriteComment'],
  338. 'id' => $recordId,
  339. 'lang' => $lang,
  340. 'msgCommentHeader' => $PMF_LANG['msgCommentHeader'],
  341. 'msgNewContentName' => $PMF_LANG['msgNewContentName'],
  342. 'msgNewContentMail' => $PMF_LANG['msgNewContentMail'],
  343. 'defaultContentMail' => ($user instanceof CurrentUser) ? $user->getUserData('email') : '',
  344. 'defaultContentName' => ($user instanceof CurrentUser) ? $user->getUserData('display_name') : '',
  345. 'msgYourComment' => $PMF_LANG['msgYourComment'],
  346. 'msgNewContentSubmit' => $PMF_LANG['msgNewContentSubmit'],
  347. 'captchaFieldset' => $captchaHelper->renderCaptcha($captcha, 'writecomment', $PMF_LANG['msgCaptcha'], $auth),
  348. 'renderComments' => $faqComment->getComments($recordId, CommentType::FAQ),
  349. 'msg_about_faq' => $PMF_LANG['msg_about_faq'],
  350. ]
  351. );
  352. $template->parseBlock(
  353. 'index',
  354. 'breadcrumb',
  355. [
  356. 'breadcrumbHeadline' => $categoryName
  357. ]
  358. );