# PLUGIN PREVIEW BY TEXTPATTERN.INFO
/**
* smd_macro
*
* A Textpattern CMS plugin for creating new tags:
* -> Define new <txp:tags> to do any task you like -- lightbox effect, comment scheme...
* -> Add arbitrary numbers of attributes and set defaults if you wish
* -> Import / export your creations to share new tags with the community
*
* @author Stef Dawson
* @link http://stefdawson.com/
* @todo Allow apostrophes / tags-in-tags in container code
*/
if (txpinterface === 'admin') {
global $smd_macro_event;
$smd_macro_event = 'smd_macro';
add_privs($smd_macro_event,'1,2');
register_tab('content', $smd_macro_event, gTxt('smd_macro_tab_name'));
register_callback('smd_macro_dispatcher', 'smd_macro');
register_callback('smd_macro_upload_form', $smd_macro_event.'_ui', 'inputlabel.'.$smd_macro_event.'-upload');
register_callback('smd_macro_welcome', 'plugin_lifecycle.smd_macro');
}
if (!defined('SMD_MACRO')) {
define("SMD_MACRO", 'smd_macro');
}
// Bootstrap the function insertion
register_callback('smd_macro_boot', 'pretext');
// ********************
// ADMIN SIDE INTERFACE
// ********************
// ------------------------
function smd_macro_dispatcher($evt, $stp)
{
global $smd_macro_event;
$available_steps = array(
'smd_macro_table_install' => true,
'smd_macro_table_remove' => true,
'smd_macro_prefsave' => true,
'smd_macro_save' => true,
'smd_macro_delete' => true,
);
if (!$stp or !bouncer($stp, $available_steps)) {
$stp = $smd_macro_event;
}
$stp();
}
// ------------------------
function smd_macro_welcome($evt, $stp)
{
$msg = '';
switch ($stp) {
case 'installed':
smd_macro_table_install(0);
$msg = 'Go go gadget macro!';
break;
case 'deleted':
smd_macro_table_remove(0);
break;
}
return $msg;
}
// ------------------------
function smd_macro($msg = '')
{
global $smd_macro_event;
if (!smd_macro_table_exist(1)) {
smd_macro_table_install(0);
}
$export_list = gps('smd_macro_export');
$max_file_size = get_pref('smd_macro_import_filesize', 15 * 1024);
if (gps('step') == 'smd_macro_export' && $export_list) {
// Generate an associative array that is translated to an ini file
$out = array();
$rows = safe_rows('*', SMD_MACRO, "macro_name IN ('".join("','", doSlash($export_list))."')");
foreach ($rows as $row) {
$atts = unserialize($row['attributes']);
$outatts = array();
foreach ($atts as $att => $vals) {
$outatts[] = utf8_decode(join('|', array($att, $vals['default'], $vals['rep'])));
}
$out[$row['macro_name']] = array(
'description' => $row['description'],
'attributes' => $outatts,
'definition' => $row['definition'],
);
}
$content = smd_macro_create_ini($out, array('definition' => 'base64_encode'));
header("Content-Type: text/plain; charset=utf-8");
header("Content-Disposition: attachment; filename=\"smd_macros.ini\"");
header("Content-Length: " . mb_strlen($content));
header("Content-Transfer-Encoding: binary");
header("Cache-Control: no-cache, must-revalidate, max-age=60");
header("Expires: Sat, 01 Jan 2000 12:00:00 GMT");
print($content);
exit;
} elseif (gps('step') == 'smd_macro_import') {
$overwrite = gps('smd_macro_import_overwrite');
$file = @get_uploaded_file($_FILES['thefile']['tmp_name'], get_pref('tempdir').DS.basename($_FILES['thefile']['tmp_name']));
if ($file === false) {
$msg = array(gTxt('smd_macro_cannot_import'), E_WARNING);
} else {
$size = filesize($file);
if ($max_file_size < $size) {
$msg = array(gTxt('smd_macro_file_size'), E_WARNING);
} else {
$ini = @parse_ini_file($file, true);
if ($ini) {
$done = array('ok' => array(), 'nok' => array(), 'skip' => array());
foreach ($ini as $key => $val) {
$atts = array();
$def = $desc = '';
if (isset($val['attributes'])) {
foreach ($val['attributes'] as $attlist) {
list($nam, $dflt, $rep) = explode('|', utf8_encode($attlist));
$atts[strtolower(str_replace('-', '_', sanitizeForUrl($nam)))] = array(
'default' => $dflt,
'rep' => $rep
);
}
}
if (isset($val['description'])) {
$desc = $val['description'];
}
if (isset($val['definition'])) {
$def = base64_decode($val['definition']);
}
$result = (string)smd_macro_save_direct($key, $desc, $atts, $def, $overwrite);
switch ($result) {
case 'SMD_MACRO_BAD_NAME':
case 'SMD_MACRO_CLASH':
$done['nok'][] = $key;
break;
case 'SMD_MACRO_SKIP':
$done['skip'][] = $key;
break;
default:
$done['ok'][] = $key;
break;
}
}
$msg = (($done['ok']) ? gTxt('smd_macro_imported') . join(', ', $done['ok']).br : '')
. (($done['skip']) ? gTxt('smd_macro_skipped') . join(', ', $done['skip']).br : '')
. (($done['nok']) ? gTxt('smd_macro_not_imported') . join(', ', $done['nok']) : '');
} else {
$msg = array(gTxt('smd_macro_invalid_ini'), E_WARNING);
}
}
unlink($file);
}
} elseif (gps('step') == 'smd_macro_clone') {
$source = safe_row('*', SMD_MACRO, "macro_name='".doSlash(gps('smd_macro_name'))."'");
$name = gps('smd_macro_new_name');
$desc = $source['description'];
$def = $source['definition'];
$atts = $source['attributes'];
$result = (string)smd_macro_save_direct($name, $desc, $atts, $def, false);
switch($result) {
case 'SMD_MACRO_BAD_NAME':
$msg = array(gTxt('smd_macro_invalid'), E_WARNING);
break;
case 'SMD_MACRO_CLASH':
case 'SMD_MACRO_SKIP':
$msg = array(gTxt('smd_macro_exists'), E_WARNING);
break;
default:
$msg = gTxt('smd_macro_created');
$_POST['smd_macro_name'] = $name; // Inject the new macro into the post stream so it's selected by default
break;
}
}
pagetop(gTxt('smd_macro_tab_name'), $msg);
$macros = safe_rows('*', SMD_MACRO, '1=1 ORDER BY macro_name');
$macro_name = $macro_new_name = gps('smd_macro_name');
$macro_description = $macro_def = $macro_code = '';
$macro_atts = array();
// Build the select list if possible
$macrolist = array();
foreach ($macros as $idx => $macro) {
$macrolist[$macro['macro_name']] = $macro['macro_name'];
if ($macro['macro_name'] == $macro_name) {
$macro_new_name = $macro['macro_name'];
$macro_description = $macro['description'];
$macro_atts = $macro['attributes'] ? unserialize($macro['attributes']) : array();
$macro_def = $macro['definition'];
}
}
$macroSelector = ($macrolist) ? selectInput('smd_macro_name', $macrolist, $macro_name, true, 1) : '';
$multi_macsel = '';
if ($macrolist) {
$multi_macsel = '<select name="smd_macro_export[]" id="smd_macro_export_list" size="8" class="list" multiple="multiple">'.n;
foreach ($macrolist as $key => $val) {
$multi_macsel .= '<option value="'.$key.'"' . (($macro_name==$key) ? ' selected="selected"' : '') .'>'.$val.'</option>'.n;
}
$multi_macsel .= '</select>';
}
$editcell = ($macrolist) ? '<label>'.gTxt('smd_macro_choose').'</label>'.n.$macroSelector : '';
// Edit form
echo n, '<h1 class="txp-heading">', gTxt('smd_macro_tab_name'), '</h1>',
n, '<div id="smd_macro_control" class="txp-control-panel">',
n, '<form id="smd_macro_select" action="index.php" method="post">',
n, '<p id="smd_macro_select">',
n, $editcell,
n, eInput($smd_macro_event),
n, '</form>',
// Import / export form
n, '<a id="smd_macro_import" href="#">'.gTxt('smd_macro_import').'</a>',
n, (($multi_macsel)
? ' / ' . '<a id="smd_macro_export" href="#">'.gTxt('smd_macro_export').'</a>'
. (($macro_name) ? ' / ' . '<a id="smd_macro_clone" href="#">'.gTxt('smd_macro_clone').'</a>' : '')
: ''),
n, '<div id="smd_macro_import_holder" class="smd_hidden">',
n, upload_form(gTxt('smd_macro_upload'), '', 'smd_macro_import', $smd_macro_event, ''),
n, '</div>',
n, (($multi_macsel)
? '<div id="smd_macro_export_holder" class="smd_hidden"><form id="smd_macro_export_form" action="index.php" method="post">'
. $multi_macsel . br
. fInput('submit', 'smd_macro_export_go', gTxt('go'), '', '', 'smd_macro_export_close();')
. eInput($smd_macro_event)
. sInput('smd_macro_export')
. '</form></div>'
. '<div id="smd_macro_clone_holder" class="smd_hidden"><form id="smd_macro_clone_form" action="index.php" method="post">'
. hInput('smd_macro_name', $macro_name)
. fInput('text', 'smd_macro_new_name', '')
. fInput('submit', 'smd_macro_clone_go', gTxt('go'))
. eInput($smd_macro_event)
. sInput('smd_macro_clone')
. tInput()
. '</form></div>'
: ''
),
n, '</div>';
echo n, '<div class="txp-container">',
n, '<form id="smd_macro_form" action="index.php" method="post">',
n, startTable(),
n, tr(
fLabelCell('name', '', 'smd_macro_new_name')
.n.td(
fInput('text', 'smd_macro_new_name', $macro_new_name).fInput('hidden', 'smd_macro_name', $macro_name)
.(($macro_name == '') ? '' : sp.'<a href="?event='.$smd_macro_event. a.'step=smd_macro_delete' . a . 'smd_macro_name='.urlencode($macro_name). a . '_txp_token='.form_token() . '" onclick="return confirm(\''.gTxt('confirm_delete_popup').'\');">[x]</a>')
)
),
n, tr(
fLabelCell('description', '', 'smd_macro_description')
.n.td(
fInput('text', 'smd_macro_description', $macro_description, '', '', '', '66')
)
);
$attlist = array('<table id="smd_macro_attlist"><thead><tr><th>'.gTxt('name').'</th><th>'.gTxt('default').'</th><th>'.gTxt('smd_macro_repname').'</th></tr></thead><tbody>');
$macro_atts = empty($macro_atts) ? array('' => array('default' => '', 'rep' => '')) : $macro_atts;
foreach ($macro_atts as $attname => $att) {
$attlist[] = '<tr class="smd_macro_att">'
.'<td>'.fInput('text', 'smd_macro_attname[]', $attname). '</td>'
.'<td>'.fInput('text', 'smd_macro_attdflt[]', $att['default']). '</td>'
.'<td>'.fInput('text', 'smd_macro_attrep[]', $att['rep']). '</td>'
.'</tr>';
}
$attlist[] = '</tbody></table>';
$atts = join(n, $attlist);
echo n, tr(
tda('<label>'.gTxt('smd_macro_attributes').'</label>'.n.'<a href="#" id="smd_macro_att_add" title="'.gTxt('smd_macro_att_clone_help').'">'.gTxt('smd_macro_att_clone').'</a>')
.tda($atts, ' id="smd_macro_att"')
),
n, tr(
tda('<label>'.gTxt('smd_macro_tag_definition').'</label>')
.n.tda(
text_area('smd_macro_definition', 250, 700, $macro_def)
)
),
n, tr(td(' ').eInput($smd_macro_event).sInput('smd_macro_save').td(fInput('submit', 'save', gTxt('save')))),
n, tInput(),
n, endTable(),
n, '</form>',
n, '</div>',
n, script_js(<<<EOJS
jQuery(function() {
jQuery('#smd_macro_att_add').click(function(ev) {
var obj = jQuery('#smd_macro_attlist tbody');
var elems = jQuery('.smd_macro_att');
// Add the row, empty it and focus. Can't do this in any fewer jQuery() calls for some reason
obj.append(obj.children().eq(0).clone());
obj.children().last().find('input:text').val('');
obj.children().last().find('input:text').first().focus();
ev.preventDefault();
});
// Import link
jQuery('#smd_macro_import').click(function(ev) {
jQuery('#smd_macro_export_holder').hide('normal');
jQuery('#smd_macro_clone_holder').hide('normal');
jQuery('#smd_macro_import_holder').toggle('normal');
});
// Export link
jQuery('#smd_macro_export').click(function(ev) {
jQuery('#smd_macro_import_holder').hide('normal');
jQuery('#smd_macro_clone_holder').hide('normal');
jQuery('#smd_macro_export_holder').toggle('normal');
});
// Clone link
jQuery('#smd_macro_clone').click(function(ev) {
jQuery('#smd_macro_import_holder').hide('normal');
jQuery('#smd_macro_export_holder').hide('normal');
jQuery('#smd_macro_clone_holder').toggle('normal').find("input[name='smd_macro_new_name']").focus();
});
});
function smd_macro_export_close() {
jQuery("#smd_macro_export").click();
}
EOJS
),
<<<EOCSS
<style type="text/css">
.smd_hidden {
display:none;
}
</style>
EOCSS;
}
// ------------------------
// Add an overwrite checkbox to the upload form
function smd_macro_upload_form($evt, $stp, $data, $args)
{
return $data .
checkbox('smd_macro_import_overwrite', '1', false, 0, 'smd_macro_import_overwrite') .
'<label for="smd_macro_import_overwrite">' . gTxt('smd_macro_overwrite') . '</label>';
}
// ------------------------
// Takes function params; assumes attribute sanitization done already
function smd_macro_save_direct($name = '', $desc = '', $atts = array(), $def = '', $overwrite = false)
{
global $smd_macro_event;
$smd_macro_new_name = sanitizeForUrl($name);
$smd_macro_description = doSlash($desc);
$smd_macro_definition = $def;
$ret = '';
if (smd_macro_valid($smd_macro_new_name)) {
$code = doSlash(smd_macro_build($smd_macro_new_name, $atts, $smd_macro_definition));
$smd_macro_definition = doSlash($smd_macro_definition);
$smd_macro_atts = is_array($atts) ? serialize($atts) : $atts;
if (smd_macro_table_exist()) {
// Check if this macro name clashes with a built-in PHP/Txp function
$exists = smd_macro_exists($smd_macro_new_name, false);
if ($exists === false) {
$indb = safe_field('macro_name', SMD_MACRO, "macro_name='$smd_macro_new_name'");
if ($indb) {
if ($overwrite) {
// Update
$ret = safe_update(SMD_MACRO, "macro_name='$smd_macro_new_name', description='$smd_macro_description', attributes='$smd_macro_atts', definition='$smd_macro_definition', code='$code'", "macro_name='$smd_macro_new_name'");
} else {
$ret = 'SMD_MACRO_SKIP';
}
} else {
// Insert
$ret = safe_insert(SMD_MACRO, "macro_name='$smd_macro_new_name', description='$smd_macro_description', attributes='$smd_macro_atts', definition='$smd_macro_definition', code='$code'");
}
} else {
$ret = 'SMD_MACRO_CLASH';
}
}
} else {
$ret = 'SMD_MACRO_BAD_NAME';
}
// $ret may be empty only if macro table not installed
return $ret;
}
// ------------------------
// Takes URL params; sanitizes attributes locally
function smd_macro_save()
{
global $smd_macro_event;
extract(doSlash(gpsa(array(
'smd_macro_name',
'smd_macro_new_name',
'smd_macro_description',
))));
$msg = '';
$smd_macro_new_name = sanitizeForUrl($smd_macro_new_name);
if (smd_macro_valid($smd_macro_new_name)) {
$att_name = gps('smd_macro_attname');
$att_dflt = gps('smd_macro_attdflt');
$att_rep = gps('smd_macro_attrep');
// Don't doSlash() this yet
$smd_macro_definition = gps('smd_macro_definition');
$atts = array();
foreach($att_name as $idx => $att) {
if ($att == '') continue;
$atts[strtolower(str_replace('-', '_', sanitizeForUrl($att)))] = array(
'default' => $att_dflt[$idx],
'rep' => $att_rep[$idx]
);
}
$code = doSlash(smd_macro_build($smd_macro_new_name, $atts, $smd_macro_definition));
$smd_macro_definition = doSlash($smd_macro_definition);
$smd_macro_atts = serialize($atts);
if (smd_macro_table_exist()) {
$exists = ($smd_macro_name != $smd_macro_new_name) && smd_macro_exists($smd_macro_new_name);
if ($exists === false) {
// Inject the new name so it's selected in the dropdown if renamed
$_POST['smd_macro_name'] = $smd_macro_new_name;
if ($smd_macro_name == '') {
// Insert
$ret = safe_insert(SMD_MACRO, "macro_name='$smd_macro_new_name', description='$smd_macro_description', attributes='$smd_macro_atts', definition='$smd_macro_definition', code='$code'");
$msg = gTxt('smd_macro_created');
} else {
// Update
$ret = safe_update(SMD_MACRO, "macro_name='$smd_macro_new_name', description='$smd_macro_description', attributes='$smd_macro_atts', definition='$smd_macro_definition', code='$code'", "macro_name='$smd_macro_name'");
$msg = gTxt('smd_macro_saved');
}
} else {
$msg = array(gTxt('smd_macro_exists'), E_WARNING);
}
}
} else {
$msg = array(gTxt('smd_macro_invalid'), E_WARNING);
}
smd_macro($msg);
}
// ------------------------
function smd_macro_delete()
{
global $smd_macro_event;
$macro_name = doSlash(gps('smd_macro_name'));
$ret = safe_delete(SMD_MACRO, "macro_name='$macro_name'");
$msg = gTxt('smd_macro_deleted');
$_GET['smd_macro_name'] = '';
smd_macro($msg);
}
// ------------------------
// Check the macro doesn't exist and also that it doesn't clash with an internal PHP/Txp function
function smd_macro_exists($macro, $check_db = true)
{
$ret = ($check_db) ? safe_field('macro_name', SMD_MACRO, "macro_name='$macro'") : false;
if ($ret === false) {
$fns = get_defined_functions();
foreach ($fns as $flist) {
if ($ret === false) {
$ret = in_array($macro, $flist);
}
}
}
return $ret;
}
// ------------------------
// Check the macro name is a valid PHP function name
function smd_macro_valid($macro)
{
return is_callable($macro, true) && preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $macro);
}
// ------------------------
function smd_macro_build($macro, $args, $def)
{
$arglist = $reps = array();
$lAtts = '';
// Validate macro name and bail if it'd cause problems
// Build arg list and replacements array.
// Replacements are prefixed with the name of the macro for clash minimisation
$args = is_array($args) ? $args : unserialize($args);
foreach ($args as $arg => $vals) {
$arglist[] = "'$arg' => '".$vals['default']."'";
$rep = empty($vals['rep']) ? $arg : $vals['rep'];
$reps[] = '"{' . $rep . '}" => "$'.$macro.'_'.$arg.'"';
}
$reps[] = '"{smd_container}" => "$'.$macro.'_smd_container"';
$arg_string = join(','.n, $arglist);
$rep_string = join(','.n, $reps);
$def = doSlash($def);
// Only create lAtts if there's at least one attribute defined
if ($arg_string) {
$lAtts = <<<EOATT
extract(lAtts(array(
{$arg_string}
),\$atts), EXTR_PREFIX_ALL, '{$macro}');
EOATT;
}
$full_macro = <<< EOFN
function {$macro}(\$atts, \$thing = NULL) {
{$lAtts}
\${$macro}_smd_container = (\$thing)? \$thing : (isset(\${$macro}_smd_container) ? \${$macro}_smd_container : '');
\$out = '';
\$out = strtr(stripslashes('{$def}'), array({$rep_string}));
return parse(\$out);
}
EOFN;
return $full_macro;
}
// ------------------------
// Create ini file contents and return it.
// $special is an array of keynames and a function to apply to them (e.g. 'definition' => 'base64_encode')
function smd_macro_create_ini($arr, $special=array())
{
$content = '';
foreach ($arr as $key => $elem) {
$content .= "[".$key."]\n";
foreach ($elem as $subkey => $subelem) {
$subelem = (array_key_exists($subkey, $special)) ? doArray($subelem, $special[$subkey]) : $subelem;
if (is_array($subelem)) {
$num = count($subelem);
for ($idx = 0; $idx < $num; $idx++) {
$content .= $subkey . "[] = \"" . $subelem[$idx] . "\"\n";
}
} elseif ($subelem == "") {
$content .= $subkey . " = \n";
} else {
$content .= $subkey . " = \"" . $subelem . "\"\n";
}
}
}
return $content;
}
// ------------------------
// Add macro table if not already installed
function smd_macro_table_install($showpane = '1')
{
global $DB;
$GLOBALS['txp_err_count'] = 0;
$ret = '';
$sql = array();
$sql[] = "CREATE TABLE IF NOT EXISTS `".PFX.SMD_MACRO."` (
`macro_name` varchar(32) NOT NULL default '',
`description` varchar(255) NULL default '' COLLATE utf8_general_ci,
`attributes` text NULL COLLATE utf8_general_ci,
`definition` mediumtext NULL COLLATE utf8_general_ci,
`code` mediumtext NULL,
PRIMARY KEY (`macro_name`)
) ENGINE=MyISAM, CHARACTER SET=utf8";
if (gps('debug')) {
dmp($sql);
}
foreach ($sql as $qry) {
$ret = safe_query($qry);
if ($ret===false) {
$GLOBALS['txp_err_count']++;
echo "<b>".$GLOBALS['txp_err_count'].".</b> ".mysql_error()."<br />\n";
echo "<!--\n $qry \n-->\n";
}
}
$flds = getThings('describe `'.PFX.SMD_MACRO.'`');
if (!in_array('definition',$flds)) {
safe_alter(SMD_MACRO, "add `definition` mediumtext NULL default '' after `attributes`");
}
// Upgrade table collation if necessary
$ret = @safe_field("COLLATION_NAME", "INFORMATION_SCHEMA.COLUMNS", "table_name = '".PFX.SMD_MACRO."' AND table_schema = '" . $DB->db . "' AND column_name = 'description'");
if ($ret != 'utf8_general_ci') {
$ret = safe_alter(SMD_MACRO, 'CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci');
}
// Spit out results
if ($GLOBALS['txp_err_count'] == 0) {
if ($showpane) {
$msg = gTxt('smd_macro_tbl_installed');
smd_macro($msg);
}
} else {
if ($showpane) {
$msg = gTxt('smd_macro_tbl_not_installed');
smd_macro($msg);
}
}
}
// ------------------------
// Drop table if in database
function smd_macro_table_remove()
{
$ret = '';
$sql = array();
$GLOBALS['txp_err_count'] = 0;
if (smd_macro_table_exist()) {
$sql[] = "DROP TABLE IF EXISTS " .PFX.SMD_MACRO. "; ";
if (gps('debug')) {
dmp($sql);
}
foreach ($sql as $qry) {
$ret = safe_query($qry);
if ($ret===false) {
$GLOBALS['txp_err_count']++;
echo "<b>".$GLOBALS['txp_err_count'].".</b> ".mysql_error()."<br />\n";
echo "<!--\n $qry \n-->\n";
}
}
}
if ($GLOBALS['txp_err_count'] == 0) {
$msg = gTxt('smd_macro_tbl_removed');
} else {
$msg = gTxt('smd_macro_tbl_not_removed');
smd_macro($msg);
}
}
// ------------------------
function smd_macro_table_exist($all = '')
{
if ($all) {
$tbls = array(SMD_MACRO => 5);
$out = count($tbls);
foreach ($tbls as $tbl => $cols) {
if (gps('debug')) {
echo "++ TABLE ".$tbl." HAS ".count(@safe_show('columns', $tbl))." COLUMNS; REQUIRES ".$cols." ++".br;
}
if (count(@safe_show('columns', $tbl)) == $cols) {
$out--;
}
}
return ($out===0) ? 1 : 0;
} else {
if (gps('debug')) {
echo "++ TABLE ".SMD_MACRO." HAS ".count(@safe_show('columns', SMD_MACRO))." COLUMNS;";
}
return(@safe_show('columns', SMD_MACRO));
}
}
// *********************
// PUBLIC SIDE INTERFACE
// *********************
// ------------------------
// Extracts all prebuilt macros (essentially, new tags) and injects them into the global scope so they can be called
function smd_macro_boot()
{
$full_macros = array();
$toRegister = array();
$reg = '';
$rs = safe_rows('*', SMD_MACRO, '1=1');
foreach ($rs as $row) {
$toRegister[] = "->register('" . txpspecialchars($row['macro_name']) . "')";
$full_macros[] = str_replace('\r\n','
', $row['code']); // yukky newline workaround
}
if (count($toRegister) > 0) {
$reg = "if (class_exists('\Textpattern\Tag\Registry')) {
Txp::get('\Textpattern\Tag\Registry')"
. (implode(n, $toRegister))
.";
}";
}
$macros = $reg . n . implode(n, $full_macros);
// Inject the virtual tags into the global scope
eval($macros);
}