// v0.1 Initial public release
// v0.11 Added !category support
// v0.12 Added textpos option (thanks lee) and imageid option (thanks wouaren)
// v0.2 js and css can now be loaded elsewhere (thanks jstubbs). Added wraptag and changed defaults to make output less like tag-soup (thanks wouaren)
// v0.21 Added orderby="fixed" to allowed fixed imageid ordering (thanks lee). $where warning squashed (thanks benjibot)
// v0.22 Now uses smd_lib and subcats. Also MLP(Multi-lingual)-aware.
// v0.23 Fixed orderby when using random (thanks mrdale) and added ability to override maxlimit
// v0.24 Added libcheck (thanks papuass). Numeric ranges now allowed for IDs (smd_lib). Fixed empty field/imageid negation bugs. Fixed next/prev buttons for MLP.
// v0.24b Fixed paging in subdirectory installations (thanks ultramega and Qwest)
// v0.25 Extended groupname (thanks [Axel])
// v0.25a Bundled latest version of slimbox in the package. Made the docs easier to read.
// v0.26 Added true multiple gallery support (thanks wheaticus)
// v0.27 Added thumb (thanks progre55)
// v0.28 Added imagecaption (thanks Adam_V)
// v0.29 Added thumbsize (thanks Simanek)
// v0.29a Fixed undefined index warnings (thanks Ambitiouslemon)
// v0.29b Fixed the fix from v0.24 regarding an SQL error if the category is not set when using ?c (thanks Mr.Smith). Also fixed 'px' in thumbsize (thanks robin746)
// v0.210 Uses smd_lib v0.3. Integer ranges now only allowed in imageid. Added thumbtext/thumbtextclass/paramdelim. Deprecated showalt/showcaption/altclass/captionclass/textpos
// v0.211 Fixed (more) undefined index warnings and added onchange text support (thanks karl.spencer, decoderltd). thumbtext can now take (encoded) rudimentary html tags
// v0.3 Uses smd_lib v0.31. Added trigger, class and linkclass, deprecated smd_slimbox_inc, removed libcheck and the deprecated atts from v0.210. Moonbox, Imagebox and Thickbox (jQuery) now supported
// DEPRECATED
function smd_slimbox_inc($atts) {
global $production_status;
if (in_array($production_status, array('debug', 'testing'))) {
trigger_error("smd_slimbox_inc is no longer necessary. Please remove it from your page.");
}
return;
}
function smd_slimbox($atts) {
require_plugin('smd_lib');
// MLP support
global $pretext, $thisarticle, $img_dir, $smd_sboxLang;
$smd_sboxStr = array(
'next_lbl' => 'Next',
'prev_lbl' => 'Prev',
);
$smd_sboxLang = new smd_MLP('smd_slimbox', 'smd_sbox', $smd_sboxStr);
// ** LOOKUP TABLES
// 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.
$fieldMap = array(
"name" => "name",
"id" => "id",
"alt" => "alt",
"caption" => "caption",
"category" => "category",
"author" => "author",
"date" => "date",
"ext" => "ext",
"w" => "width",
"h" => "height",
);
$showLocs = array("below","above","after","before","onchange");
$classTypes = array_merge(array("group","cell","link","img","thumbtext","nav","prev","next"),$showLocs);
$frequencies = array("pos", "step", "when", "even", "odd");
$operands = array("changes", "contains", "eq", "lt", "gt", "lte", "gte", "not");
$thumbholder = "::SBOX_REPL::";
$catidopts = (isset($atts['category']) || isset($atts['imageid'])) ? true : false;
// Plugin options
extract(lAtts(array(
'paramdelim' => ':',
'imageid' => '',
'category' => '',
'subcats' => false,
'catlabel' => 'article-',
'trigger' => 'lightbox',
'groupname' => 'smd',
'thumb' => false,
'limit' => '0',
'maxlimit' => '99999',
'orderby' => 'category',
'showpagelinks' => '1',
'galleryid' => '?4:6',
'imagecaption' => '?caption',
'thumbtext' => '',
'thumbsize' => '',
'prevlabel' => $smd_sboxLang->gTxt('prev_lbl'),
'nextlabel' => $smd_sboxLang->gTxt('next_lbl'),
'wraptag' => '',
'class' => '',
'appendclasses' => false,
'groupclass' => '', // shortcut for class="group:"
'cellclass' => '', // shortcut for class="cell:"
'linkclass' => '', // shortcut for class="link:"
'imgclass' => '', // shortcut for class="img:"
'navclass' => '', // shortcut for class="nav:"
'prevclass' => 'smd_slimbox_prev', // shortcut for class="prev:"
'nextclass' => 'smd_slimbox_next', // shortcut for class="next:"
'thumbtextclass' => '', // shortcut for class="thumbtext:"
'belowclass' => '', // shortcut for class="below:"
'aboveclass' => '', // shortcut for class="above:"
'afterclass' => '', // shortcut for class="after:"
'beforeclass' => '', // shortcut for class="before:"
'onchangeclass' => '', // shortcut for class="onchange:"
), $atts));
// Category relies on whether imageid 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) : (($imageid=="") ? $pretext['c']: '');
$fixedOrder = false;
// Main code body
// $freqTable = new smd_freqStore($appendclasses);
// $freqTable.addType(array("fixed","cyclic","match"));
// The trigger is the first part of rel= attribute (before groupname)
// ToDo: experiment: add option to suppress trigger and use jQuery to add the rel= attributes when a single thumb/text
// link is used to "launch" the gallery (http://forum.textpattern.com/viewtopic.php?pid=146188#p146188)
$trigger .= ($trigger) ? "-" : "";
// The gallery identifier is used for keeping track of next/prev thumbpage links
$gallID = "";
if ($galleryid == "" || substr($galleryid,0,1) == "?") {
// Generate a (hopefully) unique ID for this gallery instance
$gallTmp = md5($category.$imageid.$orderby.$limit.$maxlimit.$wraptag);
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;
}
// Load the class position arrays with relevant info
foreach ($classTypes as $type) {
$fullclass = ${$type.'class'};
if ($fullclass != "") {
$classdefs = smd_split($fullclass, false, ";");
foreach ($classdefs as $classdef) {
$classfreqs = smd_split($classdef, false, "@");
$classname = $classfreqs[0];
if (count($classfreqs) > 1) {
$frequency = smd_split($classfreqs[1], true, "=");
$freq = (in_array($frequency[0], $frequencies)) ? $frequency[0] : '';
if (count($frequency) > 1) {
$positions = smd_split($frequency[1], true, "|");
} else {
$positions = array();
}
// TODO: Handle field names with ? and !
switch ($freq) {
case "pos":
foreach ($positions as $pos) {
$fixedPos[$type][$pos][] = $classname;
}
break;
case "even":
$cyclePos[$type][] = '2,0,'.$classname;
break;
case "odd":
$cyclePos[$type][] = '2,1,'.$classname;
break;
case "step":
foreach ($positions as $pos) {
$stepoffs = smd_split($pos, true, "+");
$step = $stepoffs[0];
$offset = (count($stepoffs) > 1) ? $stepoffs[1] : 0;
$cyclePos[$type][] = $step . ',' . $offset . ',' .$classname;
}
break;
}
} else {
}
}
}
}
dmp($fixedPos);
dmp($cyclePos);
// Convert the category (possibly a comma-sep list) into an SQL-style 'in' list.
$fullCatList = array();
$notCatList = array();
$catVars = array("?c", "!c", "?s", "!s", "?t", "!t", "?id", "!id", "?field", "!field");
list($fullCatList,$notCatList) = smd_getOpts($category, $catVars, $catlabel, false);
// Ditto with imageid, except ranges are allowed
$fullIDList = array();
$notIDList = array();
$idVars = array("?id", "!id", "?field", "!field");
list($fullIDList,$notIDList) = smd_getOpts($imageid, $idVars);
// Make up the WHERE statement from the combined lists.
// The categories/image IDs are ORed together in brackets first
// before the negation options are tacked on as ANDs afterwards
$whereOR = array();
$whereAND = array();
// Start the WHERE statement with the combined list of image IDs
if (count($fullIDList) > 0) {
for ($idx = count($fullIDList); $idx >=0; $idx--) {
if (empty($fullIDList[$idx])) {
unset($fullIDList[$idx]);
} else {
$fullIDList[$idx] = "'" . $fullIDList[$idx] . "'";
}
}
if (count($fullIDList) > 0) {
$whereOR[] = "id IN (" . implode(",", $fullIDList). ")";
}
}
// included categories - some may be subcats
$tmpa = array();
for ($idx = 0; $idx < count($fullCatList); $idx++) {
if ($subcats) {
$categs = getTree($fullCatList[$idx],'image');
for ($jdx = 0; $jdx < count($categs); $jdx++) {
$tmpa[] = "'" .$categs[$jdx]['name']. "'";
}
} else {
$tmpa[] = "'" .$fullCatList[$idx]. "'";
}
}
if (count($tmpa) > 0) {
$whereOR[] = "category IN (" .implode(",", $tmpa). ")";
}
if (count($whereOR) > 0) {
$where = "(" . implode(" OR ", $whereOR) . ")";
} else {
$where = "";
}
// excluded categories
$tmpa = array();
for ($idx = 0; $idx < count($notCatList); $idx++) {
if ($subcats) {
$categs = getTree($notCatList[$idx],'image');
for ($jdx = 0; $jdx < count($categs); $jdx++) {
$tmpa[] = "'" .$categs[$jdx]['name']. "'";
}
} else {
$tmpa[] = "'" .$notCatList[$idx]. "'";
}
}
if (count($tmpa) > 0) {
$whereAND[] = "category NOT IN (" .implode(",", $tmpa). ")";
}
// Add to the WHERE statement the combined list of NOT image IDs
if (count($notIDList) > 0) {
for ($idx = count($notIDList); $idx >=0; $idx--) {
if (empty($notIDList[$idx])) {
unset($notIDList[$idx]);
} else {
$notIDList[$idx] = "'" . $notIDList[$idx] . "'";
}
}
if (count($notIDList) > 0) {
$whereAND[] = "id NOT IN (" . implode(",", $notIDList). ")";
}
}
if (count($whereAND) > 0) {
$where .= (($where=="") ? "" : " AND ") . implode(" AND ", $whereAND);
}
if ($where == "") {
if (!$catidopts) {
// If no category/id options given, choose from the whole database
$where = "1=1";
} else {
// If a category/id option was chosen then the values must be empty.
// In this case the plugin should choose from a bogus category that should not exist.
$where = "category='smd_".mt_rand()."'";
}
}
// Convert the orderby 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 orderby 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
$whereOB = Array();
$postOrder = array();
$rule = "sql";
$postPrefix = "SORT_";
$randsort = false;
$obList = preg_split('/[,\s]+/',$orderby,-1,PREG_SPLIT_NO_EMPTY);
for ($idx = 0; $idx < count($obList); $idx++) {
$ob_dir = explode(':',$obList[$idx]);
$direction = '';
$colRef = '';
if (count($ob_dir) <= 1) {
$ob_dir[1] = "asc";
}
if ($rule == "sql") {
$direction = ($ob_dir[1] == "desc") ? ' desc' : ' asc';
} else {
$direction = ($ob_dir[1] == "desc") ? $postPrefix.'DESC' : $postPrefix.'ASC';
}
switch ($ob_dir[0]) {
case 'random':
$rule = "post";
if ($idx == 0) {
$whereOB[] = 'rand()';
} else {
$randsort = true;
}
break;
case 'fixed':
// 'fixed' only works on image ids
if ($imageid != "") {
$fixedOrder = true;
}
break;
default:
$colRef = array_search($ob_dir[0], $fieldMap);
break;
}
if ($rule == "sql") {
if ($colRef != "") {
$whereOB[] = $colRef.$direction;
}
} else {
if (!$randsort) {
if ($colRef != "") {
$postOrder[] = array("col" => $colRef, "sort" => $direction);
}
}
}
}
if (count($whereOB) > 0) {
$where .= ' ORDER BY ' . implode(",",$whereOB);
}
// The database fields required
$fields = implode(",", array_keys($fieldMap));
// Handle paging
if (($limit > 0) && $showpagelinks) {
$rs = safe_rows($fields, 'txp_image', $where .' LIMIT 0,'.$maxlimit);
$numthum = count($rs);
$numPages = ($numthum > 0) ? ceil($numthum/$limit) : 1;
$thumbpage = (!gps($gallID)) ? 1 : gps($gallID);
$offset = ($thumbpage - 1) * $limit;
$rs = safe_rows($fields, 'txp_image', $where .' LIMIT '.$offset.','.$limit);
} else {
// No paging required
$numPages = 1;
$thumbpage = 1;
$rs = safe_rows($fields, 'txp_image', $where .' LIMIT 0,'. (($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($fullIDList); $idx++) {
foreach($rs as $row) {
if ("'".$row['id']."'" == $fullIDList[$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
$thumbWidth = $thumbHeight = "";
$thumbWUnits = $thumbHUnits = "";
if ($thumbsize) {
$thumbSizes = smd_split(trim($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;
}
}
// Prepare the additional text details to show, if required
$showopts = smd_split($thumbtext, false, "/,|,s/");
$posArray = array();
foreach ($showopts as $item) {
$showpos = smd_split($item, false, "/".$paramdelim."/");
if (count($showpos) > 1 && in_array($showpos[0],$showLocs)) {
$posArray[$showpos[0]] = explode(" ", $showpos[1]);
} else {
$posArray[$showLocs[0]] = explode(" ", ((count($showpos)>1) ? $showpos[1] : $showpos[0]));
}
}
// Create the class replacement lookups
$classDefs = smd_split($class, false);
foreach ($classDefs as $classDef) {
$classObjs = smd_split($classDef, false, ":");
$validObj = false;
for ($idx = 0; $idx < count($classObjs); $idx++) {
if ($idx == 0) {
// First item is the object to apply the class to
$validObj = (in_array($classObjs[0], $classTypes)) ? true : false;
} else if ($validObj) {
$classFreqs = smd_split($classObjs[$idx], true, "@");
if (count($classFreqs) > 0) {
foreach ($classFreqs as $classFreq) {
}
} else {
// cyclic frequency: note that a class applied to every element is cyclic (a cycle length of 1!)
$classCycle = smd_split($classObjs[$idx], true, ";");
}
}
}
}
// Construct the output
$captionItems = smd_split($imagecaption, false, "\s");
$thumbtextPlate = ''.$thumbholder.'';
$outStr = '';
if($rs) {
$ctr = 1;
$thumbtextCurr="";
foreach($rs as $row) {
$row['ctr'] = $ctr++; // Add a 'static' counter to the array
$theThumb = hu.$img_dir.'/' . $row['id'] . 't' . $row['ext'];
$full = hu.$img_dir.'/' . $row['id'] . (($thumb) ? "t" : "") .$row['ext'];
if ($groupname === "?c") {
$grp = $row['category'];
} else if (substr($groupname,0,1) === "?") {
// Split off the field name from the question mark
$fieldname = substr($groupname,1);
// Is it a standard image field? If not, see if it is an article field
$grpID = array_search($fieldname, $fieldMap);
if ($grpID === false) {
if (isset($thisarticle[$fieldname])) {
$grp = $thisarticle[$fieldname];
} else {
$grp = '';
}
} else {
$grp = $row[$grpID];
}
} else {
$grp = $groupname;
}
// Handle imagecaption
$captionArray = array();
foreach ($captionItems as $item) {
if (substr($item,0,1) === "?") {
$fieldname = substr($item,1);
$key = array_search($fieldname, $fieldMap);
if ($key) {
$captionArray[] = $row[$key];
} else {
$captionArray[] = $fieldname;
}
} else {
$captionArray[] = $item;
}
}
// Handle thumbtext
$thumbtextArray = array_combine($showLocs, array("","","","","")); // Initialise every element
foreach ($posArray as $pos => $text) {
foreach ($text as $item) {
if (substr($item,0,1) === "?") {
$fieldname = substr($item,1);
$key = array_search($fieldname, $fieldMap);
if ($key) {
$thumbtextArray[$pos][] = $row[$key];
} else {
$thumbtextArray[$pos][] = $fieldname;
}
} else {
$thumbtextArray[$pos][] = $item;
}
}
$thumbtextArray[$pos] = implode(" ", $thumbtextArray[$pos]);
}
// Construct the link and put thumbtext in the appropriate position
$outStr .= ($thumbtextArray["onchange"] && $thumbtextArray["onchange"] != $thumbtextCurr) ? str_replace($thumbholder, htmlspecialchars_decode($thumbtextArray["onchange"]), $thumbtextPlate) : '';
$outStr .= ($wraptag != "") ? '<' .$wraptag. (($cellclass != "") ? ' class="' .$cellclass. '"' : '') . '>' : '';
$outStr .= ($thumbtextArray["above"]) ? str_replace($thumbholder, htmlspecialchars_decode($thumbtextArray["above"]), $thumbtextPlate) : '';
$outStr .= '' .(($thumbtextArray["before"]) ? str_replace($thumbholder, htmlspecialchars_decode($thumbtextArray["before"]), $thumbtextPlate) : ''). '' .(($thumbtextArray["after"]) ? str_replace($thumbholder, htmlspecialchars_decode($thumbtextArray["after"]), $thumbtextPlate) : ''). ''.n;
$outStr .= ($thumbtextArray["below"]) ? str_replace($thumbholder, htmlspecialchars_decode($thumbtextArray["below"]), $thumbtextPlate) : '';
$outStr .= ($wraptag != "") ? '' .$wraptag. '>' : '';
$outStr .= n;
$thumbtextCurr = $thumbtextArray["onchange"];
}
// Add the paging features if required
if (($limit > 0) && $showpagelinks) {
$next = ($numPages > 1 && $thumbpage != $numPages);
$prev = ($numPages > 1 && $thumbpage > 1);
// Replace any paging info in the query string
$prevPage = smd_addQSVar($pretext['request_uri'], $gallID, $thumbpage-1);
$nextPage = smd_addQSVar($pretext['request_uri'], $gallID, $thumbpage+1);
$outStr .= ($wraptag != "") ? '<' .$wraptag. (($navclass != "") ? ' class="' .$navclass. '"' : '') . '>' : '';
$outStr .= ($prev) ? '' .$prevlabel. ''.n : '';
$outStr .= ($next) ? '' .$nextlabel. ''.n : '';
$outStr .= ($wraptag != "") ? '' .$wraptag. '>' : '';
$outStr .= n;
}
}
return $outStr;
}