// Public interfaces: convenience functions function smd_prev($atts, $thing) { $atts['dir'] = 'prev'; return smd_nearest($atts, $thing); } function smd_next($atts, $thing) { $atts['dir'] = 'next'; return smd_nearest($atts, $thing); } function smd_link_to_prev($atts, $thing) { $atts['dir'] = 'prev'; return smd_link_to($atts, $thing); } function smd_link_to_next($atts, $thing) { $atts['dir'] = 'next'; return smd_link_to($atts, $thing); } function smd_if_start($atts, $thing) { $atts['dir'] = 'prev'; return smd_if_horizon($atts, $thing); } function smd_if_end($atts, $thing) { $atts['dir'] = 'next'; return smd_if_horizon($atts, $thing); } // **************************** // Private function: not for public consumption // **************************** function smd_if_horizon($atts, $thing) { global $pretext, $thisarticle, $thiscategory, $smd_last, $smd_first, $smd_in_nearest; extract(lAtts(array( 'type' => 'list', 'logic' => 'or', 'dir' => 'next', 'debug' => 0, ), $atts)); $itout = array(); // For debug only $type = do_list($type); $out = array(); foreach ($type as $item) { if ($debug) { $itout[] = $item; } switch ($item) { case 'list': if ($smd_in_nearest) { if ($dir == 'next') { $out[] = (empty($smd_last)) ? true : false; } else { $out[] = (empty($smd_first)) ? true : false; } } break; case 'category': if ($smd_in_nearest) { if ($dir == 'next') { $out[] = (!empty($smd_last) && ($smd_last['category1'] != $thisarticle['category1'] || $smd_last['category2'] != $thisarticle['category2'])) ? true : false; } else { $out[] = (!empty($smd_first) && ($smd_first['category1'] != $thisarticle['category1'] || $smd_first['category2'] != $thisarticle['category2'])) ? true : false; } } else { if ($dir == 'next') { $out[] = (!empty($thiscategory['is_last'])) ? true : false; } else { $out[] = (!empty($thiscategory['is_first'])) ? true : false; } } break; case 'author': if ($smd_in_nearest) { if ($dir == 'next') { $out[] = (!empty($smd_last) && $smd_last['author'] != $thisarticle['authorid']) ? true : false; } else { $out[] = (!empty($smd_first) && $smd_first['author'] != $thisarticle['authorid']) ? true : false; } } else { // Not possible since author lists are not permitted in TXP } break; case 'cat1': case 'category1': if ($smd_in_nearest) { if ($dir == 'next') { $out[] = (!empty($smd_last) && $smd_last['category1'] != $thisarticle['category1']) ? true : false; } else { $out[] = (!empty($smd_first) && $smd_first['category1'] != $thisarticle['category1']) ? true : false; } } else { if ($dir == 'next') { $out[] = (!empty($thiscategory['is_last'])) ? true : false; } else { $out[] = (!empty($thiscategory['is_first'])) ? true : false; } } break; case 'cat2': case 'category2': if ($smd_in_nearest) { if ($dir == 'next') { $out[] = (!empty($smd_last) && $smd_last['category2'] != $thisarticle['category2']) ? true : false; } else { $out[] = (!empty($smd_first) && $smd_first['category2'] != $thisarticle['category2']) ? true : false; } } else { if ($dir == 'next') { $out[] = (!empty($thiscategory['is_last'])) ? true : false; } else { $out[] = (!empty($thiscategory['is_first'])) ? true : false; } } break; case 'section': default: if ($smd_in_nearest) { if ($dir == 'next') { $out[] = (!empty($smd_last) && $smd_last['section'] != $thisarticle['section']) ? true : false; } else { $out[] = (!empty($smd_first) && $smd_first['section'] != $thisarticle['section']) ? true : false; } } else { if ($dir == 'next') { $out[] = empty($pretext['next_id']) ? true : false; } else { $out[] = empty($pretext['prev_id']) ? true : false; } } break; } } if ($debug) { echo '++ TEST RESULTS ++'; dmp($itout); dmp($out); } $res = ($out) ? true : false; if (strtolower($logic) == "and" && in_array(false, $out)) { $res = false; } if (strtolower($logic) == "or" && !in_array(true, $out)) { $res = false; } if ($debug) { echo '++ FINAL RESULT ++'; dmp($res); } return parse(EvalElse($thing, $res)); } // **************************** // Private function: not for public consumption // **************************** function smd_nearest($atts, $thing) { global $pretext, $thisarticle, $thiscategory, $prefs, $next_id, $prev_id, $next_title, $prev_title, $smd_last, $smd_first, $smd_in_nearest; extract(lAtts(array( 'section' => $pretext['s'], 'category' => $pretext['c'], 'author' => $pretext['author'], 'realname' => '', 'status' => '4', 'time' => 'any', // any, future, past 'datasort' => 'section, category1, category2, author', 'timesort' => 'posted', 'form' => '', 'dir' => 'next', // Set by wrapper tags 'debug' => 0, ), $atts)); extract($prefs); $smd_in_nearest = true; $thing = (empty($form)) ? $thing : fetch_form($form); $expired = ($publish_expired_articles) ? '' : ' AND (now() <= Expires or Expires = '.NULLDATETIME.')'; $safe_name = safe_pfx('textpattern'); // Filters $catSQL = $secSQL = $authSQL = ''; if($category) { $catSQL = doQuote(join("','", doSlash(do_list($category)))); $catSQL = ' AND ( Category1 IN ('.$catSQL.') OR Category2 IN ('.$catSQL.') ) '; } if($section) { $secSQL = ' AND Section IN ('.doQuote(join("','", doSlash(do_list($section)))).') '; } if($realname) { $author = join(',', safe_column('name', 'txp_users', 'RealName IN ('. doQuote(join("','", doSlash(doArray(do_list($realname), 'urldecode')))) .')' )); } if($author) { $authSQL = ' AND AuthorID IN ('.doQuote(join("','", doSlash(do_list($author)))).') '; } $status = do_list($status); $stati = array(); foreach ($status as $stat) { if (empty($stat)) { continue; } else if (is_numeric($stat)) { $stati[] = $stat; } else { $stati[] = getStatusNum($stat); } } $statSQL = 'Status IN ('.join(',', $stati).')'; $timeSQL = ''; switch($time) { case "any" : break; case "future" : $timeSQL = " AND Posted > now()"; break; default : $timeSQL = " AND Posted < now()"; break; // The past } // Sort $sorder = (($dir=='next') ? ' DESC' : ' ASC'); // Negative logic to avoid lookahead: the "last" row seen is always the one required $orderby = array(); if ($datasort) { $datasort = do_list($datasort); foreach ($datasort as $item) { switch($item) { case 'section': if ($section) { $orderby[] = 'Section'.$sorder; } break; case 'category': if ($category) { $orderby[] = 'Category1'.$sorder; $orderby[] = 'Category2'.$sorder; } break; case 'category1': if ($category) { $orderby[] = 'Category1'.$sorder; } break; case 'category2': if ($category) { $orderby[] = 'Category2'.$sorder; } break; case 'author': if ($author) { $orderby[] = 'AuthorID'.$sorder; } break; } } } if ($timesort) { $timesort = do_list($timesort); foreach ($timesort as $item) { switch(strtolower($item)) { case 'lastmod': $orderby[] = 'LastMod'.$sorder; break; case 'expires': $orderby[] = 'Expires'.$sorder; break; case 'posted': default: $orderby[] = 'Posted'.$sorder; break; } } } $orderby = ' ORDER BY ' . join(',', $orderby); // Do it assert_article(); $rs = safe_rows('*, unix_timestamp(Posted) as uPosted, unix_timestamp(Expires) as uExpires, unix_timestamp(LastMod) as uLastMod', 'textpattern', $statSQL. (($category) ? $catSQL : ''). (($section) ? $secSQL : ''). (($author) ? $authSQL : ''). $timeSQL. $expired. $orderby, $debug); if ($debug > 1 && $rs) { echo '++ RECORD SET ++'; dmp($rs); } // Find the current article in the record set, then move to find next/prev $last = $curr = $ctr = 1; foreach ($rs as $row) { if ($row['ID'] == $thisarticle['thisid']) { $curr = $last; break; } $last = $row; // Store current $ctr++; } if ($curr !== 1) { if ($dir=='next') { $smd_last['position'] = $ctr; $smd_last['section'] = $thisarticle['section']; $smd_last['psec'] = $pretext['s']; $smd_last['pcat'] = $pretext['c']; $smd_last['category1'] = $thisarticle['category1']; $smd_last['category2'] = $thisarticle['category2']; $smd_last['author'] = $thisarticle['authorid']; } else { $smd_first['position'] = $ctr; $smd_first['psec'] = $pretext['s']; $smd_first['pcat'] = $pretext['c']; $smd_first['section'] = $thisarticle['section']; $smd_first['category1'] = $thisarticle['category1']; $smd_first['category2'] = $thisarticle['category2']; $smd_first['author'] = $thisarticle['authorid']; } } else { if ($dir=='next') { $smd_last = array(); } else { $smd_first = array(); } } if ($debug) { if ($dir=='next') { echo '++ MOST RECENT (NEXT) ++'; dmp($smd_last); } else { echo '++ MOST RECENT (PREV) ++'; dmp($smd_first); } } // Populate globals if the next/prev article exists $out = ''; $saved = array(); if ($curr === 1) { $out = parse($thing); } else { // Keep a note of where we were article_push(); $saved['prev_id'] = $prev_id; $saved['next_id'] = $next_id; $saved['prev_title'] = $prev_title; $saved['next_title'] = $next_title; // Pretend we're in the new article, and fake the global vars populateArticleData($curr); $prev_id = ($dir=='prev') ? $curr['ID'] : ''; $next_id = ($dir=='next') ? $curr['ID'] : ''; $prev_title = ($dir=='prev') ? $curr['Title'] : ''; $next_title = ($dir=='next') ? $curr['Title'] : ''; $url = permlinkurl_id($curr['ID']); $thing = (empty($thing)) ? ''.$curr['Title'].'' : $thing; $out = parse($thing); // Restore everything $prev_id = $saved['prev_id']; $next_id = $saved['next_id']; $prev_title = $saved['prev_title']; $next_title = $saved['next_title']; article_pop(); } $smd_in_nearest = false; return $out; } // **************************** // Private function: not for public consumption // **************************** function smd_link_to($atts, $thing = NULL) { global $next_id, $prev_id, $next_title, $prev_title, $smd_last, $smd_first, $smd_in_nearest; extract(lAtts(array( 'showalways' => 0, 'wraptag' => '', 'class' => '', 'urlvars' => '', 'dir' => 'next', // Set by wrapper tags 'debug' => '0', ), $atts)); // Maintain any URL variables $addArgs = array(); if($urlvars) { $optencode = $optforce = $optpri = false; if (strpos($urlvars, 'SMD_ALL') === 0) { // Determine if options are to be applied globally $urlopts = do_list($urlvars, ':'); $optencode = (in_array('ESCAPE', $urlopts)) ? true : false; $optforce = (in_array('FORCE', $urlopts)) ? true : false; $optpri = (in_array('TAG_PRIORITY', $urlopts)) ? true : false; // POST overrides GET if both exist $urlvars = array_merge(array_keys($_GET), array_keys($_POST)); } else { $urlvars = do_list($urlvars); } foreach ($urlvars as $urlvar) { $urlopts = do_list($urlvar, ':'); $encode = ($optencode || in_array('ESCAPE', $urlopts)) ? true : false; $force = ($optforce || in_array('FORCE', $urlopts)) ? true : false; $pri = ($optpri || in_array('TAG_PRIORITY', $urlopts)) ? true : false; $urlparts = do_list($urlopts[0], '='); $var = $urlparts[0]; $val = gps($urlparts[0]); if ($pri) { $val = (isset($urlparts[1])) ? $urlparts[1] : gps($urlparts[0]); } else { if ($val=='' && isset($urlparts[1])) { $val = $urlparts[1]; } } $val = ($encode) ? htmlentities($val) : $val; if ($val !== '' || $force) { $addArgs[] = $var.'='.$val; } } } if ($debug && $addArgs) { echo '++ URL VARS ++'; dmp($addArgs); } if ($dir=='next' && (($smd_in_nearest) ? $smd_last : 1)) { if ($next_id) { $url = permlinkurl_id($next_id) . (($addArgs) ? '?'. join(a, $addArgs) : ''); if ($thing) { $thing = parse($thing); $next_title = escape_title($next_title); return doWrap(array(''), $wraptag, '', $class); } return $url; } else { return ($showalways) ? parse($thing) : ''; } } if ($dir=='prev' && (($smd_in_nearest) ? $smd_first : 1)) { if ($prev_id) { $url = permlinkurl_id($prev_id) . (($addArgs) ? '?'. join(a, $addArgs) : ''); if ($thing) { $thing = parse($thing); $prev_title = escape_title($prev_title); return doWrap(array(''), $wraptag, '', $class); } return $url; } else { return ($showalways) ? parse($thing) : ''; } } return; }