function smd_gallery($atts, $thing='') { global $pretext, $thisarticle, $img_dir; // ** LOOKUP TABLE // Holds database field as key index and plugin option names as value. // Use array_search to find if value exists and return correct key for use in db calls/array refs. $fieldMap = array( "name" => "name", "id" => "id", "alt" => "alt", "caption" => "caption", "category" => "category", "author" => "author", "date" => "date", "ext" => "ext", "w" => "width", "h" => "height", ); // Plugin options extract(lAtts(array( 'type' => 'image', 'delim' => ',', 'paramdelim' => ':', 'form' => '', 'id' => '', 'category' => '', 'directory' => '', 'match' => '', 'sublevel' => '0', 'sort' => 'category', 'offset' => '0', 'limit' => '0', 'maxlimit' => '99999', 'thumblimit' => '99999', 'thumboffset' => '0', 'thumb' => '0', 'thumbsize' => '', 'text' => '', 'collate' => '', 'combo' => '', 'pageform' => '', 'navwraptag' => 'ul', 'navbreak' => 'li', 'navclass' => 'smd_gallery_nav', 'galleryid' => '?4:6', 'wraptag' => '', 'break' => '', 'class' => '', 'debug' => '0', ), $atts)); // Category relies on whether id has been specified or not. // lAtts() won't let us handle this logic so it has to be done manually $category = isset($category) ? doSlash($category) : (($id=='') ? $pretext['c']: ''); // Handle forms and set a default tag if none specified $thing = (empty($form)) ? $thing : fetch_form($form); $thing = (empty($thing)) ? '' : $thing; // Paging init $paging = 0; if ($pageform) { $paging = 1; $pageform = fetch_form($pageform); } // The gallery identifier is used for keeping track of next/prev thumbnail page links $gallID = ''; if ($galleryid == '' || substr($galleryid,0,1) == "?") { // Generate a (fairly likely) unique ID for this gallery instance $gallTmp = md5($category.$id.$directory.$match.$sublevel.$sort.$limit.$maxlimit); list($gallLen, $gallSkip) = explode(":", substr($galleryid,1)); $gallLen = (empty($gallLen)) ? "4" : $gallLen; $gallSkip = (empty($gallSkip)) ? "1" : $gallSkip; for ($idx = 0, $cnt = 0; $cnt < $gallLen; $cnt++, $idx = (($idx+$gallSkip) % strlen($gallTmp))) { $gallID .= $gallTmp[$idx]; } } else { $gallID = $galleryid; } $gallID = "smd_glry_".$gallID; // Avoid (already incredibly unlikely) namespace clashes // The manner of obtaining files differs if using directory vs category/id if ($directory) { $rs = array(); $rsall = smd_doDirectory($directory, 0, $sublevel, $match, $delim); list($numPages, $thumPage, $offset) = smd_getPage($rsall, $gallID, $limit, $offset, $paging); $dirlimit = ($limit==0) ? $maxlimit : $limit; $rs = array_slice($rsall, $offset, $dirlimit); $postOrder = array(); $fixedOrder = 0; } else { // Process categories/IDs and make up the crux of the where clause. // Clause order = IDs OR cats AND !cats AND !IDs $clause[] = '1=1'; $clauseOR = array(); // Surround each entry in the ID list with quotes and make an SQL "in" list $subinfo = ($sublevel) ? "image:".$sublevel : ''; list($idinc, $idexc) = smd_doList($id, true, '', true, $delim); list($catinc, $catexc) = smd_doList($category, false, $subinfo, true, $delim); if ($idinc) { $clauseOR[] = 'id IN ('.implode(",", $idinc).')'; } if ($catinc) { $clauseOR[] = 'category IN ('.implode(",", $catinc).')'; } if (!empty($clauseOR)) { $clause[] = implode(" OR ", $clauseOR); } $clause = implode(" AND ", $clause); $clause .= ($catexc) ? ' AND category NOT IN ('.implode(",", $catexc).')' : ''; $clause .= ($idexc) ? ' AND id NOT IN ('.implode(",", $idexc).')' : ''; if ($debug > 1) { echo "++ INCLUDED/EXCLUDED IDs AND CATs ++"; dmp($idinc); dmp($idexc); dmp($catinc); dmp($catexc); } // Convert the sort into a valid sorting hierarchy. // Note that random is a special case and the following rules apply: // 1) Anything appearing before random is passed to the database query for ordering // 2) As soon as random is encountered, the rest of the sort string is ignored. // The resultset will be sorted randomly instead // 3) If random appears first in the list, the data is retrieved randomly then the // remaining options sort the resultset $clauseSort = array(); $postOrder = array(); $rule = "sql"; $postPrefix = "SORT_"; $fixedOrder = $randsort = false; $sort = do_list($sort, $delim); for ($idx = 0; $idx < count($sort); $idx++) { $sort_dir = explode(' ', $sort[$idx]); $direction = $colRef = ''; if (count($sort_dir) <= 1) { $sort_dir[1] = "asc"; } if ($rule == "sql") { $direction = ($sort_dir[1] == "desc") ? ' desc' : ' asc'; } else { $direction = ($sort_dir[1] == "desc") ? $postPrefix.'DESC' : $postPrefix.'ASC'; } switch ($sort_dir[0]) { case 'random': $rule = "post"; if ($idx == 0) { $clauseSort[] = 'rand()'; } else { $randsort = true; } break; case 'fixed': // 'fixed' only works on ids $fixedOrder = ($id == '') ? false : true; break; default: $colRef = array_search($sort_dir[0], $fieldMap); break; } if ($rule == "sql") { if ($colRef != '') { $clauseSort[] = $colRef.$direction; } } else { if (!$randsort) { if ($colRef != '') { $postOrder[] = array("col" => $colRef, "sort" => $direction); } } } } if (count($clauseSort) > 0) { $clause .= ' ORDER BY ' . implode(",",$clauseSort); } if ($debug > 0) { echo "++ WHERE CLAUSE ++"; dmp($clause); } // The database fields required $fields = implode(",", array_keys($fieldMap)); // Time to grab the matching rows from the database $rsall = safe_rows($fields, 'txp_image', $clause .' LIMIT '.$offset.','.$maxlimit); list($numPages, $thumPage, $offset) = smd_getPage($rsall, $gallID, $limit, $offset, $paging); $rs = safe_rows($fields, 'txp_image', $clause .' LIMIT ' .$offset. ','. (($limit==0) ? $maxlimit : $limit)); } // Is post-ordering required? Do it if ($randsort) { shuffle($rs); } else if (count($postOrder) > 0) { // Translate the rows into columns that can be sorted foreach($rs as $key => $row) { $col_id[$key] = $row['id']; $col_name[$key] = $row['name']; $col_caption[$key] = $row['caption']; $col_alt[$key] = $row['alt']; $col_category[$key] = $row['category']; $col_date[$key] = $row['date']; $col_ext[$key] = $row['ext']; $col_author[$key] = $row['author']; $col_w[$key] = $row['w']; $col_h[$key] = $row['h']; } // Make up an array_multisort arg list and execute it for ($idx = 0; $idx < count($postOrder); $idx++) { $sortargs[] = '$col_'.$postOrder[$idx]['col']; $sortargs[] = $postOrder[$idx]['sort']; } $sortit = 'array_multisort('.implode(", ",$sortargs).', $rs);'; eval($sortit); } // Is fixed-order output desired? If so, re-order the $rs if ($fixedOrder) { $orderedRS = Array(); $ignoreList = Array(); $remainingRS = Array(); // Suck out the fixed items first for ($idx = 0; $idx < count($idinc); $idx++) { foreach($rs as $row) { if ("'".$row['id']."'" == $idinc[$idx]) { $orderedRS[] = $row; $ignoreList[] = $row['id']; } } } // Tack on the remaining rows foreach($rs as $row) { if (!in_array($row['id'], $ignoreList)) { $remainingRS[] = $row; } } $rs = array_merge($orderedRS, $remainingRS); } // Handle thumbsize - of limited use now but still available $thumbWidth = $thumbHeight = ''; $thumbWUnits = $thumbHUnits = ''; if ($thumbsize) { $thumbSizes = smd_split($thumbsize, false, ",\s", 0); if (count($thumbSizes) == 1) { $thumbWidth = $thumbHeight = ereg_replace("[^0-9]", '', $thumbSizes[0]); $units = ereg_replace("[0-9]", '', $thumbSizes[0]); $thumbWUnits = $thumbHUnits = ($units == '') ? $thumbWUnits : $units; } else { $thumbWidth = ereg_replace("[^0-9]", '', $thumbSizes[0]); $thumbHeight = ereg_replace("[^0-9]", '', $thumbSizes[1]); $units = ereg_replace("[0-9]", '', $thumbSizes[0]); $thumbWUnits = ($units == '' && $thumbWidth != '') ? $thumbWUnits : $units; $units = ereg_replace("[0-9]", '', $thumbSizes[1]); $thumbHUnits = ($units == '' && $thumbHeight != '') ? $thumbHUnits : $units; } } // Collation defers parsing the form for each returned item. // Instead it outputs the data as a list at the end (items may be individually quoted or extracted if required) //TODO : Add ALWAYS switch as a shortcut to specify that all matching items (regardless of #1, #2, #3 etc) are to be quoted if ($collate) { $dblQuote = $sglQuote = array('SMDNONE'); $collateOpts = do_list($collate, $delim); foreach ($collateOpts as $option) { $indexes = do_list($option, $paramdelim); $colType = array_shift($indexes); // Supplying 'quote' on its own will empty the relevant array, implying "ALL" fields are to be quoted switch ($colType) { case "sglquote": $sglQuote = $indexes; break; case "dblquote": case "quote": $dblQuote = $indexes; break; } } } // Combos are user-defined combinations of replacement modifiers (e.g. idext: {id}{ext}, ... $combos = array(); if ($combo) { $combo = do_list($combo, $delim); foreach ($combo as $newDef) { list($defParts, $unwanted) = smd_doList($newDef, false, '', false, $paramdelim); $defName = "{".array_shift($defParts)."}"; $combos[$defName] = $defParts[0]; } } if ($debug > 1) { echo "++ COMBOs ++"; dmp($combos); } // Construct the output $out = array(); $collations = array(); if ($debug > 2) { echo "++ RECORD SET ++"; dmp($rs); } if ($debug > 3) { echo "++ AVAILABLE '?' VARS ++"; dmp($pretext); dmp($thisarticle); } if($rs) { $idx = 0; $ctr = 1; foreach($rs as $row) { if ($directory) { $urlPath = $row['path'].DS; } else { $urlPath = hu.$img_dir.DS; } $theThumb = $urlPath . $row['id'] . 't' . $row['ext']; $fullSize = $urlPath . $row['id'] . (($thumb) ? "t" : '') .$row['ext']; // Is a thumb/text object required this time? $object = ''; if ($idx >= $thumboffset && $idx < $thumboffset+$thumblimit) { switch ($type) { case image: $object = ($text) ? $text : ''.$row['alt'].''; break; } } // Construct the replacement array $replacements = array( '{id}' => $row['id'], '{name}' => $row['name'], '{ext}' => $row['ext'], '{category}' => $row['category'], '{author}' => $row['author'], '{hasthumb}' => $row['thumbnail'], '{url}' => $fullSize, '{thumburl}' => $theThumb, '{imagepath}' => $urlPath, '{imagedef}' => basename($fullSize), '{thumbdef}' => basename($theThumb), '{alt}' => $row['alt'], '{title}' => $row['caption'], '{thumbwidth}' => $thumbWidth, '{thumbheight}' => $thumbHeight, '{thumbwunits}' => $thumbWUnits, '{thumbhunits}' => $thumbHUnits, '{width}' => $row['w'], '{height}' => $row['h'], '{date}' => $row['date'], '{object}' => $object, '{galleryid}' => $gallID, ); // Append any combos foreach ($combos as $defName => $newDef) { if (!array_key_exists($defName, $replacements)) { $replacements[$defName] = strtr($newDef, $replacements); } } // Solos are items in the output form that require details from a specific row. Useful only in collation // mode, they are added to the replacements array on an as-needed basis to save space/time $soloRE = '/\{([a-z0-9]+)#'.$ctr.'\}/'; $numSolos = preg_match_all($soloRE, $thing, $solos); for ($soloCtr = 0; $soloCtr < $numSolos; $soloCtr++) { $fieldname = '{'.$solos[1][$soloCtr].'#'.$ctr.'}'; $grabfield = '{'.$solos[1][$soloCtr].'}'; $replacements[$fieldname] = $replacements[$grabfield]; } if ($debug > 0) { echo "++ REPLACEMENTS : IMAGE ".$ctr." ++"; dmp($replacements); } // In collate mode the form is only parsed at the end: build a collosal multi-dimension array of all items here if ($collate) { foreach ($replacements as $defName => $defVal) { $collations[$defName][] = $defVal; } } else { $out[] = parse(strtr($thing, $replacements)); } $idx++; $ctr++; } if ($collate) { foreach ($collations as $item => $list) { // Quote the lists if required $list = (empty($dblQuote) || in_array($item, $dblQuote)) ? doArray($list, 'smd_doDblQuote') : $list; $list = (empty($sglQuote) || in_array($item, $sglQuote)) ? doArray($list, 'doQuote') : $list; $collations[$item] = implode($delim, $list); } if ($debug > 0) { echo "++ COLLATIONS ++"; dmp($collations); } $out[] = parse(strtr($thing, $collations)); } // Add the paging features if required if (($limit > 0) && $paging) { $navout = array(); $next = ($numPages > 1 && $thumPage != $numPages); $prev = ($numPages > 1 && $thumPage > 1); // Replace any paging info in the query string $prevPage = smd_addQSVar($pretext['request_uri'], $gallID, $thumPage-1); $nextPage = smd_addQSVar($pretext['request_uri'], $gallID, $thumPage+1); $thisPage = smd_addQSVar($pretext['request_uri'], $gallID, $thumPage); $firstPage = smd_addQSVar($pretext['request_uri'], $gallID, 1); $lastPage = smd_addQSVar($pretext['request_uri'], $gallID, $numPages); // Labels can be specified in the replacement tag after a paramdelim. // 'earlier' and 'later' don't have labels. Anything specified after paramdelim is the max number of pages to show; empty = all pages $label = array( '{navfirst}' => 'First', '{navprev}' => '«', '{navearlier}' => '', '{navthis}' => $thumPage, '{navthisurl}' => $thumPage, '{navlater}' => '', '{navnext}' => '»', '{navlast}' => 'Last', ); $labelRE = '/\{(nav[a-z]+)'.$paramdelim.'(.+?)\}/'; $numLabels = preg_match_all($labelRE, $pageform, $labels); for ($labelCtr = 0; $labelCtr < $numLabels; $labelCtr++) { $fieldname = '{'.$labels[1][$labelCtr].'}'; $label[$fieldname] = $labels[2][$labelCtr]; $pageform = str_replace($labels[0][$labelCtr], '{'.$labels[1][$labelCtr].'}', $pageform); } if ($debug > 1) { echo "++ PAGING LABELS ++"; dmp($label); } // Generate 'earlier' and 'later' lists $numEarlier = intval($label['{navearlier}']); $numLater = intval($label['{navlater}']); $numEarlier = ($numEarlier && ($thumPage-$numEarlier > 1)) ? $numEarlier : $thumPage-1; $numLater = ($numLater && ($thumPage+$numLater < $numPages)) ? $numLater : $numPages-$thumPage; $navEarlier = $navLater = array(); for ($idx = $thumPage-$numEarlier; $idx < $thumPage; $idx++) { $navEarlier[] = ''.$idx.''; } for ($idx = $thumPage+1; $idx <= $thumPage+$numLater; $idx++) { $navLater[] = ''.$idx.''; } $navEarlier = doWrap($navEarlier, '', $navbreak, ''); $navLater = doWrap($navLater, '', $navbreak, ''); // Make up the replacements array for pageform $replacements = array( '{navfirst}' => (($prev) ? doTag(''.$label['{navfirst}'].'', $navbreak) : doTag($label['{navfirst}'], $navbreak)), '{navprev}' => (($prev) ? doTag(''.$label['{navprev}'].'', $navbreak) : doTag($label['{navprev}'], $navbreak)), '{navearlier}' => $navEarlier, '{navthis}' => doTag($label['{navthis}'], $navbreak, 'current'), '{navthisurl}' => doTag(''.$label['{navthisurl}'].'', $navbreak, 'current'), '{navlater}' => $navLater, '{navnext}' => (($next) ? doTag(''.$label['{navnext}'].'', $navbreak) : doTag($label['{navnext}'], $navbreak)), '{navlast}' => (($next) ? doTag(''.$label['{navlast}'].'', $navbreak) : doTag($label['{navlast}'], $navbreak)), ); if ($debug > 0) { echo "++ PAGING REPLACEMENTS ++"; dmp($replacements); } // Perform all substitutions and wrap the entire lot if required $navout[] = parse(strtr($pageform, $replacements)); $out[] = doWrap($navout, $navwraptag, '', $navclass); } if ($debug > 2) { echo "++ FINAL OUTPUT ++"; dmp($out); } } return doWrap($out, $wraptag, $break, $class); } // Return an expanded list of items: // 1) Anything containing '?' or '!' is checked for a match with a TXP field // 2) Any ranges of items are expanded (e.g. 4-7 = 4,5,6,7) // 3) TXP fields may themselves be lists or ranges // 4) Anything that is not a TXP field is used verbatim // 5) The items are returned as 2 lists; inclusion and exclusion // Args: // lst = the list as a delimited string // rng = whether to allow ranges or not (bool) // sub = the type of subcategory to traverse (image, file, link, article, none="") and how many levels to go down (e.g. image:2) // qte = whether to quote each item in the array or not (bool) // dlm = the delimiter (string) if (!function_exists("smd_doList")) { function smd_doList($lst, $rng=true, $sub="", $qte=true, $dlm=",") { global $thisarticle, $pretext; $inc = $exc = array(); $lst = do_list($lst, $dlm); $modRE = '/(\?|\!)([A-Za-z0-9_\- ]+)/'; foreach ($lst as $item) { $mod = 0; // 0 = include, 1 = exclude $numMods = preg_match_all($modRE, $item, $mods); for ($modCtr = 0; $modCtr < $numMods; $modCtr++) { // mod "type" is governed by the first one found only. i.e. if "article-?c!s" was used in one field // it would be an "include" of the word "article-" plus the category and section concatenated $mod = ($mods[1][0] === "!") ? 1 : 0; $modChar = $mods[1][$modCtr]; $modItem = trim($mods[2][$modCtr]); $lowitem = strtolower($modItem); if (array_key_exists($lowitem, $pretext)) { $item = str_replace($modChar.$modItem, $pretext[$lowitem], $item); } else if (isset($thisarticle[$lowitem])) { $item = str_replace($modChar.$modItem, $thisarticle[$lowitem], $item); } else { $item = str_replace($modChar.$modItem, $modItem, $item); } } // Handle ranges of values if ($rng && preg_match('/^(\d+)\-(\d+)$/', $item)) { list($lo, $hi) = explode("-", $item, 2); $item = implode($dlm, range($lo, $hi)); } // Item may be empty; ignore it if so if ($item) { $item = do_list($item, $dlm); // Handle sub-categories if ($sub) { list($subtype, $level) = explode(":", $sub); $level = (empty($level)) ? 0 : $level; $level = (strtolower($level)=="all") ? 99999 : $level; foreach ($item as $cat) { $cats = getTree(doslash($cat), $subtype); foreach ($cats as $jdx => $val) { if ($cats[$jdx]['level'] <= $level) { $item[] = $cats[$jdx]['name']; } } } } // Quote if asked $item = ($qte) ? doArray($item, 'doQuote') : $item; if ($mod === 0) { $inc = array_unique(array_merge($inc, $item)); } else { $exc = array_unique(array_merge($exc, $item)); } } } return array($inc, $exc); } } // Alternative to the core's doQuote(). This one dbl-quotes instead of sgl-quotes if (!function_exists("smd_doDblQuote")) { function smd_doDblQuote($val) { return '"'.$val.'"'; } } // Work out which page this gallery is on and return the relevant info function smd_getPage($rs, $gallID, $limit, $offset, $paging) { if (($limit > 0) && $paging) { $numThums = count($rs); $numPages = ($numThums > 0) ? ceil($numThums/$limit) : 1; $thumPage = (!gps($gallID)) ? 1 : gps($gallID); $offset = ($thumPage - 1) * $limit; } else { // No paging required $numPages = 1; $thumPage = 1; } return array($numPages, $thumPage, $offset); } // Read the given delimited directories and get all files that match. // Go down as many subdir levels as given in the $sub parameter function smd_doDirectory($lst, $lvl=0, $sub=0, $match="", $dlm=",", $ctr=0) { list($dirinc, $direxc) = smd_doList($lst, false, $sub, false, $dlm); list($matchinc, $matchexc) = smd_doList($match, false, '', false, $dlm); $matchinc = ".*" .(($matchinc) ? "(" .implode("|",$matchinc).")+.*" : ""); $matchexc = ($matchexc) ? ".*(" .implode("|",$matchexc).")+.*" : "smd_glry_".mt_rand(); $sub = (empty($sub)) ? 0 : $sub; $sub = (strtolower($sub)=="all") ? 99999 : $sub; $fileList = array(); foreach ($dirinc as $dir) { if ($handle = @opendir($dir)) { while (($file = @readdir($handle)) !== false) { $fullpath = $dir.DS.$file; if (preg_match("/".$matchinc."/", $fullpath) && !preg_match("/".$matchexc."/", $fullpath)) { if (is_dir($fullpath)) { if (($file == ".") || ($file== "..") || (in_array($file, $direxc)) || ($lvl >= $sub)) { continue; } else { // Recurse the subdir. Note that the '!' is temporarily added back to each direxc item $tmp = $direxc; // Don't overwrite the original array array_unshift($tmp, $fullpath); $subFiles = smd_doDirectory(implode($dlm."!", $tmp), $lvl+1, $sub, $match, $dlm, $ctr); $fileList = array_merge($fileList, $subFiles); $ctr += count($subFiles); continue; } } else { $filestat = stat($fullpath); $imgsize = getimagesize($fullpath); $fileList[$ctr]['path'] = $dir; $fileList[$ctr]['name'] = basename($fullpath); $fileList[$ctr]['id'] = preg_replace('/\..+$/', '', $file); $fileList[$ctr]['alt'] = ''; $fileList[$ctr]['caption'] = ''; $parts = explode(DS, $dir); $fileList[$ctr]['category'] = (count($parts) > 1) ? end($parts) : ''; $fileList[$ctr]['author'] = strval($filestat['uid']); $fileList[$ctr]['date'] = strftime("%Y-%m-%d %H:%M:%S", $filestat['ctime']); $parts = explode(".", $file); $fileList[$ctr]['ext'] = (count($parts) > 1) ? ".".end($parts) : ''; $fileList[$ctr]['w'] = (is_array($imgsize)) ? strval($imgsize[0]) : ''; $fileList[$ctr]['h'] = (is_array($imgsize)) ? strval($imgsize[1]) : ''; } $ctr++; } } } if ($handle) { @closedir($handle); } } return $fileList; } if (!function_exists("smd_removeQSVar")) { function smd_removeQSVar($url, $key) { $url = preg_replace('/(.*)(\?|&)' . $key . '=[^&]+?(&)(.*)/i', '$1$2$4', $url . '&'); $url = substr($url, 0, -1); return ($url); } } if (!function_exists("smd_addQSVar")) { function smd_addQSVar($url, $key, $value) { $url = smd_removeQSVar($url, $key); if (strpos($url, '?') === false) { return ($url . '?' . $key . '=' . $value); } else { return ($url . '&' . $key . '=' . $value); } } }