A fork of the NoNonsenseForum, we use as bugtracker http://camendesign.com/nononsense_forum https://github.com/Kroc/NoNonsenseForum
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

313 lines
17KB

  1. <?php //display the index of threads in a folder
  2. /* ====================================================================================================================== */
  3. /* NoNonsense Forum v26 © Copyright (CC-BY) Kroc Camen 2010-2015
  4. licenced under Creative Commons Attribution 3.0 <creativecommons.org/licenses/by/3.0/deed.en_GB>
  5. you may do whatever you want to this code as long as you give credit to Kroc Camen, <camendesign.com>
  6. */
  7. //bootstrap the forum; you should read that file first
  8. require_once './start.php';
  9. //submitted info for making a new thread
  10. //(name / password already handled in 'start.php')
  11. define ('TITLE', mb_substr (@$_POST['title'], 0, SIZE_TITLE));
  12. define ('TEXT', mb_substr (@$_POST['text'], 0, SIZE_TEXT ));
  13. //can the current user post new threads in the current forum?
  14. //(posting replies is dependent on the the thread -- if locked -- so tested in 'thread.php')
  15. define ('CAN_POST', FORUM_ENABLED && (
  16. //- if the user is a moderator or member of the current forum, they can post
  17. IS_MOD || IS_MEMBER ||
  18. //- if the forum is unlocked (mods will have to log in to see the form)
  19. !FORUM_LOCK
  20. ));
  21. /* ======================================================================================================================
  22. new thread submitted
  23. ====================================================================================================================== */
  24. //has the user submitted a new thread?
  25. //(`AUTH` will be true if username and password submitted and correct, `TITLE` and `TEXT` are checked to not be blank)
  26. if (CAN_POST && AUTH && TITLE && TEXT) {
  27. //the file on disk is a simplified version of the title; see 'lib/functions.php' for `safeTransliterate`
  28. $translit = safeTransliterate (TITLE);
  29. //if a thread already exsits with that name, append a number until an available filename is found.
  30. //we also check for directories with the same name so as to avoid problematic Apache behaviour
  31. $c = 0; do $file = $translit.($c++ ? '_'.($c-1) : '');
  32. while (file_exists ("$file") || file_exists ("$file.rss"));
  33. //write out the new thread as an RSS file:
  34. $post_id = base_convert (microtime (), 10, 36);
  35. $rss = new DOMTemplate (file_get_contents (FORUM_LIB.'rss-template.xml'));
  36. $rss->set (array (
  37. '/rss/channel/title' => TITLE,
  38. '/rss/channel/link' => FORUM_URL.url (PATH_URL, $file),
  39. //the thread's first post
  40. '/rss/channel/item/title' => TITLE,
  41. '/rss/channel/item/link' => FORUM_URL.url (PATH_URL, $file).'#'.$post_id,
  42. '/rss/channel/item/author' => NAME,
  43. '/rss/channel/item/pubDate' => gmdate ('r'),
  44. '/rss/channel/item/description' => formatText (TEXT, //process markup into HTML...
  45. //provide a permalink so that title lines link to themselves
  46. FORUM_URL.url (PATH_URL, $file, 1),
  47. //also provide the post ID for title-linking and ID-uniqueness
  48. $post_id
  49. )
  50. //remove the locked / deleted categories
  51. ))->remove ('//category');
  52. file_put_contents ("$file.rss", $rss) or require FORUM_LIB.'error_permissions.php';
  53. //regenerate the folder's RSS file
  54. indexRSS ();
  55. //redirect to newley created thread
  56. header ('Location: '.FORUM_URL.url (PATH_URL, $file), true, 303);
  57. exit;
  58. }
  59. /* ======================================================================================================================
  60. template the page
  61. ====================================================================================================================== */
  62. //first load the list of threads in the forum so that we can determine the number of pages and validate the page number,
  63. //the thread list won't be used until further down after templating begins
  64. if ($threads = preg_grep ('/\.rss$/', scandir ('.'))) {
  65. //sort the list of threads in the forum
  66. array_multisort (array_map (
  67. //if the forum is set to "news" lock, we will order the threads by their original date
  68. (FORUM_LOCK == 'news') ? 'filectime' : 'filemtime',
  69. $threads), SORT_DESC, $threads);
  70. //get sticky list (see 'lib/functions.php')
  71. //(the use of `array_intersect` will only return filenames in `sticky.txt` that were also in the directory)
  72. $stickies = array_intersect (getStickies (), $threads);
  73. //order the stickies
  74. array_multisort (array_map (
  75. //if the forum is set to "news" lock, we will order the threads by their original date
  76. (FORUM_LOCK == 'news') ? 'filectime' : 'filemtime',
  77. $stickies), SORT_DESC, $stickies);
  78. //remove the stickies from the thread list
  79. $threads = array_diff ($threads, $stickies);
  80. //handle a rounding problem with working out the number of pages (PHP 5.3 has a fix for this)
  81. $PAGES = count ($threads) % FORUM_THREADS == 1 ? floor (count ($threads) / FORUM_THREADS)
  82. : ceil (count ($threads) / FORUM_THREADS);
  83. //validate the given page number; an invalid page number returns the first instead
  84. $PAGE = !PAGE || PAGE > $PAGES ? 1 : PAGE;
  85. } else {
  86. $PAGES = 1; $PAGE = 1;
  87. }
  88. /* load the template into DOM where we can manipulate it:
  89. ---------------------------------------------------------------------------------------------------------------------- */
  90. //(see 'lib/domtemplate.php' or <camendesign.com/dom_templating> for more details. `prepareTemplate` can be found in
  91. // 'lib/functions.php' and handles some shared templating done across all pages)
  92. $template = prepareTemplate (
  93. THEME_ROOT.'index.html',
  94. //the canonical URL of this page
  95. url (PATH_URL, '', $PAGE),
  96. //the HTML title is both templated and translatable. `THEME_TITLE` is defined in 'start.php' and is a shorthand to
  97. //either the default language string in 'theme.php' or the translated string in 'lang.*.php'
  98. sprintf (THEME_TITLE,
  99. //if in a sub-forum use the folder name, else the site's name
  100. PATH ? SUBFORUM : FORUM_NAME,
  101. //if on page 2 or greater, include the page number in the title
  102. $PAGE>1 ? sprintf (THEME_TITLE_PAGENO, $PAGE) : ''
  103. )
  104. )->setValue (
  105. //the RSS feed for this forum / sub-forum
  106. 'a#nnf_rss@href', FORUM_PATH.PATH_URL.'index.xml'
  107. )->remove (array (
  108. //if threads can't be added (forum is disabled / locked, user is not moderator / member),
  109. //remove the "add thread" link and anything else (like the input form) related to posting
  110. '#nnf_add, #nnf_new-form' => !CAN_POST,
  111. //if the forum is not thread-locked (only mods can post, anybody can reply) then remove the warning message
  112. '#nnf_forum-lock-threads' => !FORUM_LOCK || FORUM_LOCK == 'posts' || IS_MOD,
  113. //if the forum is not post-locked (only mods can post / reply) then remove the warning message
  114. '#nnf_forum-lock-posts' => FORUM_LOCK != 'posts' || IS_MOD || IS_MEMBER
  115. ));
  116. //an 'about.html' file can be provided to add a description or other custom HTML to the forum / sub-forum,
  117. //for translations, 'about_en.html' can be used where 'en' is the language code for the translation
  118. //(see 'lang.example.php' in the themes folder for more details on translation)
  119. if ($about = @array_shift (array_filter (array (
  120. @file_get_contents ('about_'.LANG.'.html'), @file_get_contents ('about.html')
  121. )))) {
  122. //load the 'about.html' file and insert it into the page
  123. $template->setValue ('#nnf_about', $about, true);
  124. } else {
  125. //no file? remove the element reserved for it
  126. $template->remove ('#nnf_about');
  127. }
  128. /* sub-forums
  129. ---------------------------------------------------------------------------------------------------------------------- */
  130. if ($folders = array_filter (
  131. //get a list of folders:
  132. //include only directories, but ignore directories starting with ‘.’ and the users / themes folders
  133. //TODO: need to do this check in a way that allows user expansion
  134. preg_grep ('/^(\.|users$|themes$|lib$|cgi-bin$)/', scandir ('.'), PREG_GREP_INVERT), 'is_dir'
  135. )) {
  136. //get the dummy list-item to repeat (removes it and takes a copy)
  137. $item = $template->repeat ('.nnf_folder');
  138. foreach ($folders as $FOLDER) {
  139. //the sorting (below) requires we be in the directory at hand to use `filemtime`
  140. chdir ($FOLDER);
  141. //check if / how the forum is locked
  142. $lock = trim (@file_get_contents ('locked.txt'));
  143. //get a list of files in the folder to determine which one is newest
  144. $files = preg_grep ('/\.rss$/', scandir ('.'));
  145. //order by last modified date / created date ("news" forum)
  146. array_multisort (array_map (
  147. ($lock == 'news') ? 'filectime' : 'filemtime',
  148. $files), SORT_DESC, $files);
  149. //read the newest thread (folder could be empty though)
  150. $last = ($xml = @simplexml_load_file ($files[0])) ? $xml->channel->item[0] : '';
  151. //start applying the data to the template
  152. $item->set (array (
  153. 'a.nnf_folder-name' => $FOLDER,
  154. 'a.nnf_folder-name@href' => url (PATH_URL.safeURL ($FOLDER).'/')
  155. //remove the lock icons if not required
  156. ))->remove (array (
  157. '.nnf_lock-threads' => $lock != 'threads',
  158. '.nnf_lock-posts' => $lock != 'posts'
  159. ));
  160. //is there a last post in this sub-forum?
  161. if ((bool) $last) {
  162. $item->set (array (
  163. //last post author name
  164. '.nnf_post-author' => $last->author,
  165. //last post time (human readable)
  166. 'time.nnf_post-time' => date (DATE_FORMAT, strtotime ($last->pubDate)),
  167. //last post time (machine readable)
  168. 'time.nnf_post-time@datetime' => date ('c', strtotime ($last->pubDate)),
  169. //link to the last post
  170. 'a.nnf_post-link@href' => substr ($last->link, strpos ($last->link, '/', 9))
  171. ))->remove (array (
  172. //is the last author a mod?
  173. '.nnf_post-author@class' => isMod ($last->author) ? false : 'nnf_mod'
  174. ));
  175. } else {
  176. //no last post, remove the template for it
  177. $item->remove ('.nnf_subforum-post');
  178. }
  179. //attach the templated sub-forum item to the list
  180. $item->next ();
  181. chdir ('..');
  182. }
  183. } else {
  184. //no sub-forums, remove the template stuff
  185. $template->remove ('#nnf_folders');
  186. }
  187. /* threads
  188. ---------------------------------------------------------------------------------------------------------------------- */
  189. if ($threads || @$stickies) {
  190. //do the page links (stickies are not included in the count as they appear on all pages)
  191. theme_pageList ($template, '', $PAGE, $PAGES);
  192. //slice the full list into the current page
  193. $threads = array_merge ($stickies, array_slice ($threads, ($PAGE-1) * FORUM_THREADS, FORUM_THREADS));
  194. //get the dummy list-item to repeat (removes it and takes a copy)
  195. $item = $template->repeat ('.nnf_thread');
  196. //generate the list of threads with data, for the template
  197. foreach ($threads as $file) if (
  198. //read the file, and refer to the last post made
  199. $xml = @simplexml_load_file ($file)
  200. ) if ( //get the last post in the thread
  201. $last = &$xml->channel->item[0]
  202. //apply the data to the template
  203. )
  204. $item->set (array (
  205. //thread title and URL
  206. 'a.nnf_thread-name' => $xml->channel->title . " (by " . $xml->channel->item[count($xml->channel->item)-1]->author . ")" ,
  207. 'a.nnf_thread-name@href' => url (PATH_URL, pathinfo ($file, PATHINFO_FILENAME)),
  208. //number of replies
  209. '.nnf_thread-replies' => count ($xml->channel->item) - 1,
  210. //last post info:
  211. //link to the last post
  212. 'a.nnf_thread-post@href' => substr ($last->link, strpos ($last->link, '/', 9)),
  213. //last post time (human readable)
  214. 'time.nnf_thread-time' => date (DATE_FORMAT, strtotime ($last->pubDate)),
  215. //last post time (machine readable)
  216. 'time.nnf_thread-time@datetime' => date ('c', strtotime ($last->pubDate)),
  217. //last post author
  218. '.nnf_thread-author' => $last->author
  219. ))->remove (array (
  220. //if the thread isn’t locked, remove the lock icon
  221. '.nnf_thread-locked' => !$xml->channel->xpath ('category[.="locked"]'),
  222. //if the thread isn't sticky, remove the 'sticky' class
  223. './@class' => !in_array ($file, $stickies) ? 'nnf_sticky' : false,
  224. //if the thread isn't sticky, remove the sticky icon
  225. '.nnf_thread-sticky' => !in_array ($file, $stickies)
  226. //the lock-icon takes precedence over the sticky icon
  227. || $xml->channel->xpath ('category[.="locked"]'),
  228. //is the last post author a mod?
  229. '.nnf_thread-author@class' => !isMod ($last->author) ? 'nnf_mod' : false
  230. //attach the templated sub-forum item to the list
  231. ))->next ();
  232. } else {
  233. //no threads, remove the template stuff
  234. $template->remove ('#nnf_threads');
  235. }
  236. /* new thread form
  237. ---------------------------------------------------------------------------------------------------------------------- */
  238. if (CAN_POST) $template->set (array (
  239. //set the field values from what was typed in before //set the maximum field sizes
  240. 'input#nnf_title-field@value' => TITLE, 'input#nnf_title-field@maxlength' => SIZE_TITLE,
  241. 'input#nnf_name-field-http@value' => NAME,
  242. 'input#nnf_name-field@value' => NAME, 'input#nnf_name-field@maxlength' => SIZE_NAME,
  243. 'input#nnf_pass-field@value' => PASS, 'input#nnf_pass-field@maxlength' => SIZE_PASS,
  244. 'textarea#nnf_text-field' => TEXT, 'textarea#nnf_text-field@maxlength' => SIZE_TEXT
  245. //is the user already signed-in?
  246. ))->remove (AUTH_HTTP
  247. //don’t need the usual name / password fields and the deafult message for anonymous users
  248. ? '#nnf_name, #nnf_pass, #nnf_email, #nnf_error-none'
  249. //user is not signed in, remove the "you are signed in as:" field and the message for signed in users
  250. : '#nnf_name-http, #nnf_error-none-http'
  251. //are new registrations allowed?
  252. )->remove (FORUM_NEWBIES
  253. ? '#nnf_error-newbies' //yes: remove the warning message
  254. : '#nnf_error-none' //no: remove the default message
  255. //handle error messages
  256. )->remove (array (
  257. //if there's an error of any sort, remove the default messages
  258. '#nnf_error-none, #nnf_error-none-http, #nnf_error-newbies' => FORM_SUBMIT,
  259. //if the username & password are correct, remove the error message
  260. '#nnf_error-auth' => !FORM_SUBMIT || !TITLE || !TEXT || !NAME || !PASS || AUTH,
  261. //if the password is valid, remove the error message
  262. '#nnf_error-pass' => !FORM_SUBMIT || !TITLE || !TEXT || !NAME || PASS,
  263. //if the name is valid, remove the error message
  264. '#nnf_error-name' => !FORM_SUBMIT || !TITLE || !TEXT || NAME,
  265. //if the message text is valid, remove the error message
  266. '#nnf_error-text' => !FORM_SUBMIT || !TITLE || TEXT,
  267. //if the title is valid, remove the error message
  268. '#nnf_error-title'=> !FORM_SUBMIT || TITLE
  269. ));
  270. //call the theme-specific templating function, in 'theme.php', before outputting
  271. theme_custom ($template);
  272. exit ($template);
  273. ?>