Browse code

Fix trying to access array offset on null warnings when responding to a review - re-add UNIXTIMESTAMP

Clarissa Walker authored on 2026/09/02 02:03:31
Showing 1 changed files
... ...
@@ -248,10 +248,10 @@ function format_link($text, $title = "", $target = 0) {
248 248
 function title_link($stories) {
249 249
 	global $ratingslist, $ageconsent, $disablepopups;
250 250
 
251
-	$rating = $stories['rid'];
251
+	$rating = isset($stories['rid']) ? $stories['rid'] : null;
252 252
 	$warningtext = !empty($ratingslist[$rating]['warningtext']) ? addslashes(strip_tags($ratingslist[$rating]['warningtext'])) : "";
253 253
 		if(empty($ratingslist[$rating]['ratingwarning']))
254
-			$title = "<a href=\""._BASEDIR."viewstory.php?sid=".$stories['sid']."\">".$stories['title']."</a>";
254
+			$title = "<a href=\""._BASEDIR."viewstory.php?sid=".(isset($stories['sid']) ? $stories['sid'] : null)."\">".(isset($stories['title']) ? $stories['title'] : null)."</a>";
255 255
 		else {
256 256
 			$warning = "";
257 257
 			$warninglevel = sprintf("%03b", $ratingslist[$rating]['ratingwarning']);
Browse code

Fix occasional foreach array to string conversion warning in author and coauthor URL handling function

Clarissa Walker authored on 2026/09/01 23:42:11
Showing 1 changed files
... ...
@@ -278,7 +278,7 @@ function title_link($stories) {
278 278
 
279 279
 // Same with the author list
280 280
 function author_link($stories) {
281
-	if(is_array($stories['coauthors'])) {
281
+	if ((!empty($stories['coauthors'])) && (is_array($stories['coauthors']))) {
282 282
 		$authlink[] = "<a href=\""._BASEDIR."viewuser.php?uid=".$stories['uid']."\">".$stories['penname']."</a>";
283 283
 		//not needed, why is it there?
284 284
 		//$coauth = dbquery("SELECT "._PENNAMEFIELD." as penname, co.uid FROM ".TABLEPREFIX."fanfiction_coauthors AS co LEFT JOIN "._AUTHORTABLE." ON co.uid = "._UIDFIELD." WHERE co.sid = '".$stories['sid']."'");
Browse code

Revert coauthors array changes as it occasionally brings up an undefined variable warning with no real way to fix it

Clarissa Walker authored on 2026/09/01 23:37:00
Showing 1 changed files
... ...
@@ -278,20 +278,15 @@ function title_link($stories) {
278 278
 
279 279
 // Same with the author list
280 280
 function author_link($stories) {
281
-	
282
-	$authlink[] = "<a href=\"" . _BASEDIR . "viewuser.php?uid=" . $stories['uid'] . "\">" . $stories['penname'] . "</a>";
283
-
284
-	if($stories['coauthors'] && $stories['coauthors_array'])  {	
281
+	if(is_array($stories['coauthors'])) {
282
+		$authlink[] = "<a href=\""._BASEDIR."viewuser.php?uid=".$stories['uid']."\">".$stories['penname']."</a>";
285 283
 		//not needed, why is it there?
286
-		//$coauth = dbquery("SELECT "._PENNAMEFIELD." as penname, co.uid FROM ".TABLEPREFIX."fanfiction_coauthors AS co 
287
-		//LEFT JOIN "._AUTHORTABLE." ON co.uid = "._UIDFIELD." WHERE co.sid = '".$stories['sid']."'");
288
-
289
-		foreach($stories['coauthors_array'] AS $k => $v) {
284
+		//$coauth = dbquery("SELECT "._PENNAMEFIELD." as penname, co.uid FROM ".TABLEPREFIX."fanfiction_coauthors AS co LEFT JOIN "._AUTHORTABLE." ON co.uid = "._UIDFIELD." WHERE co.sid = '".$stories['sid']."'");
285
+		foreach($stories['coauthors'] AS $k => $v) {
290 286
 			$authlink[] = "<a href=\""._BASEDIR."viewuser.php?uid=".$k."\">".$v."</a>";
291 287
 		}
292 288
 	}
293
- 
294
-	return  implode(", ", $authlink) ;
289
+	return isset($authlink) ? implode(", ", $authlink) : "<a href=\""._BASEDIR."viewuser.php?uid=".$stories['uid']."\">".$stories['penname']."</a>";
295 290
 }
296 291
 
297 292
 // Used to truncate text (summaries in blocks for example) to a set length.  An improvement on the old version as this keeps words intact
Browse code

Number of Likes: cosmetic consistency fix

Clarissa Walker authored on 2026/09/01 20:59:34
Showing 1 changed files
... ...
@@ -481,7 +481,7 @@ function ratingpics($rating) {
481 481
 	$ratingpics = "";
482 482
 
483 483
 	if($ratings == "3") { 
484
-		$ratingpics = "<span title=\""._LIKES_NUMBER. "\"><b>" . $rating . "</b></span>";
484
+		$ratingpics = "<span title=\""._LIKES_NUMBER. "\"><b>Likes:</b> " . $rating . "</span>";
485 485
 	}	
486 486
 	elseif($ratings == "2") {
487 487
 		if($rating >= 0.5)
Browse code

Stories: add story with null category in 'Only one' setting exhibits Uncaught TypeError in PHP8 dirty fix - Categories: deleting only category exhibits Uncaught TypeError in PHP8 fix

Clarissa Walker authored on 2026/08/22 19:57:15
Showing 1 changed files
... ...
@@ -42,7 +42,7 @@ function categoryitems($catid, $value)
42 42
 			if($cat['leveldown'] > 0) $pcats[] = $cat['parentcatid'];
43 43
 			if(!in_array($cat['catid'], $cats)) $cats[] = $cat['catid'];
44 44
 		}
45
-		if(count($pcats) > 0) $catquery = dbquery("SELECT catid, parentcatid, leveldown FROM ".TABLEPREFIX."fanfiction_categories WHERE FIND_IN_SET(catid, '".implode($pcats)."') GROUP BY catid");
45
+		if($pcats && (count($pcats) > 0)) $catquery = dbquery("SELECT catid, parentcatid, leveldown FROM ".TABLEPREFIX."fanfiction_categories WHERE FIND_IN_SET(catid, '".implode($pcats)."') GROUP BY catid");
46 46
 		else unset($catquery);
47 47
 	}
48 48
 	dbquery("UPDATE ".TABLEPREFIX."fanfiction_categories SET numitems = (numitems + $value) WHERE FIND_IN_SET(catid, '".implode(",", $cats)."')");
Browse code

Number of Likes

Jimako authored on 2024/05/24 17:54:38
Showing 1 changed files
... ...
@@ -167,14 +167,26 @@ function subseriesList($thisseries) {
167 167
 
168 168
 function seriesreview($thisseries) {
169 169
 
170
+	global $ratings;
171
+
170 172
 	if(!isNumber($thisseries)) return;
171 173
 	$storylist = storiesInSeries($thisseries);
172 174
 	$serieslist = subseriesList($thisseries);
173
-$newrating = dbquery("SELECT AVG(rating) as totalreviews FROM ".TABLEPREFIX."fanfiction_reviews 
174
-	WHERE ((item = '$thisseries' AND type = 'SE')".
175
-	(count($storylist) > 0 ? " OR (FIND_IN_SET(item, '".(implode(",", $storylist))."') > 0 AND type = 'ST')" : "").
176
-	(count($serieslist) > 0 ? " OR (FIND_IN_SET(item, '".(implode(",", $serieslist))."') > 0 AND type = 'SE')" : "").
177
-	") AND rating != '-1'");
175
+	if($ratings == 3) {
176
+		$newrating = dbquery("SELECT COUNT(rating) as totalreviews FROM " . TABLEPREFIX . "fanfiction_reviews 
177
+		WHERE ((item = '$thisseries' AND type = 'SE')" .
178
+		(count($storylist) > 0 ? " OR (FIND_IN_SET(item, '" . (implode(",", $storylist)) . "') > 0 AND type = 'ST')" : "") .
179
+			(count($serieslist) > 0 ? " OR (FIND_IN_SET(item, '" . (implode(",", $serieslist)) . "') > 0 AND type = 'SE')" : "") .
180
+			") AND rating != '-1'");
181
+	}
182
+    else {
183
+		$newrating = dbquery("SELECT AVG(rating) as totalreviews FROM " . TABLEPREFIX . "fanfiction_reviews 
184
+	WHERE ((item = '$thisseries' AND type = 'SE')" .
185
+		(count($storylist) > 0 ? " OR (FIND_IN_SET(item, '" . (implode(",", $storylist)) . "') > 0 AND type = 'ST')" : "") .
186
+		(count($serieslist) > 0 ? " OR (FIND_IN_SET(item, '" . (implode(",", $serieslist)) . "') > 0 AND type = 'SE')" : "") .
187
+		") AND rating != '-1'");
188
+	}
189
+
178 190
 list($totalreviews) = dbrow($newrating);
179 191
 $newcount = dbquery("SELECT count(reviewid) as totalcount FROM ".TABLEPREFIX."fanfiction_reviews 
180 192
 	WHERE ((item = '$thisseries' AND type = 'SE')".
... ...
@@ -269,8 +281,7 @@ function author_link($stories) {
269 281
 	
270 282
 	$authlink[] = "<a href=\"" . _BASEDIR . "viewuser.php?uid=" . $stories['uid'] . "\">" . $stories['penname'] . "</a>";
271 283
 
272
-	if($stories['coauthors']) {
273
-		
284
+	if($stories['coauthors'] && $stories['coauthors_array'])  {	
274 285
 		//not needed, why is it there?
275 286
 		//$coauth = dbquery("SELECT "._PENNAMEFIELD." as penname, co.uid FROM ".TABLEPREFIX."fanfiction_coauthors AS co 
276 287
 		//LEFT JOIN "._AUTHORTABLE." ON co.uid = "._UIDFIELD." WHERE co.sid = '".$stories['sid']."'");
... ...
@@ -468,7 +479,11 @@ function build_pagelinks($url, $total, $offset = 0, $columns = 1) {
468 479
 function ratingpics($rating) {
469 480
 	global $ratings, $like, $dislike, $star, $halfstar;
470 481
 	$ratingpics = "";
471
-	if($ratings == "2") {
482
+
483
+	if($ratings == "3") { 
484
+		$ratingpics = "<span title=\""._LIKES_NUMBER. "\"><b>" . $rating . "</b></span>";
485
+	}	
486
+	elseif($ratings == "2") {
472 487
 		if($rating >= 0.5)
473 488
 			$ratingpics = ($like ? $like : "<img src=\""._BASEDIR."images/like.gif\" alt=\""._LIKED."\">");
474 489
 		else if(($rating < 0.5) && ($rating > 0))
... ...
@@ -575,4 +590,11 @@ function search($storyquery, $countquery, $pagelink = "search.php?", $pagetitle
575 590
 	$tpl->gotoBlock("_ROOT");
576 591
 	return $numrows;
577 592
 }
578
-?>
593
+
594
+
595
+// This function replaces escaped newlines with html <br /> tags
596
+function fixup_newlines($string)
597
+{
598
+	$string = str_replace(array("\\r\\n", "\\n\\r", "\\r", "\\n"), "<br />", $string);
599
+	return $string;
600
+}
Browse code

fix for link and level for admincreated users

Jimako authored on 2024/05/13 13:49:32
Showing 1 changed files
... ...
@@ -227,7 +227,7 @@ function format_email($text) {
227 227
 // Function to format a URL into a clickable link
228 228
 function format_link($text, $title = "", $target = 0) {
229 229
 	if(empty($title)) $title = $text;
230
-	if(strpos($text, "http://") === false) $text = "http://".$text;
230
+	if(strpos($text, "http://") === false && strpos($text, "https://")) $text = "https://".$text;
231 231
 	$text = "<a href='$text'".($target ? " target='_blank'" : "").">$title</a>";
232 232
 	return $text;
233 233
 }
Browse code

coauthors fix

Jimako authored on 2024/05/12 17:29:23
Showing 1 changed files
... ...
@@ -266,14 +266,21 @@ function title_link($stories) {
266 266
 
267 267
 // Same with the author list
268 268
 function author_link($stories) {
269
-	if(is_array($stories['coauthors'])) {
270
-		$authlink[] = "<a href=\""._BASEDIR."viewuser.php?uid=".$stories['uid']."\">".$stories['penname']."</a>";
271
-		$coauth = dbquery("SELECT "._PENNAMEFIELD." as penname, co.uid FROM ".TABLEPREFIX."fanfiction_coauthors AS co LEFT JOIN "._AUTHORTABLE." ON co.uid = "._UIDFIELD." WHERE co.sid = '".$stories['sid']."'");
272
-		foreach($stories['coauthors'] AS $k => $v) {
269
+	
270
+	$authlink[] = "<a href=\"" . _BASEDIR . "viewuser.php?uid=" . $stories['uid'] . "\">" . $stories['penname'] . "</a>";
271
+
272
+	if($stories['coauthors']) {
273
+		
274
+		//not needed, why is it there?
275
+		//$coauth = dbquery("SELECT "._PENNAMEFIELD." as penname, co.uid FROM ".TABLEPREFIX."fanfiction_coauthors AS co 
276
+		//LEFT JOIN "._AUTHORTABLE." ON co.uid = "._UIDFIELD." WHERE co.sid = '".$stories['sid']."'");
277
+
278
+		foreach($stories['coauthors_array'] AS $k => $v) {
273 279
 			$authlink[] = "<a href=\""._BASEDIR."viewuser.php?uid=".$k."\">".$v."</a>";
274 280
 		}
275 281
 	}
276
-	return isset($authlink) ? implode(", ", $authlink) : "<a href=\""._BASEDIR."viewuser.php?uid=".$stories['uid']."\">".$stories['penname']."</a>";
282
+ 
283
+	return  implode(", ", $authlink) ;
277 284
 }
278 285
 
279 286
 // Used to truncate text (summaries in blocks for example) to a set length.  An improvement on the old version as this keeps words intact
... ...
@@ -568,4 +575,4 @@ function search($storyquery, $countquery, $pagelink = "search.php?", $pagetitle
568 575
 	$tpl->gotoBlock("_ROOT");
569 576
 	return $numrows;
570 577
 }
571
-?>
572 578
\ No newline at end of file
579
+?>
Browse code

3.5.6 efiction version

Jimako authored on 2024/03/09 16:09:42
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,571 @@
1
+<?php
2
+// ----------------------------------------------------------------------
3
+// Copyright (c) 2007 by Tammy Keefer
4
+// Based on eFiction 1.1
5
+// Copyright (C) 2003 by Rebecca Smallwood.
6
+// http://efiction.sourceforge.net/
7
+// ----------------------------------------------------------------------
8
+// LICENSE
9
+//
10
+// This program is free software; you can redistribute it and/or
11
+// modify it under the terms of the GNU General Public License (GPL)
12
+// as published by the Free Software Foundation; either version 2
13
+// of the License, or (at your option) any later version.
14
+//
15
+// This program is distributed in the hope that it will be useful,
16
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
+// GNU General Public License for more details.
19
+//
20
+// To read the license please visit http://www.gnu.org/copyleft/gpl.html
21
+// ----------------------------------------------------------------------
22
+
23
+if(!defined("_CHARSET")) exit( );
24
+
25
+// Validates emails
26
+function validEmail($str) {
27
+	return (bool) preg_match('/^[-_a-z0-9\'+*$^&%=~!?{}]++(?:\.[-_a-z0-9\'+*$^&%=~!?{}]+)*@(?:(?![-.])[-a-z0=9.]+(?<![-.])\.[a-z]{2,6}|\d{1,3}(?:\.\d{1,3}){3})(?::\d++)?$/iD', $str);
28
+}
29
+
30
+// Function used to add and remove stories from category counts
31
+function categoryitems($catid, $value)
32
+{
33
+	//add or subtract one to the current categories
34
+	//find out current category's parent
35
+	if(is_array($catid)) $cats = $catid;
36
+	else $cats = array($catid);
37
+	$cats = array_filter($cats, "isNumber");
38
+	$catquery = dbquery("SELECT catid, parentcatid, leveldown FROM ".TABLEPREFIX."fanfiction_categories WHERE FIND_IN_SET(catid, '$catid') GROUP BY catid");
39
+	while(isset($catquery)) {
40
+		while($cat = dbassoc($catquery)) {
41
+			$pcats = array();
42
+			if($cat['leveldown'] > 0) $pcats[] = $cat['parentcatid'];
43
+			if(!in_array($cat['catid'], $cats)) $cats[] = $cat['catid'];
44
+		}
45
+		if(count($pcats) > 0) $catquery = dbquery("SELECT catid, parentcatid, leveldown FROM ".TABLEPREFIX."fanfiction_categories WHERE FIND_IN_SET(catid, '".implode($pcats)."') GROUP BY catid");
46
+		else unset($catquery);
47
+	}
48
+	dbquery("UPDATE ".TABLEPREFIX."fanfiction_categories SET numitems = (numitems + $value) WHERE FIND_IN_SET(catid, '".implode(",", $cats)."')");
49
+}
50
+
51
+// Function to recurse through categories to build a list of the category and all it's sub-categories.
52
+function recurseCategories($catid) {
53
+	global $catlist;
54
+	$$catid = $catlist;
55
+	$categorylist[] = $catid;
56
+	foreach($$catid as $cat => $info) {
57
+		if($info['pid'] == $catid) {
58
+			$categorylist = array_merge($categorylist, recurseCategories($cat));
59
+		}
60
+	}
61
+	return $categorylist;
62
+}
63
+
64
+// Captcha script validation
65
+function captcha_confirm() {
66
+	if(empty($_SESSION[SITEKEY.'_digit'])) return false;
67
+	$digit = $_SESSION[SITEKEY.'_digit'];
68
+	$userdigit = $_POST['userdigit'];
69
+	unset($_SESSION[SITEKEY.'_digit']);
70
+	if($digit == md5(SITEKEY.$userdigit) && $userdigit > 1) return true;
71
+	return false;
72
+}
73
+
74
+// Sanitizes user input to help prevent XSS attacks 
75
+function descript($text) {
76
+	// Convert problematic ascii characters to their true values
77
+	$search = array("40","41","58","65","66","67","68","69","70",
78
+		"71","72","73","74","75","76","77","78","79","80","81",
79
+		"82","83","84","85","86","87","88","89","90","97","98",
80
+		"99","100","101","102","103","104","105","106","107",
81
+		"108","109","110","111","112","113","114","115","116",
82
+		"117","118","119","120","121","122");
83
+	
84
+	$replace = array("(",")",":","a","b","c","d","e","f","g","h",
85
+		"i","j","k","l","m","n","o","p","q","r","s","t","u",
86
+		"v","w","x","y","z","a","b","c","d","e","f","g","h",
87
+		"i","j","k","l","m","n","o","p","q","r","s","t","u",
88
+		"v","w","x","y","z");
89
+
90
+	$entities = count($search);
91
+	
92
+	for ($i=0;$i < $entities;$i++) $text = preg_replace("#(&\#)(0*".$search[$i]."+);*#si", $replace[$i], $text);
93
+
94
+	// the following is based on code from bitflux (http://blog.bitflux.ch/wiki/)	
95
+	// Kill hexadecimal characters completely
96
+	$text = preg_replace('#(&\#x)([0-9A-F]+);*#si', "", $text);
97
+
98
+	// remove any attribute starting with "on" or xmlns
99
+
100
+	$text = preg_replace('#(<[^>]+[\\"\'\s])(onmouseover|onmousedown|onmouseup|onmouseout|onmousemove|onclick|ondblclick|onload|xmlns)[^>]*>#iU', ">", $text);
101
+
102
+	// remove javascript: and vbscript: protocol
103
+	
104
+	$text = preg_replace('#([a-z]*)=([\`\'\"]*)script:#iU', '$1=$2nojscript...', $text);
105
+	$text = preg_replace('#([a-z]*)=([\`\'\"]*)javascript:#iU', '$1=$2nojavascript...', $text);
106
+	$text = preg_replace('#([a-z]*)=([\'\"]*)vbscript:#iU', '$1=$2novbscript...', $text);
107
+
108
+	//<span style="width: expression(alert('Ping!'));"></span> (only affects ie...)
109
+	$text = preg_replace('#(<[^>]+)style=([\`\'\"]*).*expression\([^>]*>#iU', "$1>", $text);
110
+	$text = preg_replace('#(<[^>]+)style=([\`\'\"]*).*behaviour\([^>]*>#iU', "$1>", $text);
111
+	return $text;
112
+}
113
+
114
+// Call this function when the user tries to do something they shouldn't have access to.
115
+function accessDenied($str = ""){
116
+	global $tpl, $output;
117
+
118
+	if(!empty($str)) $output = write_error($str);
119
+	else $output = write_error(_NOTAUTHORIZED);
120
+	if(!empty($tpl)) {
121
+		$tpl->assign("output", $output);
122
+		$tpl->printToScreen( );
123
+		dbclose( );
124
+	}
125
+	else echo $output;
126
+	exit( );
127
+}
128
+
129
+// Call this function when something causes an error and the script has to die.
130
+function errorExit( $msg = ""){
131
+	global $tpl, $output;
132
+
133
+	$output .= write_error(_ERROR.(!empty($msg) ? " " : "").$msg);
134
+	$tpl->assign("output", $output);
135
+	$tpl->printToScreen( );
136
+	dbclose( );
137
+	exit( );
138
+}
139
+// The next three functions are used to calculate the series reviews and rating
140
+
141
+function storiesInSeries($thisseries) {
142
+	$storylist = array( );
143
+	if(!isNumber($thisseries)) return $storylist;
144
+	$serieslist = array( );
145
+	$stinseries = dbquery("SELECT sid, subseriesid FROM ".TABLEPREFIX."fanfiction_inseries WHERE seriesid = '$thisseries'");
146
+	while($st = dbassoc($stinseries)) { 
147
+		if(!empty($st['sid'])) $storylist[] = $st['sid'];
148
+		else if(!empty($st['subseriesid'])) $serieslist[] = $st['subseriesid'];
149
+	}
150
+	if($serieslist) {
151
+		foreach($serieslist as $s) {
152
+			$storylist = array_merge($storylist, storiesInSeries($s));
153
+		}
154
+	}
155
+	return $storylist;
156
+}
157
+function subseriesList($thisseries) {
158
+	$serieslist = array( );
159
+	if(!isNumber($thisseries)) return $serieslist;
160
+	$stinseries = dbquery("SELECT subseriesid FROM ".TABLEPREFIX."fanfiction_inseries WHERE seriesid = '$thisseries'");
161
+	while($st = dbassoc($stinseries)) {
162
+		$serieslist[] = $st['subseriesid']; 
163
+		$serieslist = array_merge($serieslist, subseriesList($st['subseriesid']));
164
+	}
165
+	return $serieslist;
166
+}
167
+
168
+function seriesreview($thisseries) {
169
+
170
+	if(!isNumber($thisseries)) return;
171
+	$storylist = storiesInSeries($thisseries);
172
+	$serieslist = subseriesList($thisseries);
173
+$newrating = dbquery("SELECT AVG(rating) as totalreviews FROM ".TABLEPREFIX."fanfiction_reviews 
174
+	WHERE ((item = '$thisseries' AND type = 'SE')".
175
+	(count($storylist) > 0 ? " OR (FIND_IN_SET(item, '".(implode(",", $storylist))."') > 0 AND type = 'ST')" : "").
176
+	(count($serieslist) > 0 ? " OR (FIND_IN_SET(item, '".(implode(",", $serieslist))."') > 0 AND type = 'SE')" : "").
177
+	") AND rating != '-1'");
178
+list($totalreviews) = dbrow($newrating);
179
+$newcount = dbquery("SELECT count(reviewid) as totalcount FROM ".TABLEPREFIX."fanfiction_reviews 
180
+	WHERE ((item = '$thisseries' AND type = 'SE')".
181
+	(count($storylist) > 0 ? " OR (FIND_IN_SET(item, '".(implode(",", $storylist))."') > 0 AND type = 'ST')" : "").
182
+	(count($serieslist) > 0 ? " OR (FIND_IN_SET(item, '".(implode(",", $serieslist))."') > 0 AND type = 'SE')" : "").
183
+	") AND review != 'No Review'");
184
+list($totalcount) = dbrow($newcount);
185
+if($totalcount) $update = dbquery("UPDATE ".TABLEPREFIX."fanfiction_series SET rating = '".round($totalreviews)."', reviews = '$totalcount' WHERE seriesid = '$thisseries'");
186
+$parentq = dbquery("SELECT seriesid FROM ".TABLEPREFIX."fanfiction_inseries WHERE subseriesid = '$thisseries' AND seriesid != '$thisseries'");
187
+while($parent2 = dbassoc($parentq)) { seriesreview($parent2['seriesid']); }
188
+
189
+}
190
+
191
+// Per a suggestion from jrabbit, we'll use this function to optimize some queries 
192
+function findclause($field,$set) {
193
+  if (empty($set)) {
194
+    return "1 = 0";
195
+  }
196
+  if(is_array($set)) $set = implode(",", $set);
197
+  if (strpos($set,',')>0) {
198
+    return "FIND_IN_SET($field,'$set') > 0";
199
+  }
200
+  return "$field='$set'";
201
+}
202
+
203
+// Added 3.3
204
+function nl2br2($string) {
205
+	$string = str_replace(array("\r\n", "\r", "\n"), "<br />", $string);
206
+	return $string;
207
+}
208
+
209
+// Formats the text of the story when displayed on screen.
210
+function format_story($text) {
211
+      $text = trim((string)$text);
212
+      if(strpos($text, "<br>") === false && strpos($text, "<p>") === false && strpos($text, "<br />") === false) $text = nl2br2($text);
213
+      if(_CHARSET != "ISO-8859-1" && _CHARSET != "US-ASCII") return stripslashes($text);
214
+      $badwordchars = array(chr(212), chr(213), chr(210), chr(211), chr(209), chr(208), chr(201), chr(145), chr(146), chr(147), chr(148), chr(151), chr(150), chr(133));
215
+      $fixedwordchars = array('&#8216;', '&#8217;', '&#8220;', '&#8221;', '&#8212;', '&#8211;', '&#8230;', '&#8216;', '&#8217;', '&#8220;', '&#8221;', '&#8212;', '&#8211;',  '&#8230;' );
216
+      $text = str_replace($badwordchars,$fixedwordchars,stripslashes($text));
217
+      return $text;
218
+}
219
+
220
+// Function to Spam-protect emails.  Is called for the IM fields in the user's profile.
221
+function format_email($text) {
222
+	$search = array('@', '.');
223
+	$replace = array(" [AT] ", " [DOT] ");
224
+	return str_replace($search, $replace, $text);
225
+}
226
+
227
+// Function to format a URL into a clickable link
228
+function format_link($text, $title = "", $target = 0) {
229
+	if(empty($title)) $title = $text;
230
+	if(strpos($text, "http://") === false) $text = "http://".$text;
231
+	$text = "<a href='$text'".($target ? " target='_blank'" : "").">$title</a>";
232
+	return $text;
233
+}
234
+
235
+// Because this is used in places other than the listings of stories, we're setting it up as a function to be called as needed.
236
+function title_link($stories) {
237
+	global $ratingslist, $ageconsent, $disablepopups;
238
+
239
+	$rating = $stories['rid'];
240
+	$warningtext = !empty($ratingslist[$rating]['warningtext']) ? addslashes(strip_tags($ratingslist[$rating]['warningtext'])) : "";
241
+		if(empty($ratingslist[$rating]['ratingwarning']))
242
+			$title = "<a href=\""._BASEDIR."viewstory.php?sid=".$stories['sid']."\">".$stories['title']."</a>";
243
+		else {
244
+			$warning = "";
245
+			$warninglevel = sprintf("%03b", $ratingslist[$rating]['ratingwarning']);
246
+			if($warninglevel[2] && !isset($_SESSION[SITEKEY."_warned"][$rating])) {
247
+				$location = "viewstory.php?sid=".$stories['sid']."&amp;warning=$rating";
248
+				$warning = $warningtext;
249
+			}
250
+			if($warninglevel[1] && !$ageconsent && empty($_SESSION[SITEKEY.'_ageconsent'])) {
251
+				$location = "viewstory.php?sid=".$stories['sid']."&amp;ageconsent=ok&amp;warning=$rating";
252
+				$warning = _AGECHECK." - "._AGECONSENT." ".$warningtext." -- 1";
253
+			}
254
+			if($warninglevel[0] && !isMEMBER) {
255
+				$location = "user.php?action=login&amp;sid=".$stories['sid'];
256
+				$warning = _RUSERSONLY." - $warningtext";		
257
+			}
258
+			if(!empty($warning)) {
259
+				$warning = preg_replace("@'@", "\'", $warning);
260
+				$title = "<a href=\"javascript:if(confirm('".$warning."')) location = '"._BASEDIR."$location'\">".$stories['title']."</a>";
261
+			}
262
+			else $title = "<a href=\""._BASEDIR."viewstory.php?sid=".$stories['sid']."\">".$stories['title']."</a>";
263
+		}
264
+	return $title;
265
+}
266
+
267
+// Same with the author list
268
+function author_link($stories) {
269
+	if(is_array($stories['coauthors'])) {
270
+		$authlink[] = "<a href=\""._BASEDIR."viewuser.php?uid=".$stories['uid']."\">".$stories['penname']."</a>";
271
+		$coauth = dbquery("SELECT "._PENNAMEFIELD." as penname, co.uid FROM ".TABLEPREFIX."fanfiction_coauthors AS co LEFT JOIN "._AUTHORTABLE." ON co.uid = "._UIDFIELD." WHERE co.sid = '".$stories['sid']."'");
272
+		foreach($stories['coauthors'] AS $k => $v) {
273
+			$authlink[] = "<a href=\""._BASEDIR."viewuser.php?uid=".$k."\">".$v."</a>";
274
+		}
275
+	}
276
+	return isset($authlink) ? implode(", ", $authlink) : "<a href=\""._BASEDIR."viewuser.php?uid=".$stories['uid']."\">".$stories['penname']."</a>";
277
+}
278
+
279
+// Used to truncate text (summaries in blocks for example) to a set length.  An improvement on the old version as this keeps words intact
280
+function truncate_text($str, $n = 75, $delim='...') { 
281
+   $len = strlen($str);
282
+   if($len > $n) {
283
+        $pos = strpos($str, " ", $n);
284
+	if($pos) $str = trim(substr($str, 0, $pos), "\n\t\.,"). $delim;
285
+  }
286
+  return closetags($str);
287
+} 
288
+
289
+// A helper function for the truncate_text function.  This will close all open tags
290
+function closetags($html){
291
+
292
+  $donotclose=array('br','img','input', 'hr');
293
+  preg_match_all("#<([a-z]+)( .*)?(?!/)>#iU",$html,$result);
294
+  $openedtags=$result[1];
295
+
296
+  preg_match_all("#</([a-z]+)>#iU",$html,$result);
297
+  $closedtags=$result[1];
298
+  $len_opened = count($openedtags);
299
+  if(count($closedtags) == $len_opened){
300
+	  return $html;
301
+  }
302
+
303
+  $openedtags = array_reverse($openedtags);
304
+
305
+  for($i=0;$i < $len_opened;$i++) {
306
+
307
+    if (!in_array($openedtags[$i],$closedtags) && !in_array($openedtags[$i], $donotclose)){
308
+      $html .= '</'.$openedtags[$i].'>';
309
+    } 
310
+    else {
311
+      unset($closedtags[array_search($openedtags[$i],$closedtags)]);
312
+    }
313
+  }
314
+  return $html;
315
+}
316
+
317
+//  Builds the naughty word list for the site's censor
318
+function build_word($myword) {
319
+	$letters =preg_split('//',$myword, -1, PREG_SPLIT_NO_EMPTY);
320
+	$word = "";
321
+	foreach($letters as $letter) { 
322
+		if($word != "") $word .= "\W+";
323
+		$word .= $letter;
324
+	}
325
+	return $word;
326
+
327
+}
328
+
329
+// Finds forbidden words in text.  Returns true if a forbidden word is found
330
+function find_naughty($text) {
331
+	global $words;
332
+	if(!count($words)) return false;
333
+	$naughty = 0;
334
+	for($i = 0; $i < sizeof($words); $i++) {
335
+		if(strpos($words[$i], "*") === false) {
336
+			// check for whole word
337
+			if(preg_match('/(\s|^)+(('.$words[$i].')|('.build_word($words[$i]).')+(s|es)?)+(\W|$)+/i', $text, $match)) {
338
+				echo $words[$i] ." 0=".$match[0];
339
+				$naughty = 1; 
340
+			}
341
+		}
342
+		if(strpos($words[$i], "*") !== false && strpos($words[$i], "*") == 0 && !$naughty) {
343
+			// remove the * from the beginning of the word
344
+			$word = substr($words[$i], 1, strlen($words[$i]));
345
+			if(strrpos($word, "*") == strlen($word) - 1) $word = substr($word, 0, strlen($word) - 1);
346
+			// check for whole word plus word as suffix
347
+			if(preg_match('/[\s|^]+\w*('.$word.'|\W'.build_word($word).')+(s|es)?(\W|$)/i', $text, $match)) $naughty = 1; 
348
+		}
349
+		if(strrpos($words[$i], "*") == strlen($words[$i]) - 1 && !$naughty) {
350
+			// remove the * from the end of the word
351
+			$word = substr($words[$i], 0, strlen($words[$i]) - 1);
352
+			if(strpos($word, "*") !== false && strpos($word, "*") == 0) $word = substr($word, 1, strlen($word));
353
+			// check for whole word plus word as prefix
354
+			if(preg_match('/(\s|^)('.$word.'|'.build_word($word).'\W)+\w*(s|es)?(\W|$)/i', $text, $match)) $naughty = 1; 
355
+		}
356
+		if($naughty) break;  // no sense continuing down the list and this will make it run faster
357
+	}
358
+	return $naughty;
359
+}
360
+
361
+// Replaces forbidden words in text.  The forbidden word is replaced with the first letter and trailing ***
362
+function replace_naughty($text) {
363
+	global $words;
364
+
365
+	if(!count($words)) return $text;
366
+	$i = 0;
367
+	for($j = 0; $j < sizeof($words); $j++) {
368
+		if(strpos($words[$j], "*") === false) {
369
+			$replace[$i] = str_pad($words[$j][0], strlen($words[$j]), "*");
370
+			$naughtywords[$i] = '/\b('.$words[$j].'\b)|('.build_word($words[$j]).')\b/i';
371
+			$i++;
372
+		}
373
+
374
+		if(strpos($words[$j], "*") !== false && strpos($words[$j], "*") == 0) {
375
+			$word = substr($words[$j], 1, strlen($words[$j]));
376
+			if(strrpos($word, "*") == strlen($word) - 1) $word = substr($word, 0, strlen($word) - 1);
377
+			$naughtywords[$i] = '/(\s|^)+(\w*)+('.$word.'|'.build_word($word).')/i';
378
+			$replace[$i] = "$1$2".str_pad(substr($word, 0, 1), strlen($word), "*");
379
+			$i++;
380
+		}
381
+		if(strrpos($words[$j], "*") == strlen($words[$j]) - 1) {
382
+			$word = substr($words[$j], 0, strlen($words[$j]) - 1);
383
+			if(strpos($word, "*") !== false && strpos($word, "*") == 0) $word = substr($word, 1, strlen($word));
384
+			$replace[$i] = '$1'.str_pad(substr($word, 0, 1), strlen($word), "*").'$3';
385
+			$naughtywords[$i] = '/(.*)('.$word.'|'.build_word($word).')(\s|\w)/i';
386
+			$i++;
387
+		}
388
+	}
389
+	$text = preg_replace($naughtywords, $replace, $text);
390
+	return $text;
391
+}
392
+
393
+// Format for messages sent back from various forms and actions 
394
+function write_message($str) {
395
+	return "<div style='text-align: center; margin: 1em;'>$str</div>";
396
+}
397
+
398
+// Formats error messages sent back from various forms and actions
399
+function write_error($str) {
400
+	return "<div style='text-align: center; margin: 1em;' class='errortext'>$str</div>";
401
+}
402
+
403
+// Checks that the given $num is actually a number.  Used to help prevent XSS attacks.
404
+function isNumber($num) {
405
+	if(empty($num)) return false;
406
+	if(!is_string($num)) return false;
407
+	return preg_match("/^[0-9]+$/", $num);
408
+}
409
+
410
+// May be needed for sites that have bridged the authors table
411
+function check_prefs($uid) {
412
+	$test = dbquery("SELECT uid FROM ".TABLEPREFIX."fanfiction_authorprefs WHERE uid = '$uid'");
413
+	if(dbnumrows($test)) return true;
414
+	else return false;
415
+}
416
+
417
+// Function builds the alphabet links on various pages.
418
+function build_alphalinks($url, $let) {
419
+	global $alphabet;
420
+
421
+	$alpha = "<div id=\"alphabet\">";
422
+	foreach( $alphabet as $link ) {
423
+		// Build a link that calls a function with ($link and 1 (page number) )
424
+		$alpha .= "<a href=\"{$url}let=$link\"".($let == $link ? " id='currentletter'" : "").">$link</a> \n";
425
+	}
426
+	$alpha .= "</div>";
427
+	return $alpha;
428
+}
429
+
430
+// Function builds the pagination links
431
+function build_pagelinks($url, $total, $offset = 0, $columns = 1) {
432
+	global $itemsperpage, $linkstyle, $linkrange;
433
+	$pages = "";
434
+	$itemsperpage = $itemsperpage * $columns;
435
+
436
+	if($itemsperpage >= $total) return;
437
+
438
+	if(empty($linkrange)) $linkrange = 4;
439
+
440
+	$totpages = floor($total/$itemsperpage) + ($total % $itemsperpage ? 1 : 0);
441
+	$curpage = floor($offset/$itemsperpage) + 1;
442
+	if(!$linkstyle) $startrange = $curpage;
443
+	else {
444
+		if($totpages <= $linkrange || $curpage == 1) $startrange = 1;
445
+		else if($curpage >= $totpages - floor($linkrange / 2) + 1) $startrange = $totpages - $linkrange;
446
+		else $startrange = $curpage - floor($linkrange / 2) > 0 ? $curpage - floor($linkrange / 2) : 1;
447
+	}
448
+	if($startrange >= $totpages - $linkrange ) $startrange = $totpages - $linkrange > 0 ? $totpages - $linkrange : 1;
449
+	$stoprange = $totpages > $startrange + $linkrange ? $startrange + $linkrange : $totpages + 1;
450
+	if($curpage > 1 && $linkstyle != 1) $pages .= "<a href='".$url."offset=".( $offset - $itemsperpage)."' id='plprev'>["._PREVIOUS."]</a> ";
451
+	if($startrange > 1 && $linkstyle > 0) $pages .= "<a href='".$url."offset=0'>1</a><span class='ellipses'>...</span>";
452
+	for($x = $startrange; $x < $stoprange; $x++) {
453
+		$pages .= "<a href='".$url."offset=".(($x - 1) * $itemsperpage)."'".($x == $curpage ? "id='currentpage'" : "").">".$x."</a> \n";
454
+	}
455
+	if($stoprange < $totpages && $linkstyle > 0) $pages .= "<span class='ellipses'>...</span> <a href='".$url."offset=".(($totpages - 1) * $itemsperpage)."'>$totpages</a>\n";
456
+	if ($curpage < $totpages && $linkstyle != 1) $pages .=  " <a href='".$url."offset=".($offset+$itemsperpage)."' id='plnext'>["._NEXT."]</a>";
457
+	return "<div id=\"pagelinks\">$pages</div>";
458
+}
459
+
460
+// Function that returns the ratings picks 
461
+function ratingpics($rating) {
462
+	global $ratings, $like, $dislike, $star, $halfstar;
463
+	$ratingpics = "";
464
+	if($ratings == "2") {
465
+		if($rating >= 0.5)
466
+			$ratingpics = ($like ? $like : "<img src=\""._BASEDIR."images/like.gif\" alt=\""._LIKED."\">");
467
+		else if(($rating < 0.5) && ($rating > 0))
468
+			$ratingpics = ($dislike ? $dislike :"<img src=\""._BASEDIR."images/dislike.gif\" alt=\""._DISLIKED."\">");
469
+		else $ratingpics = "";
470
+	}
471
+	if($ratings == "1") {
472
+		global $star, $halfstar;
473
+		if($rating > 0) {
474
+			for($x = 0; $x < ($rating / 2) - .5; $x++) {
475
+				$ratingpics .= ($star ? $star  : "<img src=\""._BASEDIR."images/star.gif\" alt=\"star\">");
476
+			}
477
+			if($rating % 2 != 0) $ratingpics .= ($halfstar ? $halfstar  : "<img src=\""._BASEDIR."images/starhalf.gif\" alt=\"half-star\">");
478
+		}
479
+		else $ratingpics = "";
480
+	}
481
+	if(!empty($ratingpics)) return "<span style='white-space: nowrap;'>$ratingpics</span>"; // the no-wrap style will keep the stars together
482
+	else return;
483
+}
484
+
485
+// This function builds the list of category links (including the breadcrumb depending on settings)
486
+function catlist($catid) {
487
+	global $extendcats, $catlist, $action;
488
+
489
+	if(!is_array($catid)) $catid = explode(",", $catid);
490
+	$categorylinks = array();
491
+	foreach($catid as $cat) {
492
+		if(empty($catlist[$cat])) continue;
493
+		if($extendcats) {
494
+			unset($link);
495
+			$thiscat = $cat;
496
+			while(isset($thiscat)) {
497
+				if(isset($link)) $link = " > ".$link;
498
+				else $link = "";
499
+				if($action != "printable") $link = "<a href='"._BASEDIR."browse.php?type=categories&amp;catid=$thiscat'>".$catlist[$thiscat]['name']."</a>".$link;
500
+				else $link = $catlist[$thiscat]['name'].$link;
501
+				if($catlist[$thiscat]['pid'] == -1) unset($thiscat);
502
+				else $thiscat = $catlist[$thiscat]['pid'];
503
+			}
504
+			$categorylinks[] = $link;
505
+		}
506
+		else $categorylinks[] = "<a href='"._BASEDIR."browse.php?type=categories&amp;catid=$cat'>".$catlist[$cat]['name']."</a>";
507
+	}
508
+	return implode(", ", $categorylinks);
509
+}
510
+
511
+// This function builds the list of character links
512
+function charlist($characters) {
513
+	global $charlist, $action;
514
+
515
+	if(!is_array($characters)) $characters = explode(",", $characters);
516
+	$charlinks = array( );
517
+	foreach($characters as $c) {
518
+		if(empty($charlist[$c]['name'])) continue;
519
+		if($action != "printable") $charlinks[] = "<a href='"._BASEDIR."browse.php?type=characters&amp;charid=$c'>".$charlist[$c]['name']."</a>";
520
+		else $charlinks[] = $charlist[$c]['name'];
521
+	}
522
+	return implode(", ", $charlinks);
523
+}
524
+
525
+// Most of the pages that list stories and series use this fuction.  This handles showing the series and stories and pagination of the two together when needed
526
+function search($storyquery, $countquery, $pagelink = "search.php?", $pagetitle = 0) {
527
+	global $tpl, $new, $ratingslist, $itemsperpage, $reviewsallowed, $output, $dateformat, $current, $featured, $favorites, $retired, $ageconsent, $classtypelist, $classlist, $offset, $recentdays;
528
+     
529
+	$count = dbquery($countquery);
530
+	list($numrows) = dbrow($count);
531
+	if($numrows) {
532
+		$tpl->assign("output", ($pagetitle ? "<div id=\"pagetitle\">$pagetitle</div>" : ""));
533
+		$tpl->newBlock("listings");
534
+		if(!$ratingslist) {
535
+			$ratlist = dbquery("SELECT * FROM ".TABLEPREFIX."fanfiction_ratings");
536
+			while($rate = dbassoc($ratlist)) {
537
+				$ratings[$rate['rid']] = array("rating" => $rate['rating'], "ratingwarning" => $rate['ratingwarning'], "warningtext" => $rate['warningtext']);
538
+			}
539
+		}
540
+		$tpl->newBlock("listings");
541
+		$tpl->gotoBlock("listings");
542
+		$tpl->assign("stories",  "<div class=\"sectionheader\">"._STORIES."</div>");
543
+		$storyquery .= " LIMIT $offset, $itemsperpage";
544
+		$result3 = dbquery($storyquery);     
545
+		$count = 0;                     
546
+		while($stories = dbassoc($result3)) {       
547
+			$tpl->newBlock("storyblock");
548
+			include(_BASEDIR."includes/storyblock.php"); 
549
+		}
550
+		$tpl->gotoBlock("_ROOT");		
551
+	}
552
+	else {
553
+		$tpl->newBlock("listings");
554
+		$tpl->assign("pagelinks", write_message(_NORESULTS));
555
+	}
556
+	if($numrows > $itemsperpage) {
557
+		$termArray = array_merge($_GET, $_POST);
558
+		$terms = array();
559
+		foreach($termArray as $term => $value) {
560
+			if($term == "submit" || $term == "go" || $term == "offset" || ($term != "complete" &&empty($value))) continue;
561
+			$terms[] = "$term=".(is_array($value) ? implode(",", $value) : $value);
562
+		}
563
+		$terms = implode("&amp;", $terms);
564
+		$terms .= "&amp;";
565
+		$tpl->gotoBlock("listings");
566
+		$tpl->assign( "pagelinks", build_pagelinks($pagelink.$terms, $numrows, $offset));
567
+	}
568
+	$tpl->gotoBlock("_ROOT");
569
+	return $numrows;
570
+}
571
+?>
0 572
\ No newline at end of file