/** * smd_wrap * * A Textpattern CMS plugin for wrapping content with HTML tags, labels and attributes. * -> Adds wraptag / class / html_id / label support round any tag * -> Permits a range of formatting options for manipulating the item * (e.g. trim, escape, sanitize, change case, linkify, format date, * strip tags, split / combine, process with textile, etc). * -> If the content is empty, nothing is displayed. * -> Supports * * @author Stef Dawson * @link http://stefdawson.com/ */ // TODO: // * Allow hidden pref to determine default transforms? function smd_wrap($atts, $thing=NULL) { global $prefs; extract(lAtts(array( 'item' => '', 'wraptag' => '', 'class' => '', 'html_id' => '', 'label' => '', 'labeltag' => '', 'attr' => '', 'prefix' => '', 'suffix' => '', 'format' => '', // convenience only: same as transform 'transform' => '', 'delim' => ',', 'param_delim' => '|', 'trim' => 1, 'debug' => 0, ),$atts)); // item attribute trumps container $thing = ($item) ? $item : $thing; $out = ''; if ($format) { trigger_error("smd_wrap: format attribute deprecated: use transform attribute instead.", E_USER_NOTICE); $transform = $format; } // Grab the true portion of any container $truePart = EvalElse($thing, 1); if ($debug) { echo '++ TO WRAP ++'; dmp($item); } if ($thing) { // Handle custom attributes if ($attr) { $custom_atts = array(); $attribs = do_list($attr); foreach($attribs as $attdef) { list($key, $val) = do_list($attdef, $param_delim); $custom_atts[] = $key . '="' . $val . '"'; } $attr = ' ' . join(' ', $custom_atts); } // Run the Txp parser first $out = parse($truePart); $out = $trim ? trim($out) : $out; if ($out) { // Top and tail the output $out = $prefix.$out.$suffix; // Reformat the item with any of the following transformations, in the supplied order if ($transform) { $formats = do_list($transform, $delim); foreach ($formats as $xformlist) { // Use explode() because do_list() performs a trim() that we don't want $xform = explode($param_delim, $xformlist); $xtype = array_shift($xform); switch ($xtype) { case 'add': $pos = array_shift($xform); $val = $xform[0]; $out = (($pos == 'before' || $pos == 'both') ? $val : '') . $out . (($pos == 'after' || $pos == 'both') ? $val : ''); break; case 'case': foreach ($xform as $arg) { if ($arg == "upper") { $out = strtoupper($out); } else if ($arg == "lower") { $out = strtolower($out); } else if ($arg == "ucfirst") { $out = ucfirst($out); } else if ($arg == "ucwords") { $out = ucwords($out); } else if ($arg == "title") { // Inelegantly ported + extended for Unicode from David Gouch's JS title case script: thanks // http://individed.com/code/to-title-case/js/to-title-case.js $has_unicode = @preg_match('/\pL/u', 'a'); $az = ($has_unicode) ? '\p{Lu}' : 'A-Z'; $wrd = ($has_unicode) ? '(?:\p{L}|\p{M}|\p{N}|\p{Pc})' : '\w'; $capsre = '/[' . $az . ']+|&|[' . $wrd . ']+[._][' . $wrd . ']+/'; $smalls = get_pref('smd_wrap_small_words', 'a(nd?|s|t)?|b(ut|y)|en|for|i[fn]|o[fnr]|t(he|o)|vs?\.?|via'); $smallre = '/^(' . $smalls . ')[ \-]/i'; $pat = '/([' . $wrd . '&`\'‘’"“.@:\/\{\(\[<>_]+-? *)/'; $ret = array(); preg_match_all($pat, $out, $matches, PREG_PATTERN_ORDER|PREG_OFFSET_CAPTURE); foreach ($matches[0] as $it) { $match = $it[0]; $index = $it[1]; $idxm2 = $index - 2; $length = strlen($match); $title = $out; // Copy the original since we're working on $out directly // Fudge because substr with negative start counts from end of string in PHP $idxm1 = (($index-1) < 0) ? 0 : $index - 1; $offset = (($index-1) < 0) ? 1 : 2; if ($index > 0 && ( $title{$idxm2} !== ":" ) && ( preg_match($smallre, $match) > 0 ) ) { $out = substr($out, 0, $index) . strtolower($match) . substr($out, $index+$length); continue; } if (preg_match('/[\'\"_{(\[]/', substr($title, $idxm1, $offset)) > 0) { $out = substr($out, 0, $index) . $match{0} . @strtoupper($match{1}) . substr($match, 2). substr($out, $index+$length); continue; } if ( ( preg_match($capsre, substr($match, 1)) > 0 ) || ( preg_match('/[\])}]/', substr($title, $idxm1, $offset)) > 0 ) ) { $out = substr($out, 0, $index) . $match . substr($out, $index+$length); continue; } $out = substr($out, 0, $index) . strtoupper($match{0}) . substr($match, 1) . substr($out, $index+$length); } } } break; case 'date': $nd = (is_numeric($out)) ? $out : strtotime($out); if ($nd !== false) { $out = strftime($xform[0], $nd); } break; case 'escape': $flags = 0; foreach ($xform as $arg) { switch ($arg) { case 'no_quotes': $flags |= ENT_NOQUOTES; break; case 'all_quotes': $flags |= ENT_QUOTES; break; case 'double_quotes': $flags |= ENT_COMPAT; break; default: $flags |= $arg; break; } } $out = htmlspecialchars($out, $flags); break; case 'fordb': $out = doSlash($out); break; case 'form': foreach ($xform as $arg) { $content = fetch_form($arg); $reps = array( '{smd_wrap_it}' => $out, ); $out = parse(strtr($content, $reps)); } break; case 'link': // From http://codesnippets.joyent.com/posts/show/2104 $pat = "@\b(https?://)?(([0-9a-zA-Z_!~*'().&=+$%-]+:)?[0-9a-zA-Z_!~*'().&=+$%-]+\@)?(([0-9]{1,3}\.){3}[0-9]{1,3}|([0-9a-zA-Z_!~*'()-]+\.)*([0-9a-zA-Z][0-9a-zA-Z-]{0,61})?[0-9a-zA-Z]\.[a-zA-Z]{2,6})(:[0-9]{1,4})?((/[0-9a-zA-Z_!~*'().;?:\@&=+$,%#-]+)*/?)@"; $text = (isset($xform[0]) && $xform[0] != '') ? $xform[0] : '$0'; $out = preg_replace($pat, ''.$text.'', $out); break; case 'no_widow': $no_widow = isset($xform[0]) ? $xform[0] : @$prefs['title_no_widow']; $out = ($no_widow) ? noWidow($out) : $out; break; case 'replace': $type = $xform[0] ? $xform[0] : 'string'; // string / regex $from = $xform[1]; $to = isset($xform[2]) ? $xform[2] : ''; $out = ($type=='regex') ? preg_replace($from, $to, $out) : str_replace($from, $to, $out); break; case 'sanitize': if ($xform[0] == "url") { $out = sanitizeForUrl($out); } else if ($xform[0] == "file") { $out = sanitizeForFile($out); } else if ($xform[0] == "url_title") { $out = stripSpace($out, 1); } break; case 'split': $parts = explode($xform[0], $out); array_shift($xform); // Throw away the split character $joinchar = array_shift($xform); // Grab the specified parts to return $retstr = array(); $numParts = count($parts); foreach ($xform as $idx) { $addit = true; if ($idx == 'all') { $retstr = array_merge($retstr, $parts); $addit = false; } else if ($idx == 'last') { $idx = $numParts; } else if ($idx == 'rand') { $idx = mt_rand(1, $numParts); } else if (strpos($idx, '-') === 0) { // Negative offset: count from the end: -1 = last, -2 = penultimate, etc // The +1 is to counter the fact we subtract one in a moment. Damn zero indices $idx = $numParts - substr($idx, 1) + 1; } else if (strpos($idx, '>') === 0) { $retstr = array_merge($retstr, array_slice($parts, substr($idx, 1))); $addit = false; } else if (strpos($idx, '<') === 0) { $retstr = array_merge($retstr, array_slice($parts, 0, substr($idx, 1) - 1 )); $addit = false; } // Subtract one because the input is 'human' // e.g. split|.|+|1|3 == return the 1st and 3rd parts == $parts[0] + $parts[2] if ($addit) { $retstr[] = $parts[$idx-1]; } } $out = join($joinchar, $retstr); break; case 'strip_tags': $out = strip_tags($out); break; case 'textile': include_once txpath.'/lib/classTextile.php'; $textile = new Textile(); $out = $textile->TextileThis($out); break; case 'trim': $charlist = isset($xform[0]) ? $xform[0] : ''; $out = ($charlist) ? trim($out, $charlist) : trim($out); break; } } } } else { return parse(EvalElse($thing, 0)); } } return ($out) ? doLabel($label, $labeltag).doTag($out, $wraptag, $class, $attr, $html_id) : ''; }