PageRenderTime 69ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/qa-include/qa-app-format.php

https://github.com/begriffs/nomnom
PHP | 1710 lines | 1056 code | 403 blank | 251 comment | 205 complexity | 96be48e1a99b7f90390907eea8af522c MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /*
  3. Question2Answer (c) Gideon Greenspan
  4. http://www.question2answer.org/
  5. File: qa-include/qa-app-format.php
  6. Version: See define()s at top of qa-include/qa-base.php
  7. Description: Common functions for creating theme-ready structures from data
  8. This program is free software; you can redistribute it and/or
  9. modify it under the terms of the GNU General Public License
  10. as published by the Free Software Foundation; either version 2
  11. of the License, or (at your option) any later version.
  12. This program is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. GNU General Public License for more details.
  16. More about this license: http://www.question2answer.org/license.php
  17. */
  18. if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
  19. header('Location: ../');
  20. exit;
  21. }
  22. define('QA_PAGE_FLAGS_EXTERNAL', 1);
  23. define('QA_PAGE_FLAGS_NEW_WINDOW', 2);
  24. function qa_time_to_string($seconds)
  25. /*
  26. Return textual representation of $seconds
  27. */
  28. {
  29. if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  30. $seconds=max($seconds, 1);
  31. $scales=array(
  32. 31557600 => array( 'main/1_year' , 'main/x_years' ),
  33. 2629800 => array( 'main/1_month' , 'main/x_months' ),
  34. 604800 => array( 'main/1_week' , 'main/x_weeks' ),
  35. 86400 => array( 'main/1_day' , 'main/x_days' ),
  36. 3600 => array( 'main/1_hour' , 'main/x_hours' ),
  37. 60 => array( 'main/1_minute' , 'main/x_minutes' ),
  38. 1 => array( 'main/1_second' , 'main/x_seconds' ),
  39. );
  40. foreach ($scales as $scale => $phrases)
  41. if ($seconds>=$scale) {
  42. $count=floor($seconds/$scale);
  43. if ($count==1)
  44. $string=qa_lang($phrases[0]);
  45. else
  46. $string=qa_lang_sub($phrases[1], $count);
  47. break;
  48. }
  49. return $string;
  50. }
  51. function qa_post_is_by_user($post, $userid, $cookieid)
  52. /*
  53. Check if $post is by user $userid, or if post is anonymous and $userid not specified, then
  54. check if $post is by the anonymous user identified by $cookieid
  55. */
  56. {
  57. // In theory we should only test against NULL here, i.e. use isset($post['userid'])
  58. // but the risk of doing so is so high (if a bug creeps in that allows userid=0)
  59. // that I'm doing a tougher test. This will break under a zero user or cookie id.
  60. if (@$post['userid'] || $userid)
  61. return @$post['userid']==$userid;
  62. elseif (@$post['cookieid'])
  63. return strcmp($post['cookieid'], $cookieid)==0;
  64. return false;
  65. }
  66. function qa_userids_handles_html($useridhandles, $microformats=false)
  67. /*
  68. Return array which maps the ['userid'] and/or ['lastuserid'] in each element of
  69. $useridhandles to its HTML representation. For internal user management, corresponding
  70. ['handle'] and/or ['lasthandle'] are required in each element.
  71. */
  72. {
  73. require_once QA_INCLUDE_DIR.'qa-app-users.php';
  74. if (QA_FINAL_EXTERNAL_USERS) {
  75. $keyuserids=array();
  76. foreach ($useridhandles as $useridhandle) {
  77. if (isset($useridhandle['userid']))
  78. $keyuserids[$useridhandle['userid']]=true;
  79. if (isset($useridhandle['lastuserid']))
  80. $keyuserids[$useridhandle['lastuserid']]=true;
  81. }
  82. if (count($keyuserids))
  83. return qa_get_users_html(array_keys($keyuserids), true, qa_path_to_root(), $microformats);
  84. else
  85. return array();
  86. } else {
  87. $usershtml=array();
  88. foreach ($useridhandles as $useridhandle) {
  89. if (isset($useridhandle['userid']) && $useridhandle['handle'])
  90. $usershtml[$useridhandle['userid']]=qa_get_one_user_html($useridhandle['handle'], $microformats);
  91. if (isset($useridhandle['lastuserid']) && $useridhandle['lasthandle'])
  92. $usershtml[$useridhandle['lastuserid']]=qa_get_one_user_html($useridhandle['lasthandle'], $microformats);
  93. }
  94. return $usershtml;
  95. }
  96. }
  97. function qa_tag_html($tag, $microformats=false)
  98. /*
  99. Convert textual $tag to HTML representation
  100. */
  101. {
  102. if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  103. return '<span class="InlineTags Meta"><A HREF="'.qa_path_html('tag/'.$tag).'"'.($microformats ? ' rel="tag"' : '').'>'.qa_html($tag).'</A></span>';
  104. }
  105. function qa_category_path($navcategories, $categoryid)
  106. /*
  107. Given $navcategories retrieved for $categoryid from the database (using qa_db_category_nav_selectspec(...)),
  108. return an array of elements from $navcategories for the hierarchy down to $categoryid.
  109. */
  110. {
  111. $upcategories=array();
  112. for ($upcategory=@$navcategories[$categoryid]; isset($upcategory); $upcategory=@$navcategories[$upcategory['parentid']])
  113. $upcategories[$upcategory['categoryid']]=$upcategory;
  114. return array_reverse($upcategories, true);
  115. }
  116. function qa_category_path_html($navcategories, $categoryid)
  117. /*
  118. Given $navcategories retrieved for $categoryid from the database (using qa_db_category_nav_selectspec(...)),
  119. return some HTML that shows the category hierarchy down to $categoryid.
  120. */
  121. {
  122. $categories=qa_category_path($navcategories, $categoryid);
  123. $html='';
  124. foreach ($categories as $category)
  125. $html.=(strlen($html) ? ' / ' : '').qa_html($category['title']);
  126. return $html;
  127. }
  128. function qa_category_path_request($navcategories, $categoryid)
  129. /*
  130. Given $navcategories retrieved for $categoryid from the database (using qa_db_category_nav_selectspec(...)),
  131. return a Q2A request string that represents the category hierarchy down to $categoryid.
  132. */
  133. {
  134. $categories=qa_category_path($navcategories, $categoryid);
  135. $request='';
  136. foreach ($categories as $category)
  137. $request.=(strlen($request) ? '/' : '').$category['tags'];
  138. return $request;
  139. }
  140. function qa_ip_anchor_html($ip, $anchorhtml=null)
  141. /*
  142. Return HTML to use for $ip address, which links to appropriate page with $anchorhtml
  143. */
  144. {
  145. if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  146. if (!strlen($anchorhtml))
  147. $anchorhtml=qa_html($ip);
  148. return '<A HREF="'.qa_path_html('ip/'.$ip).'" TITLE="'.qa_lang_html_sub('main/ip_address_x', qa_html($ip)).'" CLASS="qa-ip-link">'.$anchorhtml.'</A>';
  149. }
  150. function qa_post_html_fields($post, $userid, $cookieid, $usershtml, $dummy, $options=array())
  151. /*
  152. Given $post retrieved from database, return array of mostly HTML to be passed to theme layer.
  153. $userid and $cookieid refer to the user *viewing* the page.
  154. $usershtml is an array of [user id] => [HTML representation of user] built ahead of time.
  155. $dummy is a placeholder (used to be $categories parameter but that's no longer needed)
  156. $options is an array which sets what is displayed (see qa_post_html_defaults() in qa-app-options.php)
  157. If something is missing from $post (e.g. ['content']), correponding HTML also omitted.
  158. */
  159. {
  160. if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  161. require_once QA_INCLUDE_DIR.'qa-app-updates.php';
  162. if (isset($options['blockwordspreg']))
  163. require_once QA_INCLUDE_DIR.'qa-util-string.php';
  164. $fields=array();
  165. $fields['raw']=$post;
  166. // Useful stuff used throughout function
  167. $postid=$post['postid'];
  168. $isquestion=($post['basetype']=='Q');
  169. $isanswer=($post['basetype']=='A');
  170. $isbyuser=qa_post_is_by_user($post, $userid, $cookieid);
  171. $anchor=urlencode(qa_anchor($post['basetype'], $postid));
  172. $elementid=isset($options['elementid']) ? $options['elementid'] : $anchor;
  173. $microformats=@$options['microformats'];
  174. $isselected=@$options['isselected'];
  175. // High level information
  176. $fields['hidden']=@$post['hidden'];
  177. $fields['tags']='ID="'.qa_html($elementid).'"';
  178. if ($microformats)
  179. $fields['classes']='hentry '.($isquestion ? 'question' : ($isanswer ? ($isselected ? 'answer answer-selected' : 'answer') : 'comment'));
  180. // Question-specific stuff (title, URL, tags, answer count, category)
  181. if ($isquestion) {
  182. if (isset($post['title'])) {
  183. $fields['url']=qa_q_path_html($postid, $post['title']);
  184. if (isset($options['blockwordspreg']))
  185. $post['title']=qa_block_words_replace($post['title'], $options['blockwordspreg']);
  186. $fields['title']=qa_html($post['title']);
  187. if ($microformats)
  188. $fields['title']='<SPAN CLASS="entry-title">'.$fields['title'].'</SPAN>';
  189. /*if (isset($post['score'])) // useful for setting match thresholds
  190. $fields['title'].=' <SMALL>('.$post['score'].')</SMALL>';*/
  191. }
  192. if (@$options['tagsview'] && isset($post['tags'])) {
  193. $fields['q_tags']=array();
  194. $tags=qa_tagstring_to_tags($post['tags']);
  195. foreach ($tags as $tag) {
  196. if (isset($options['blockwordspreg']) && count(qa_block_words_match_all($tag, $options['blockwordspreg']))) // skip censored tags
  197. continue;
  198. $fields['q_tags'][]=qa_tag_html($tag, $microformats);
  199. }
  200. }
  201. if (@$options['answersview'] && isset($post['acount'])) {
  202. $fields['answers_raw']=$post['acount'];
  203. $fields['answers']=($post['acount']==1) ? qa_lang_html_sub_split('main/1_answer', '1', '1')
  204. : qa_lang_html_sub_split('main/x_answers', number_format($post['acount']));
  205. $fields['answer_selected']=isset($post['selchildid']);
  206. }
  207. if (@$options['viewsview'] && isset($post['views'])) {
  208. $fields['views_raw']=$post['views'];
  209. $fields['views']=($post['views']==1) ? qa_lang_html_sub_split('main/1_view', '1', '1') :
  210. qa_lang_html_sub_split('main/x_views', number_format($post['views']));
  211. }
  212. if (@$options['categoryview'] && isset($post['categoryname']) && isset($post['categorybackpath']))
  213. $fields['where']=qa_lang_html_sub_split('main/in_category_x',
  214. '<A HREF="'.qa_path_html(@$options['categorypathprefix'].implode('/', array_reverse(explode('/', $post['categorybackpath'])))).
  215. '" CLASS="qa-category-link">'.qa_html($post['categoryname']).'</A>');
  216. }
  217. // Answer-specific stuff (selection)
  218. if ($isanswer) {
  219. $fields['selected']=$isselected;
  220. if ($isselected)
  221. $fields['select_text']=qa_lang_html('question/select_text');
  222. }
  223. // Post content
  224. if (@$options['contentview'] && !empty($post['content'])) {
  225. $viewer=qa_load_viewer($post['content'], $post['format']);
  226. $fields['content']=$viewer->get_html($post['content'], $post['format'], array(
  227. 'blockwordspreg' => @$options['blockwordspreg'],
  228. 'showurllinks' => @$options['showurllinks'],
  229. 'linksnewwindow' => @$options['linksnewwindow'],
  230. ));
  231. if ($microformats){
  232. //$fields['content']='<DIV CLASS="entry-content">'.$fields['content'].'</DIV>';
  233. $fields['content']= $fields['content'];
  234. }
  235. $fields['content']='<A NAME="'.qa_html($postid).'"></A>'.$fields['content'];
  236. // this is for backwards compatibility with any existing links using the old style of anchor
  237. // that contained the post id only (changed to be valid under W3C specifications)
  238. }
  239. // Voting stuff
  240. if (@$options['voteview']) {
  241. $voteview=$options['voteview'];
  242. // Calculate raw values and pass through
  243. $upvotes=(int)@$post['upvotes'];
  244. $downvotes=(int)@$post['downvotes'];
  245. $netvotes=(int)($upvotes-$downvotes);
  246. $fields['upvotes_raw']=$upvotes;
  247. $fields['downvotes_raw']=$downvotes;
  248. $fields['netvotes_raw']=$netvotes;
  249. // Create HTML versions...
  250. $upvoteshtml=qa_html($upvotes);
  251. $downvoteshtml=qa_html($downvotes);
  252. if ($netvotes>=1)
  253. $netvoteshtml='+'.qa_html($netvotes);
  254. elseif ($netvotes<=-1)
  255. $netvoteshtml='&ndash;'.qa_html(-$netvotes);
  256. else
  257. $netvoteshtml='0';
  258. // ...with microformats if appropriate
  259. if ($microformats) {
  260. $netvoteshtml.='<SPAN CLASS="votes-up"><SPAN CLASS="value-title" TITLE="'.$upvoteshtml.'"></SPAN></SPAN>'.
  261. '<SPAN CLASS="votes-down"><SPAN CLASS="value-title" TITLE="'.$downvoteshtml.'"></SPAN></SPAN>';
  262. $upvoteshtml='<SPAN CLASS="votes-up">'.$upvoteshtml.'</SPAN>';
  263. $downvoteshtml='<SPAN CLASS="votes-down">'.$downvoteshtml.'</SPAN>';
  264. }
  265. // Pass information on vote viewing
  266. // $voteview will be one of:
  267. // updown, updown-disabled-page, updown-disabled-level, updown-uponly-level
  268. // net, net-disabled-page, net-disabled-level, net-uponly-level
  269. $fields['vote_view']=(substr($voteview, 0, 6)=='updown') ? 'updown' : 'net';
  270. $fields['vote_on_page']=strpos($voteview, '-disabled-page') ? 'disabled' : 'enabled';
  271. $fields['upvotes_view']=($upvotes==1) ? qa_lang_html_sub_split('main/1_liked', $upvoteshtml, '1')
  272. : qa_lang_html_sub_split('main/x_liked', $upvoteshtml);
  273. $fields['downvotes_view']=($downvotes==1) ? qa_lang_html_sub_split('main/1_disliked', $downvoteshtml, '1')
  274. : qa_lang_html_sub_split('main/x_disliked', $downvoteshtml);
  275. $fields['netvotes_view']=(abs($netvotes)==1) ? qa_lang_html_sub_split('main/1_vote', $netvoteshtml, '1')
  276. : qa_lang_html_sub_split('main/x_votes', $netvoteshtml);
  277. // Voting buttons
  278. $fields['vote_tags']='ID="voting_'.qa_html($postid).'"';
  279. $onclick='onClick="return qa_vote_click(this);"';
  280. if ($fields['hidden']) {
  281. $fields['vote_state']='disabled';
  282. $fields['vote_up_tags']='TITLE="'.qa_lang_html($isanswer ? 'main/vote_disabled_hidden_a' : 'main/vote_disabled_hidden_q').'"';
  283. $fields['vote_down_tags']=$fields['vote_up_tags'];
  284. } elseif ($isbyuser) {
  285. $fields['vote_state']='disabled';
  286. $fields['vote_up_tags']='TITLE="'.qa_lang_html($isanswer ? 'main/vote_disabled_my_a' : 'main/vote_disabled_my_q').'"';
  287. $fields['vote_down_tags']=$fields['vote_up_tags'];
  288. } elseif (strpos($voteview, '-disabled-')) {
  289. $fields['vote_state']=(@$post['uservote']>0) ? 'voted_up_disabled' : ((@$post['uservote']<0) ? 'voted_down_disabled' : 'disabled');
  290. if (strpos($voteview, '-disabled-page'))
  291. $fields['vote_up_tags']='TITLE="'.qa_lang_html('main/vote_disabled_q_page_only').'"';
  292. else
  293. $fields['vote_up_tags']='TITLE="'.qa_lang_html('main/vote_disabled_level').'"';
  294. $fields['vote_down_tags']=$fields['vote_up_tags'];
  295. } elseif (@$post['uservote']>0) {
  296. $fields['vote_state']='voted_up';
  297. $fields['vote_up_tags']='TITLE="'.qa_lang_html('main/voted_up_popup').'" NAME="'.qa_html('vote_'.$postid.'_0_'.$elementid).'" '.$onclick;
  298. $fields['vote_down_tags']=' ';
  299. } elseif (@$post['uservote']<0) {
  300. $fields['vote_state']='voted_down';
  301. $fields['vote_up_tags']=' ';
  302. $fields['vote_down_tags']='TITLE="'.qa_lang_html('main/voted_down_popup').'" NAME="'.qa_html('vote_'.$postid.'_0_'.$elementid).'" '.$onclick;
  303. } else {
  304. $fields['vote_up_tags']='TITLE="'.qa_lang_html('main/vote_up_popup').'" NAME="'.qa_html('vote_'.$postid.'_1_'.$elementid).'" '.$onclick;
  305. if (strpos($voteview, '-uponly-level')) {
  306. $fields['vote_state']='up_only';
  307. $fields['vote_down_tags']='TITLE="'.qa_lang_html('main/vote_disabled_down').'"';
  308. } else {
  309. $fields['vote_state']='enabled';
  310. $fields['vote_down_tags']='TITLE="'.qa_lang_html('main/vote_down_popup').'" NAME="'.qa_html('vote_'.$postid.'_-1_'.$elementid).'" '.$onclick;
  311. }
  312. }
  313. }
  314. // Flag count
  315. if (@$options['flagsview'] && @$post['flagcount'])
  316. $fields['flags']=($post['flagcount']==1) ? qa_lang_html_sub_split('main/1_flag', '1', '1')
  317. : qa_lang_html_sub_split('main/x_flags', $post['flagcount']);
  318. // Created when and by whom
  319. $fields['meta_order']=qa_lang_html('main/meta_order'); // sets ordering of meta elements which can be language-specific
  320. if (@$options['whatview'] ) {
  321. $fields['what']=qa_lang_html($isquestion ? 'main/asked' : ($isanswer ? 'main/answered' : 'main/commented'));
  322. if (@$options['whatlink'] && !$isquestion)
  323. $fields['what_url']=qa_path_html(qa_request(), array('show' => $postid), null, null, qa_anchor($post['basetype'], $postid));
  324. }
  325. if (isset($post['created']) && @$options['whenview']) {
  326. $fields['when']=qa_when_to_html($post['created'], @$options['fulldatedays']);
  327. if ($microformats)
  328. $fields['when']['data']='<SPAN CLASS="published"><SPAN CLASS="value-title" TITLE="'.gmdate('Y-m-d\TH:i:sO', $post['created']).'"></SPAN>'.$fields['when']['data'].'</SPAN>';
  329. }
  330. if (@$options['whoview']) {
  331. $fields['who']=qa_who_to_html($isbyuser, @$post['userid'], $usershtml, @$options['ipview'] ? @$post['createip'] : null, $microformats);
  332. if (isset($post['points'])) {
  333. if (@$options['pointsview'])
  334. $fields['who']['points']=($post['points']==1) ? qa_lang_html_sub_split('main/1_point', '1', '1')
  335. : qa_lang_html_sub_split('main/x_points', qa_html(number_format($post['points'])));
  336. if (isset($options['pointstitle']))
  337. $fields['who']['title']=qa_get_points_title_html($post['points'], $options['pointstitle']);
  338. }
  339. if (isset($post['level']))
  340. $fields['who']['level']=qa_html(qa_user_level_string($post['level']));
  341. }
  342. if (@$options['avatarsize']>0) {
  343. if (QA_FINAL_EXTERNAL_USERS)
  344. $fields['avatar']=qa_get_external_avatar_html($post['userid'], $options['avatarsize'], false);
  345. else
  346. $fields['avatar']=qa_get_user_avatar_html(@$post['flags'], @$post['email'], @$post['handle'],
  347. @$post['avatarblobid'], @$post['avatarwidth'], @$post['avatarheight'], $options['avatarsize']);
  348. }
  349. // Updated when and by whom
  350. if (
  351. @$options['updateview'] && isset($post['updated']) &&
  352. (($post['updatetype']!=QA_UPDATE_SELECTED) || $isselected) && // only show selected change if it's still selected
  353. ( // otherwise check if one of these conditions is fulfilled...
  354. (!isset($post['created'])) || // ... we didn't show the created time (should never happen in practice)
  355. ($post['hidden'] && ($post['updatetype']==QA_UPDATE_VISIBLE)) || // ... the post was hidden as the last action
  356. (isset($post['closedbyid']) && ($post['updatetype']==QA_UPDATE_CLOSED)) || // ... the post was closed as the last action
  357. (abs($post['updated']-$post['created'])>300) || // ... or over 5 minutes passed between create and update times
  358. ($post['lastuserid']!=$post['userid']) // ... or it was updated by a different user
  359. )
  360. ) {
  361. switch ($post['updatetype']) {
  362. case QA_UPDATE_TYPE:
  363. case QA_UPDATE_PARENT:
  364. $langstring='main/moved';
  365. break;
  366. case QA_UPDATE_CATEGORY:
  367. $langstring='main/recategorized';
  368. break;
  369. case QA_UPDATE_VISIBLE:
  370. $langstring=$post['hidden'] ? 'main/hidden' : 'main/reshown';
  371. break;
  372. case QA_UPDATE_CLOSED:
  373. $langstring=isset($post['closedbyid']) ? 'main/closed' : 'main/reopened';
  374. break;
  375. case QA_UPDATE_TAGS:
  376. $langstring='main/retagged';
  377. break;
  378. case QA_UPDATE_SELECTED:
  379. $langstring='main/selected';
  380. break;
  381. default:
  382. $langstring='main/edited';
  383. break;
  384. }
  385. $fields['what_2']=qa_lang_html($langstring);
  386. if (@$options['whenview']) {
  387. $fields['when_2']=qa_when_to_html($post['updated'], @$options['fulldatedays']);
  388. if ($microformats)
  389. $fields['when_2']['data']='<SPAN CLASS="updated"><SPAN CLASS="value-title" TITLE="'.gmdate('Y-m-d\TH:i:sO', $post['updated']).'"></SPAN>'.$fields['when_2']['data'].'</SPAN>';
  390. }
  391. if (isset($post['lastuserid']) && @$options['whoview'])
  392. $fields['who_2']=qa_who_to_html(isset($userid) && ($post['lastuserid']==$userid), $post['lastuserid'], $usershtml, @$options['ipview'] ? $post['lastip'] : null, false);
  393. }
  394. // That's it!
  395. return $fields;
  396. }
  397. function qa_who_to_html($isbyuser, $postuserid, $usershtml, $ip=null, $microformats=false)
  398. /*
  399. Return array of split HTML (prefix, data, suffix) to represent author of post
  400. */
  401. {
  402. if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  403. if (isset($postuserid) && isset($usershtml[$postuserid])) {
  404. $whohtml=$usershtml[$postuserid];
  405. if ($microformats)
  406. $whohtml='<SPAN CLASS="vcard author">'.$whohtml.'</SPAN>';
  407. } elseif ($isbyuser)
  408. $whohtml=qa_lang_html('main/me');
  409. else {
  410. $whohtml=qa_lang_html('main/anonymous');
  411. if (isset($ip))
  412. $whohtml=qa_ip_anchor_html($ip, $whohtml);
  413. }
  414. return qa_lang_html_sub_split('main/by_x', $whohtml);
  415. }
  416. function qa_when_to_html($timestamp, $fulldatedays)
  417. /*
  418. Return array of split HTML (prefix, data, suffix) to represent unix $timestamp, with the full date shown if it's
  419. more than $fulldatedays ago
  420. */
  421. {
  422. if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  423. $interval=qa_opt('db_time')-$timestamp;
  424. if ( ($interval<0) || (isset($fulldatedays) && ($interval>(86400*$fulldatedays))) ) { // full style date
  425. $stampyear=date('Y', $timestamp);
  426. $thisyear=date('Y', qa_opt('db_time'));
  427. return array(
  428. 'data' => qa_html(strtr(qa_lang(($stampyear==$thisyear) ? 'main/date_format_this_year' : 'main/date_format_other_years'), array(
  429. '^day' => date((qa_lang('main/date_day_min_digits')==2) ? 'd' : 'j', $timestamp),
  430. '^month' => qa_lang('main/date_month_'.date('n', $timestamp)),
  431. '^year' => date((qa_lang('main/date_year_digits')==2) ? 'y' : 'Y', $timestamp),
  432. ))),
  433. );
  434. } else // ago-style date
  435. return qa_lang_html_sub_split('main/x_ago', qa_html(qa_time_to_string($interval)));
  436. }
  437. function qa_other_to_q_html_fields($question, $userid, $cookieid, $usershtml, $dummy, $options)
  438. /*
  439. Return array of mostly HTML to be passed to theme layer, to *link* to an answer, comment or edit on
  440. $question, as retrieved from database, with fields prefixed 'o' for the answer, comment or edit.
  441. $userid, $cookieid, $usershtml, $options are passed through to qa_post_html_fields().
  442. */
  443. {
  444. if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  445. require_once QA_INCLUDE_DIR.'qa-app-updates.php';
  446. $fields=qa_post_html_fields($question, $userid, $cookieid, $usershtml, null, $options);
  447. switch ($question['obasetype'].'-'.@$question['oupdatetype']) {
  448. case 'Q-':
  449. $langstring='main/asked';
  450. break;
  451. case 'Q-'.QA_UPDATE_VISIBLE:
  452. $langstring=$question['hidden'] ? 'main/hidden' : 'main/reshown';
  453. break;
  454. case 'Q-'.QA_UPDATE_CLOSED:
  455. $langstring=isset($question['closedbyid']) ? 'main/closed' : 'main/reopened';
  456. break;
  457. case 'Q-'.QA_UPDATE_TAGS:
  458. $langstring='main/retagged';
  459. break;
  460. case 'Q-'.QA_UPDATE_CATEGORY:
  461. $langstring='main/recategorized';
  462. break;
  463. case 'A-':
  464. $langstring='main/answered';
  465. break;
  466. case 'A-'.QA_UPDATE_SELECTED:
  467. $langstring='main/answer_selected';
  468. break;
  469. case 'A-'.QA_UPDATE_VISIBLE:
  470. $langstring=$question['ohidden'] ? 'main/hidden' : 'main/answer_reshown';
  471. break;
  472. case 'A-'.QA_UPDATE_CONTENT:
  473. $langstring='main/answer_edited';
  474. break;
  475. case 'Q-'.QA_UPDATE_FOLLOWS:
  476. $langstring='main/asked_related_q';
  477. break;
  478. case 'C-':
  479. $langstring='main/commented';
  480. break;
  481. case 'C-'.QA_UPDATE_TYPE:
  482. $langstring='main/comment_moved';
  483. break;
  484. case 'C-'.QA_UPDATE_VISIBLE:
  485. $langstring=$question['ohidden'] ? 'main/hidden' : 'main/comment_reshown';
  486. break;
  487. case 'C-'.QA_UPDATE_CONTENT:
  488. $langstring='main/comment_edited';
  489. break;
  490. case 'Q-'.QA_UPDATE_CONTENT:
  491. default:
  492. $langstring='main/edited';
  493. break;
  494. }
  495. $fields['what']=qa_lang_html($langstring);
  496. if ( ($question['obasetype']!='Q') || (@$question['oupdatetype']==QA_UPDATE_FOLLOWS) )
  497. $fields['what_url']=qa_q_path_html($question['postid'], $question['title'], false, $question['obasetype'], $question['opostid']);
  498. if (@$options['contentview'] && !empty($question['ocontent'])) {
  499. $viewer=qa_load_viewer($question['ocontent'], $question['oformat']);
  500. $fields['content']=$viewer->get_html($question['ocontent'], $question['oformat'], array(
  501. 'blockwordspreg' => @$options['blockwordspreg'],
  502. 'showurllinks' => @$options['showurllinks'],
  503. 'linksnewwindow' => @$options['linksnewwindow'],
  504. ));
  505. }
  506. if (@$options['whenview'])
  507. $fields['when']=qa_when_to_html($question['otime'], @$options['fulldatedays']);
  508. if (@$options['whoview']) {
  509. $isbyuser=qa_post_is_by_user(array('userid' => $question['ouserid'], 'cookieid' => @$question['ocookieid']), $userid, $cookieid);
  510. $fields['who']=qa_who_to_html($isbyuser, $question['ouserid'], $usershtml, @$options['ipview'] ? @$question['oip'] : null, false);
  511. if (isset($question['opoints'])) {
  512. if (@$options['pointsview'])
  513. $fields['who']['points']=($question['opoints']==1) ? qa_lang_html_sub_split('main/1_point', '1', '1')
  514. : qa_lang_html_sub_split('main/x_points', qa_html(number_format($question['opoints'])));
  515. if (isset($options['pointstitle']))
  516. $fields['who']['title']=qa_get_points_title_html($question['opoints'], $options['pointstitle']);
  517. }
  518. if (isset($question['olevel']))
  519. $fields['who']['level']=qa_html(qa_user_level_string($question['olevel']));
  520. }
  521. unset($fields['flags']);
  522. if (@$options['flagsview'] && @$question['oflagcount'])
  523. $fields['flags']=($question['oflagcount']==1) ? qa_lang_html_sub_split('main/1_flag', '1', '1')
  524. : qa_lang_html_sub_split('main/x_flags', $question['oflagcount']);
  525. unset($fields['avatar']);
  526. if (@$options['avatarsize']>0) {
  527. if (QA_FINAL_EXTERNAL_USERS)
  528. $fields['avatar']=qa_get_external_avatar_html($post['ouserid'], $options['avatarsize'], false);
  529. else
  530. $fields['avatar']=qa_get_user_avatar_html($question['oflags'], $question['oemail'], $question['ohandle'],
  531. $question['oavatarblobid'], $question['oavatarwidth'], $question['oavatarheight'], $options['avatarsize']);
  532. }
  533. return $fields;
  534. }
  535. function qa_any_to_q_html_fields($question, $userid, $cookieid, $usershtml, $dummy, $options)
  536. /*
  537. Based on the elements in $question, return HTML to be passed to theme layer to link
  538. to the question, or to an associated answer, comment or edit.
  539. */
  540. {
  541. if (isset($question['opostid']))
  542. $fields=qa_other_to_q_html_fields($question, $userid, $cookieid, $usershtml, null, $options);
  543. else
  544. $fields=qa_post_html_fields($question, $userid, $cookieid, $usershtml, null, $options);
  545. return $fields;
  546. }
  547. function qa_any_sort_by_date($questions)
  548. /*
  549. Each element in $questions represents a question and optional associated answer, comment or edit, as retrieved from database.
  550. Return it sorted by the date appropriate for each element, without removing duplicate references to the same question.
  551. */
  552. {
  553. if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  554. require_once QA_INCLUDE_DIR.'qa-util-sort.php';
  555. foreach ($questions as $key => $question) // collect information about action referenced by each $question
  556. $questions[$key]['sort']=-(isset($question['opostid']) ? $question['otime'] : $question['created']);
  557. qa_sort_by($questions, 'sort');
  558. return $questions;
  559. }
  560. function qa_any_sort_and_dedupe($questions)
  561. /*
  562. Each element in $questions represents a question and optional associated answer, comment or edit, as retrieved from database.
  563. Return it sorted by the date appropriate for each element, and keep only the first item related to each question.
  564. */
  565. {
  566. if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  567. require_once QA_INCLUDE_DIR.'qa-util-sort.php';
  568. foreach ($questions as $key => $question) { // collect information about action referenced by each $question
  569. if (isset($question['opostid'])) {
  570. $questions[$key]['_time']=$question['otime'];
  571. $questions[$key]['_type']=$question['obasetype'];
  572. $questions[$key]['_userid']=@$question['ouserid'];
  573. } else {
  574. $questions[$key]['_time']=$question['created'];
  575. $questions[$key]['_type']='Q';
  576. $questions[$key]['_userid']=$question['userid'];
  577. }
  578. $questions[$key]['sort']=-$questions[$key]['_time'];
  579. }
  580. qa_sort_by($questions, 'sort');
  581. $keepquestions=array(); // now remove duplicate references to same question
  582. foreach ($questions as $question) { // going in order from most recent to oldest
  583. $laterquestion=@$keepquestions[$question['postid']];
  584. if ((!isset($laterquestion)) || // keep this reference if there is no more recent one, or...
  585. (
  586. (@$laterquestion['oupdatetype']) && // the more recent reference was an edit
  587. (!@$question['oupdatetype']) && // this is not an edit
  588. ($laterquestion['_type']==$question['_type']) && // the same part (Q/A/C) is referenced here
  589. ($laterquestion['_userid']==$question['_userid']) && // the same user made the later edit
  590. (abs($laterquestion['_time']-$question['_time'])<300) // the edit was within 5 minutes of creation
  591. )
  592. )
  593. $keepquestions[$question['postid']]=$question;
  594. }
  595. return $keepquestions;
  596. }
  597. function qa_any_get_userids_handles($questions)
  598. /*
  599. Each element in $questions represents a question and optional associated answer, comment or edit, as retrieved from database.
  600. Return an array of elements (userid,handle) for the appropriate user for each element.
  601. */
  602. {
  603. $userids_handles=array();
  604. foreach ($questions as $question)
  605. if (isset($question['opostid']))
  606. $userids_handles[]=array(
  607. 'userid' => @$question['ouserid'],
  608. 'handle' => @$question['ohandle'],
  609. );
  610. else
  611. $userids_handles[]=array(
  612. 'userid' => @$question['userid'],
  613. 'handle' => @$question['handle'],
  614. );
  615. return $userids_handles;
  616. }
  617. function qa_html_convert_urls($html, $newwindow=false)
  618. /*
  619. Return $html with any URLs converted into links (with nofollow and in a new window if $newwindow)
  620. URL regular expressions can get crazy: http://internet.ls-la.net/folklore/url-regexpr.html
  621. So this is something quick and dirty that should do the trick in most cases
  622. */
  623. {
  624. if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  625. return substr(preg_replace('/([^A-Za-z0-9])((http|https|ftp):\/\/([^\s&<>"\'\.])+\.([^\s&<>"\']|&amp;)+)/i', '\1<A HREF="\2" rel="nofollow"'.($newwindow ? ' target="_blank"' : '').'>\2</A>', ' '.$html.' '), 1, -1);
  626. }
  627. function qa_url_to_html_link($url, $newwindow=false)
  628. /*
  629. Return HTML representation of $url (if it appears to be an URL), linked with nofollow and in a new window if $newwindow
  630. */
  631. {
  632. if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  633. if (is_numeric(strpos($url, '.'))) {
  634. $linkurl=$url;
  635. if (!is_numeric(strpos($linkurl, ':/')))
  636. $linkurl='http://'.$linkurl;
  637. return '<A HREF="'.qa_html($linkurl).'" rel="nofollow"'.($newwindow ? ' target="_blank"' : '').'>'.qa_html($url).'</A>';
  638. } else
  639. return qa_html($url);
  640. }
  641. function qa_insert_login_links($htmlmessage, $topage=null, $params=null)
  642. /*
  643. Return $htmlmessage with ^1...^6 substituted for links to log in or register or confirm email and come back to $topage with $params
  644. */
  645. {
  646. require_once QA_INCLUDE_DIR.'qa-app-users.php';
  647. $userlinks=qa_get_login_links(qa_path_to_root(), isset($topage) ? qa_path($topage, $params, '') : null);
  648. return strtr(
  649. $htmlmessage,
  650. array(
  651. '^1' => empty($userlinks['login']) ? '' : '<A HREF="'.qa_html($userlinks['login']).'">',
  652. '^2' => empty($userlinks['login']) ? '' : '</A>',
  653. '^3' => empty($userlinks['register']) ? '' : '<A HREF="'.qa_html($userlinks['register']).'">',
  654. '^4' => empty($userlinks['register']) ? '' : '</A>',
  655. '^5' => empty($userlinks['confirm']) ? '' : '<A HREF="'.qa_html($userlinks['confirm']).'">',
  656. '^6' => empty($userlinks['confirm']) ? '' : '</A>',
  657. )
  658. );
  659. }
  660. function qa_html_page_links($request, $start, $pagesize, $count, $prevnext, $params=array(), $hasmore=false, $anchor=null)
  661. /*
  662. Return structure to pass through to theme layer to show linked page numbers for $request.
  663. Q2A uses offset-based paging, i.e. pages are referenced in the URL by a 'start' parameter.
  664. $start is current offset, there are $pagesize items per page and $count items in total
  665. (unless $hasmore is true in which case there are at least $count items).
  666. Show links to $prevnext pages before and after this one and include $params in the URLs.
  667. */
  668. {
  669. if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  670. $thispage=1+floor($start/$pagesize);
  671. $lastpage=ceil(min($count, 1+QA_MAX_LIMIT_START)/$pagesize);
  672. if (($thispage>1) || ($lastpage>$thispage)) {
  673. $links=array('label' => qa_lang_html('main/page_label'), 'items' => array());
  674. $keypages[1]=true;
  675. for ($page=max(2, min($thispage, $lastpage)-$prevnext); $page<=min($thispage+$prevnext, $lastpage); $page++)
  676. $keypages[$page]=true;
  677. $keypages[$lastpage]=true;
  678. if ($thispage>1)
  679. $links['items'][]=array(
  680. 'type' => 'prev',
  681. 'label' => qa_lang_html('main/page_prev'),
  682. 'page' => $thispage-1,
  683. 'ellipsis' => false,
  684. );
  685. foreach (array_keys($keypages) as $page)
  686. $links['items'][]=array(
  687. 'type' => ($page==$thispage) ? 'this' : 'jump',
  688. 'label' => $page,
  689. 'page' => $page,
  690. 'ellipsis' => (($page<$lastpage) || $hasmore) && (!isset($keypages[$page+1])),
  691. );
  692. if ($thispage<$lastpage)
  693. $links['items'][]=array(
  694. 'type' => 'next',
  695. 'label' => qa_lang_html('main/page_next'),
  696. 'page' => $thispage+1,
  697. 'ellipsis' => false,
  698. );
  699. foreach ($links['items'] as $key => $link)
  700. if ($link['page']!=$thispage) {
  701. $params['start']=$pagesize*($link['page']-1);
  702. $links['items'][$key]['url']=qa_path_html($request, $params, null, null, $anchor);
  703. }
  704. } else
  705. $links=null;
  706. return $links;
  707. }
  708. function qa_html_suggest_qs_tags($usingtags=false, $categoryrequest=null)
  709. /*
  710. Return HTML that suggests browsing all questions (in the category specified by $categoryrequest, if
  711. it's not null) and also popular tags if $usingtags is true
  712. */
  713. {
  714. if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  715. $hascategory=strlen($categoryrequest);
  716. $htmlmessage=$hascategory ? qa_lang_html('main/suggest_category_qs') :
  717. ($usingtags ? qa_lang_html('main/suggest_qs_tags') : qa_lang_html('main/suggest_qs'));
  718. return strtr(
  719. $htmlmessage,
  720. array(
  721. '^1' => '<A HREF="'.qa_path_html('questions'.($hascategory ? ('/'.$categoryrequest) : '')).'">',
  722. '^2' => '</A>',
  723. '^3' => '<A HREF="'.qa_path_html('tags').'">',
  724. '^4' => '</A>',
  725. )
  726. );
  727. }
  728. function qa_html_suggest_ask($categoryid=null)
  729. /*
  730. Return HTML that suggest getting things started by asking a question, in $categoryid if not null
  731. */
  732. {
  733. if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  734. $htmlmessage=qa_lang_html('main/suggest_ask');
  735. return strtr(
  736. $htmlmessage,
  737. array(
  738. '^1' => '<A HREF="'.qa_path_html('ask', strlen($categoryid) ? array('cat' => $categoryid) : null).'">',
  739. '^2' => '</A>',
  740. )
  741. );
  742. }
  743. function qa_category_navigation($categories, $selectedid=null, $pathprefix='', $showqcount=true, $pathparams=null)
  744. /*
  745. Return the navigation structure for the category hierarchical menu, with $selectedid selected,
  746. and links beginning with $pathprefix, and showing question counts if $showqcount
  747. */
  748. {
  749. if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  750. $parentcategories=array();
  751. foreach ($categories as $category)
  752. $parentcategories[$category['parentid']][]=$category;
  753. $selecteds=qa_category_path($categories, $selectedid);
  754. return qa_category_navigation_sub($parentcategories, null, $selecteds, $pathprefix, $showqcount, $pathparams);
  755. }
  756. function qa_category_navigation_sub($parentcategories, $parentid, $selecteds, $pathprefix, $showqcount, $pathparams)
  757. /*
  758. Recursion function used by qa_category_navigation(...) to build hierarchical category menu.
  759. */
  760. {
  761. if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  762. $navigation=array();
  763. if (!isset($parentid))
  764. $navigation['all']=array(
  765. 'url' => qa_path_html($pathprefix, $pathparams),
  766. 'label' => qa_lang_html('main/all_categories'),
  767. 'selected' => !count($selecteds),
  768. 'categoryid' => null,
  769. );
  770. if (isset($parentcategories[$parentid]))
  771. foreach ($parentcategories[$parentid] as $category)
  772. $navigation[qa_html($category['tags'])]=array(
  773. 'url' => qa_path_html($pathprefix.$category['tags'], $pathparams),
  774. 'label' => qa_html($category['title']),
  775. 'popup' => qa_html(@$category['content']),
  776. 'selected' => isset($selecteds[$category['categoryid']]),
  777. 'note' => $showqcount ? ('('.qa_html(number_format($category['qcount'])).')') : null,
  778. 'subnav' => qa_category_navigation_sub($parentcategories, $category['categoryid'], $selecteds, $pathprefix.$category['tags'].'/', $showqcount, $pathparams),
  779. 'categoryid' => $category['categoryid'],
  780. );
  781. return $navigation;
  782. }
  783. function qa_users_sub_navigation()
  784. /*
  785. Return the sub navigation structure for user listing pages
  786. */
  787. {
  788. if ((!QA_FINAL_EXTERNAL_USERS) && (qa_get_logged_in_level()>=QA_USER_LEVEL_MODERATOR)) {
  789. return array(
  790. 'users$' => array(
  791. 'url' => qa_path_html('users'),
  792. 'label' => qa_lang_html('main/highest_users'),
  793. ),
  794. 'users/special' => array(
  795. 'label' => qa_lang('users/special_users'),
  796. 'url' => qa_path_html('users/special'),
  797. ),
  798. 'users/blocked' => array(
  799. 'label' => qa_lang('users/blocked_users'),
  800. 'url' => qa_path_html('users/blocked'),
  801. ),
  802. );
  803. } else
  804. return null;
  805. }
  806. function qa_account_sub_navigation()
  807. /*
  808. Return the sub navigation structure for user account pages
  809. */
  810. {
  811. return array(
  812. 'account' => array(
  813. 'label' => qa_lang_html('misc/nav_my_details'),
  814. 'url' => qa_path_html('account'),
  815. ),
  816. 'favorites' => array(
  817. 'label' => qa_lang_html('misc/nav_my_favorites'),
  818. 'url' => qa_path_html('favorites'),
  819. ),
  820. );
  821. }
  822. function qa_custom_page_url($page)
  823. /*
  824. Return the url for $page retrieved from the database
  825. */
  826. {
  827. return ($page['flags'] & QA_PAGE_FLAGS_EXTERNAL)
  828. ? (is_numeric(strpos($page['tags'], '://')) ? $page['tags'] : qa_path_to_root().$page['tags'])
  829. : qa_path($page['tags']);
  830. }
  831. function qa_navigation_add_page(&$navigation, $page)
  832. /*
  833. Add an element to the $navigation array corresponding to $page retrieved from the database
  834. */
  835. {
  836. if (
  837. (!qa_permit_value_error($page['permit'], qa_get_logged_in_userid(), qa_get_logged_in_level(), qa_get_logged_in_flags())) || !isset($page['permit'])
  838. ) {
  839. $url=qa_custom_page_url($page);
  840. $navigation[($page['flags'] & QA_PAGE_FLAGS_EXTERNAL) ? ('custom-'.$page['pageid']) : ($page['tags'].'$')]=array(
  841. 'url' => qa_html($url),
  842. 'label' => qa_html($page['title']),
  843. 'opposite' => ($page['nav']=='O'),
  844. 'target' => ($page['flags'] & QA_PAGE_FLAGS_NEW_WINDOW) ? '_blank' : null,
  845. 'selected' => ($page['flags'] & QA_PAGE_FLAGS_EXTERNAL) && ( ($url==qa_path(qa_request())) || ($url==qa_self_html()) ),
  846. );
  847. }
  848. }
  849. function qa_match_to_min_score($match)
  850. /*
  851. Convert an admin option for matching into a threshold for the score given by database search
  852. */
  853. {
  854. return 10-2*$match;
  855. }
  856. function qa_set_display_rules(&$qa_content, $effects)
  857. /*
  858. For each [target] => [source] in $effects, set up $qa_content so that the visibility of the DOM element ID
  859. target is equal to the checked state or boolean-casted value of the DOM element ID source. Each source can
  860. also combine multiple DOM IDs using JavaScript(=PHP) operators. This is twisted but rather convenient.
  861. */
  862. {
  863. $function='qa_display_rule_'.count(@$qa_content['script_lines']);
  864. $keysourceids=array();
  865. foreach ($effects as $target => $sources)
  866. if (preg_match_all('/[A-Za-z_][A-Za-z0-9_]*/', $sources, $matches)) // element names must be legal JS variable names
  867. foreach ($matches[0] as $element)
  868. $keysourceids[$element]=true;
  869. $funcscript=array("function ".$function."(first) {"); // build the Javascripts
  870. $loadscript=array();
  871. foreach ($keysourceids as $key => $dummy) {
  872. $funcscript[]="\tvar e=document.getElementById(".qa_js($key).");";
  873. $funcscript[]="\tvar ".$key."=e && (e.checked || (e.options && e.options[e.selectedIndex].value));";
  874. $loadscript[]="var e=document.getElementById(".qa_js($key).");";
  875. $loadscript[]="if (e) {";
  876. $loadscript[]="\t".$key."_oldonclick=e.onclick;";
  877. $loadscript[]="\te.onclick=function() {";
  878. $loadscript[]="\t\t".$function."(false);";
  879. $loadscript[]="\t\tif (typeof ".$key."_oldonclick=='function')";
  880. $loadscript[]="\t\t\t".$key."_oldonclick();";
  881. $loadscript[]="\t}";
  882. $loadscript[]="}";
  883. }
  884. foreach ($effects as $target => $sources) {
  885. $funcscript[]="\tvar e=document.getElementById(".qa_js($target).");";
  886. $funcscript[]="\tif (e) { var d=(".$sources."); if (first || (e.nodeName=='SPAN')) { e.style.display=d ? '' : 'none'; } else { if (d) { $(e).fadeIn(); } else { $(e).fadeOut(); } } }";
  887. }
  888. $funcscript[]="}";
  889. $loadscript[]=$function."(true);";
  890. $qa_content['script_lines'][]=$funcscript;
  891. $qa_content['script_onloads'][]=$loadscript;
  892. }
  893. function qa_set_up_tag_field(&$qa_content, &$field, $fieldname, $tags, $exampletags, $completetags, $maxtags)
  894. /*
  895. Set up $qa_content and $field (with HTML name $fieldname) for tag auto-completion, where
  896. $exampletags are suggestions and $completetags are simply the most popular ones. Show up to $maxtags.
  897. */
  898. {
  899. $template='<A HREF="#" CLASS="qa-tag-link" onClick="return qa_tag_click(this);">^</A>';
  900. $qa_content['script_rel'][]='qa-content/qa-ask.js?'.QA_VERSION;
  901. $qa_content['script_var']['qa_tag_template']=$template;
  902. $qa_content['script_var']['qa_tag_onlycomma']=(int)qa_opt('tag_separator_comma');
  903. $qa_content['script_var']['qa_tags_examples']=qa_html(implode(',', $exampletags));
  904. $qa_content['script_var']['qa_tags_complete']=qa_html(implode(',', $completetags));
  905. $qa_content['script_var']['qa_tags_max']=(int)$maxtags;
  906. $separatorcomma=qa_opt('tag_separator_comma');
  907. $field['label']=qa_lang_html($separatorcomma ? 'question/q_tags_comma_label' : 'question/q_tags_label');
  908. $field['value']=qa_html(implode($separatorcomma ? ', ' : ' ', $tags));
  909. $field['tags']='NAME="'.$fieldname.'" ID="tags" AUTOCOMPLETE="off" onKeyUp="qa_tag_hints();" onMouseUp="qa_tag_hints();"';
  910. $sdn=' STYLE="display:none;"';
  911. $field['note']=
  912. '<br /><SPAN ID="tag_examples_title"'.(count($exampletags) ? '' : $sdn).'>'.qa_lang_html('question/example_tags').'</SPAN>'.
  913. '<SPAN ID="tag_complete_title"'.$sdn.'>'.qa_lang_html('question/matching_tags').'</SPAN><SPAN ID="tag_hints" class="InlineTags Meta">';
  914. foreach ($exampletags as $tag)
  915. $field['note'].=str_replace('^', qa_html($tag), $template).' ';
  916. $field['note'].='</SPAN>';
  917. $field['note_force']=true;
  918. }
  919. function qa_get_tags_field_value($fieldname)
  920. /*
  921. Get a list of user-entered tags submitted from a field that was created with qa_set_up_tag_field(...)
  922. */
  923. {
  924. require_once QA_INCLUDE_DIR.'qa-util-string.php';
  925. $text=qa_post_text($fieldname);
  926. if (qa_opt('tag_separator_comma'))
  927. return array_unique(preg_split('/\s*,\s*/', trim(qa_strtolower(strtr($text, '/', ' '))), -1, PREG_SPLIT_NO_EMPTY));
  928. else
  929. return array_unique(qa_string_to_words($text, true, false, false, false));
  930. }
  931. function qa_set_up_category_field(&$qa_content, &$field, $fieldname, $navcategories, $categoryid, $allownone, $allownosub, $maxdepth=null, $excludecategoryid=null)
  932. /*
  933. Set up $qa_content and $field (with HTML name $fieldname) for hierarchical category navigation, with the initial value
  934. set to $categoryid (and $navcategories retrieved for $categoryid using qa_db_category_nav_selectspec(...)).
  935. If $allownone is true, it will allow selection of no category. If $allownosub is true, it will allow a category to be
  936. selected without selecting a subcategory within. Set $maxdepth to the maximum depth of category that can be selected
  937. (or null for no maximum) and $excludecategoryid to a category that should not be included.
  938. */
  939. {
  940. $pathcategories=qa_category_path($navcategories, $categoryid);
  941. $startpath='';
  942. foreach ($pathcategories as $category)
  943. $startpath.='/'.$category['categoryid'];
  944. if (!isset($maxdepth))
  945. $maxdepth=QA_CATEGORY_DEPTH;
  946. $maxdepth=min(QA_CATEGORY_DEPTH, $maxdepth);
  947. $qa_content['script_rel'][]='qa-content/qa-ask.js?'.QA_VERSION;
  948. $qa_content['script_onloads'][]='qa_category_select('.qa_js($fieldname).', '.qa_js($startpath).');';
  949. $qa_content['script_var']['qa_cat_exclude']=$excludecategoryid;
  950. $qa_content['script_var']['qa_cat_allownone']=(int)$allownone;
  951. $qa_content['script_var']['qa_cat_allownosub']=(int)$allownosub;
  952. $qa_content['script_var']['qa_cat_maxdepth']=$maxdepth;
  953. $field['type']='select';
  954. $field['tags']='NAME="'.$fieldname.'_0" ID="'.$fieldname.'_0" onChange="qa_category_select('.qa_js($fieldname).');"';
  955. $field['options']=array();
  956. // create the menu that will be shown if Javascript is disabled
  957. if ($allownone)
  958. $field['options']['']=qa_lang_html('main/no_category'); // this is also copied to first menu created by Javascript
  959. $keycategoryids=array();
  960. if ($allownosub) {
  961. $category=@$navcategories[$categoryid];
  962. $upcategory=$category;
  963. while (true) { // first get supercategories
  964. $upcategory=@$navcategories[$upcategory['parentid']];
  965. if (!isset($upcategory))
  966. break;
  967. $keycategoryids[$upcategory['categoryid']]=true;
  968. }
  969. $keycategoryids=array_reverse($keycategoryids, true);
  970. $depth=count($keycategoryids); // number of levels above
  971. if (isset($category)) {
  972. $depth++; // to count category itself
  973. foreach ($navcategories as $navcategory) // now get siblings and self
  974. if (!strcmp($navcategory['parentid'], $category['parentid']))
  975. $keycategoryids[$navcategory['categoryid']]=true;
  976. }
  977. if ($depth<$maxdepth)
  978. foreach ($navcategories as $navcategory) // now get children, if not too deep
  979. if (!strcmp($navcategory['parentid'], $categoryid))
  980. $keycategoryids[$navcategory['categoryid']]=true;
  981. } else {
  982. $haschildren=false;
  983. foreach ($navcategories as $navcategory) // check if it has any children
  984. if (!strcmp($navcategory['parentid'], $categoryid))
  985. $haschildren=true;
  986. if (!$haschildren)
  987. $keycategoryids[$categoryid]=true; // show this category if it has no children
  988. }
  989. foreach ($keycategoryids as $keycategoryid => $dummy)
  990. if (strcmp($keycategoryid, $excludecategoryid))
  991. $field['options'][$keycategoryid]=qa_category_path_html($navcategories, $keycategoryid);
  992. $field['value']=@$field['options'][$categoryid];
  993. $field['note']='<DIV ID="'.$fieldname.'_note"><NOSCRIPT STYLE="color:red;">'.qa_lang_html('question/category_js_note').'</NOSCRIPT></DIV>';
  994. }
  995. function qa_get_category_field_value($fieldname)
  996. /*
  997. Get the user-entered category id submitted from a field that was created with qa_set_up_category_field(...)
  998. */
  999. {
  1000. for ($level=QA_CATEGORY_DEPTH; $level>=1; $level--) {
  1001. $levelid=qa_post_text($fieldname.'_'.$level);
  1002. if (strlen($levelid))
  1003. return $levelid;
  1004. }
  1005. if (!isset($levelid)) { // no Javascript-generated menu was present so take original menu
  1006. $levelid=qa_post_text($fieldname.'_0');
  1007. if (strlen($levelid))
  1008. return $levelid;
  1009. }
  1010. return null;
  1011. }
  1012. function qa_set_up_notify_fields(&$qa_content, &$fields, $basetype, $login_email, $innotify, $inemail, $errors_email, $fieldprefix='')
  1013. /*
  1014. Set up $qa_content and add to $fields to allow user to set if they want to be notified regarding their post.
  1015. $basetype is 'Q', 'A' or 'C' for question, answer or comment. $login_email is the email of logged in user,
  1016. or null if this is an anonymous post. $innotify, $inemail and $errors_email are from previous submission/validation.
  1017. */
  1018. {
  1019. $fields['notify']=array(
  1020. 'tags' => 'NAME="'.$fieldprefix.'notify"',
  1021. 'type' => 'checkbox',
  1022. 'value' => qa_html($innotify),
  1023. );
  1024. switch ($basetype) {
  1025. case 'Q':
  1026. $labelaskemail=qa_lang_html('question/q_notify_email');
  1027. $labelonly=qa_lang_html('question/q_notify_label');
  1028. $labelgotemail=qa_lang_html('question/q_notify_x_label');
  1029. break;
  1030. case 'A':
  1031. $labelaskemail=qa_lang_html('question/a_notify_email');
  1032. $labelonly=qa_lang_html('question/a_notify_label');
  1033. $labelgotemail=qa_lang_html('question/a_notify_x_label');
  1034. break;
  1035. case 'C':
  1036. $labelaskemail=qa_lang_html('question/c_notify_email');
  1037. $labelonly=qa_lang_html('question/c_notify_label');
  1038. $labelgotemail=qa_lang_html('question/c_notify_x_label');
  1039. break;
  1040. }
  1041. if (empty($login_email)) {
  1042. $fields['notify']['label']=
  1043. '<SPAN ID="'.$fieldprefix.'email_shown">'.$labelaskemail.'</SPAN>'.
  1044. '<SPAN ID="'.$fieldprefix.'email_hidden" STYLE="display:none;">'.$labelonly.'</SPAN>';
  1045. $fields['notify']['tags'].=' ID="'.$fieldprefix.'notify" onclick="if (document.getElementById(\''.$fieldprefix.'notify\').checked) document.getElementById(\''.$fieldprefix.'email\').focus();"';
  1046. $fields['notify']['tight']=true;
  1047. $fields['email']=array(
  1048. 'id' => $fieldprefix.'email_display',
  1049. 'tags' => 'NAME="'.$fieldprefix.'email" ID="'.$fieldprefix.'email"',
  1050. 'value' => qa_html($inemail),
  1051. 'note' => qa_lang_html('question/notify_email_note'),
  1052. 'error' => qa_html($errors_email),
  1053. );
  1054. qa_set_display_rules($qa_content, array(
  1055. $fieldprefix.'email_display' => $fieldprefix.'notify',
  1056. $fieldprefix.'email_shown' => $fieldprefix.'notify',
  1057. $fieldprefix.'email_hidden' => '!'.$fieldprefix.'notify',
  1058. ));
  1059. } else {
  1060. $fields['notify']['label']=str_replace('^', qa_html($login_email), $labelgotemail);
  1061. }
  1062. }
  1063. function qa_get_site_theme()
  1064. /*
  1065. Return the theme that should be used for displaying the page
  1066. */
  1067. {
  1068. if (qa_

Large files files are truncated, but you can click here to view the full file