<?php

# 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_eventgTxt('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($filetrue);

                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$deffalse);

        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_nametrue1) : '';
    
$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,
        
neInput($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">',
        
nupload_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">',
        
nstartTable(),
        
ntr(
            
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_eventa.'step=smd_macro_delete' 'smd_macro_name='.urlencode($macro_name). '_txp_token='.form_token() . '" onclick="return confirm(\''.gTxt('confirm_delete_popup').'\');">[x]</a>')
            )
        ),
        
ntr(
            
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 
ntr(
        
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"')
        ),
        
ntr(
            
tda('<label>'.gTxt('smd_macro_tag_definition').'</label>')
            .
n.tda(
                
text_area('smd_macro_definition'250700$macro_def)
            )
        ),
        
ntr(td('&nbsp;').eInput($smd_macro_event).sInput('smd_macro_save').td(fInput('submit''save'gTxt('save')))),
        
ntInput(),
        
nendTable(),
        
n'</form>',
        
n'</div>',
        
nscript_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'false0'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_namefalse);

            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($macrotrue) && 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) ? 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 implode(n$full_macros);

    
// Inject the virtual tags into the global scope
    
eval($macros);
}