// TODO:
// Tag to output articles by the current author?
// Some way of specifying a default list/radio/checkbox item
// Use an array like $types = array( 'int' => array( sql_type='INTEGER', widget="TEXT", widget_constraint="is_integer" ), );
// --> Populate the select box from this array
// Add 'index' checkbox to allow an Index to be added to a meta column
// Ability to specify wildcards/matches for items/authors in the client tag
if (@txpinterface == 'admin') {
add_privs('smd_bio', '1');
register_tab('extensions', 'smd_bio', smd_bio_gTxt('admin_tab'));
register_callback('smd_bio', 'smd_bio');
register_callback('smd_bio_fields', 'author_ui', 'extend_detail_form');
register_callback('smd_bio_admin_js', 'admin_side', 'head_end');
// Note these are all pre TXP's involvement
register_callback('smd_bio_save', 'admin', 'author_save', 1);
register_callback('smd_bio_save', 'admin', 'author_save_new', 1);
register_callback('smd_bio_delete', 'admin', 'admin_multi_edit', 1);
}
$step = gps('step');
if ($step == 'smd_bio_get_image') {
smd_bio_get_image();
}
global $smd_bio_types;
$smd_bio_types = array(
'text_input' => 'Text box',
'list' => 'Select list',
'radio' => 'Radio set',
'yesnoradio' => 'Yes/no radio',
'checkbox' => 'Checkbox(es)',
'textarea' => 'Text area',
'image' => 'Image',
'datetime' => 'Date/Time'
);
if (!defined('SMD_BIO')) {
define("SMD_BIO", 'smd_bio');
}
if (!defined('SMD_BIO_META')) {
define("SMD_BIO_META", 'smd_bio_meta');
}
// ************************
// ADMIN -> USERS TAB
// ------------------------
function smd_bio_fields($event, $step, $mt, $data) {
$out = $vals = array();
$rstep = gps('step');
if(smd_bio_table_exist()) {
if ($rstep == 'author_edit') {
extract(gpsa(array('user_id')));
// Shame we have to double de-clutch here but we can't index on user_id unfortunately ('cos when
// inserting new users, the bio functions run _BEFORE_ an auto_increment ID has been generated by TXP)
$uname = safe_field('name','txp_users',"user_id = '$user_id'");
$vals = safe_row('*', SMD_BIO, "user_ref='$uname'");
}
$widgets = safe_rows('*', SMD_BIO_META, '1=1 ORDER BY position');
$tdac = ' class="noline"';
$tdac_image = ' class="noline smd_bio_image_cell"';
foreach ($widgets as $widget) {
$val = ($vals && isset($vals[$widget['name']])) ? $vals[$widget['name']] : $widget['val'];
$title = ($widget['title']) ? $widget['title'] : $widget['name'];
$sizeopts = do_list($widget['size']);
$size1 = ($sizeopts[0] > 0) ? $sizeopts[0] : '';
$size2 = (isset($sizeopts[1]) && $sizeopts[1] > 0) ? $sizeopts[1] : '';
$name = 'smd_bio_'.$widget['name'];
switch($widget['type']) {
case 'list':
$selv = smd_bio_splitval($widget['val']);
$out[] = tr(fLabelCell($title).tda(selectInput($name, $selv, $val, true, '', $name), $tdac), ' class="smd_bio_select '.$name.'"');
break;
case 'radio':
$selv = smd_bio_splitval($widget['val']);
$out[] = tr(fLabelCell($title).tda(radioSet($selv, $name, $val), $tdac), ' class="smd_bio_radio '.$name.'"');
break;
case 'yesnoradio':
$out[] = tr(fLabelCell($title).tda(yesnoRadio($name, $val), $tdac), ' class="smd_bio_radio '.$name.'"');
break;
case 'checkbox':
$selv = smd_bio_splitval($widget['val']);
$checkedVals = explode(',', $val);
$items = array();
foreach ($selv as $idx => $lbl) {
// Not using checkbox() because it doesn't support onclick
$items[] = ''.$lbl;
}
$out[] = tr(fLabelCell($title).tda(join('', $items), $tdac), ' class="smd_bio_checkbox '.$name.'"').fInput('hidden',$name,$val,'','','','','',$name);
break;
case 'textarea':
// Not using text_area() because it forces a styled width/height which should be in CSS
$size1 = ($size1 == '' || $size1 == 0) ? 40 : $size1;
$size2 = ($size2 == '' || $size2 == 0) ? 5 : $size2;
$out[] = tr(fLabelCell($title).tda('', $tdac), ' class="smd_bio_textarea '.$name.'"');
break;
case 'image':
$parent = $widget['val'];
$val = ($vals && isset($vals[$widget['name']])) ? $vals[$widget['name']] : '';
$where = ($parent) ? "category='$parent'" : '1=1';
$tree = safe_rows('*', 'txp_image', $where);
$selv = array();
foreach ($tree as $row) {
$selv[$row['id']] = $row['name'];
}
$out[] = tr(fLabelCell($title).tda(''.selectInput($name.'_list', $selv, $val, true, '').'
', $tdac_image. ' id="'.$name.'"'), ' class="smd_bio_image_row '.$name.'"');
break;
case 'datetime':
case 'text_input':
// Not using fInput() because it has no MAXLENGTH property
$size1 = ($size1 == '' || $size1 == 0) ? 25 : $size1;
$size2 = ($size2 == '' || $size2 == 0) ? 25 : $size2;
$out[] = tr(fLabelCell($title).tda('', $tdac), ' class="smd_bio_text_input '.$name.'"');
break;
}
}
}
return join('', $out);
}
// ------------------------
// AJAX call
function smd_bio_get_image() {
global $img_dir;
$id = gps('id');
if ($id) {
$rs = safe_row('*', 'txp_image', "id = '$id'");
extract($rs);
$out = array();
$out['thumb'] = ($thumbnail) ? hu.$img_dir.'/'.$id.'t'.$ext : '';
$out['image'] = hu.$img_dir.'/'.$id.$ext;
$out['w'] = $w;
$out['h'] = $h;
$out['thw'] = ($thumb_w) ? $thumb_w : '';
$out['thh'] = ($thumb_h) ? $thumb_h : '';
send_xml_response($out);
} else {
send_xml_response();
}
}
// ------------------------
function smd_bio_admin_js($event, $step) {
$css = safe_field('css', 'txp_css', "name='smd_bio'");
if ($css) {
$css = '';
}
echo <<
//');
} else {
jQuery("#" + id + " .smd_bio_image").fadeOut().empty();
}
}
);
}
// Force the image to show if editing an author
jQuery(".smd_bio_image_id").blur();
});
//]]>
{$css}
EOJS;
}
// ------------------------
// Make the name/val pairs for selectInput / radio / checkbox sets
function smd_bio_splitval($val) {
$selv = array();
$wvals = explode(',', $val);
foreach ($wvals as $wval) {
$prts = explode('=>', $wval);
if (count($prts) == 1) {
$prts[1] = $prts[0];
$prts[0] = sanitizeForUlr($prts[0]);
}
$selv[trim($prts[0])] = trim($prts[1]);
}
return $selv;
}
// ------------------------
function smd_bio_save($event, $step) {
if (smd_bio_table_exist()) {
$targetvars = array();
extract(doSlash(psa(array('privs', 'name', 'email', 'RealName', 'user_id'))));
$length = function_exists('mb_strlen') ? mb_strlen($name, '8bit') : strlen($name);
if (($user_id || $name) and $length <= 64 and is_valid_email($email)) {
foreach ($_POST as $idx => $item) {
if (strpos($idx, 'smd_bio_') === 0) {
$targetvars[] = $idx;
}
}
// Double de-clutch again... dammit :-(
$user_id = gps('user_id');
$user_ref = safe_field('name','txp_users',"user_id = '$user_id'");
extract(gpsa($targetvars));
$bcols = getThings('describe `'.PFX.SMD_BIO.'`');
$sqlSet = array();
foreach ($targetvars as $var) {
$colname = str_replace('smd_bio_', '', $var);
if (in_array($colname, $bcols)) {
$sqlSet[] = "`$colname` = '".doSlash($$var)."'";
}
}
if ($sqlSet) {
$rs = safe_upsert(SMD_BIO, join(',', $sqlSet), "`user_ref` = '$user_ref'");
}
}
}
}
// ------------------------
function smd_bio_delete($event, $step) {
global $txp_user;
if (smd_bio_table_exist()) {
// Since we are executing 'pre' delete we need to unfortunately duplicate some of the checks
// from txp_admin.php so we minimise the opportunity to delete someone by mistake
$selected = ps('selected');
$method = ps('edit_method');
if (!$selected or !is_array($selected)) {
return;
}
if ($method != 'delete') {
return;
}
$names = safe_column('name', 'txp_users', "name IN ('".join("','", doSlash($selected))."') AND name != '".doSlash($txp_user)."'");
if (!$names) return;
$assign_assets = ps('assign_assets');
if ($assign_assets === '') {
return;
} elseif (in_array($assign_assets, $names)) {
return;
} else {
// All the checks passed -- do it
safe_delete(SMD_BIO, "user_ref IN ('".join("','", doSlash($names))."')");
}
}
}
// ************************
// BIO CONFIGURATION
// ------------------------
// Tab handler
function smd_bio($event, $step) {
if(!$step or !in_array($step, array(
'smd_bio_meta_add',
'smd_bio_meta_del',
'smd_bio_meta_save',
))) {
smd_bio_config('');
} else $step();
}
// ------------------------
function smd_bio_config($message) {
if (!smd_bio_table_exist()) {
smd_bio_install('');
exit;
}
pagetop(smd_bio_gTxt('admin_tab'), $message);
echo smd_bio_meta_edit();
echo smd_bio_meta_list();
}
// ------------------------
function smd_bio_meta_edit() {
global $smd_bio_types;
$vars = array('step', 'id', 'title', 'name', 'type', 'size', 'val', 'position');
$rs = array();
extract(gpsa($vars));
if ($id && $step == 'meta_edit') {
$id = assert_int($id);
$rs = safe_row('*', SMD_BIO_META, "id = $id");
extract($rs);
}
if ($step == 'smd_bio_meta_save' || $step == 'smd_bio_meta_add' || $step == 'smd_bio_meta_del') {
foreach ($vars as $var) {
$$var = '';
}
}
$caption = smd_bio_gTxt(($step == 'meta_edit') ? 'meta_edit' : 'meta_add');
// Make the name/val pairs for selectInput
$selv = array();
foreach ($smd_bio_types as $widx => $wval) {
$selv[$widx] = $wval;
}
return form(
hed($caption, 3,' style="text-align: center;"').
startTable('edit', '', 'edit-pane').
tr(
fLabelCell('name').
($id && $step == 'meta_edit' ? td(strong($name)) : fInputCell('name', $name))
).
tr(
fLabelCell('title').
fInputCell('title', $title)
).
tr(
fLabelCell('type').
td(selectInput('type', $selv, $type, false))
).
tr(
fLabelCell(smd_bio_gTxt('size')).
fInputCell('size', $size)
).
tr(
fLabelCell('value').
fInputCell('val', $val)
).
tr(
fLabelCell(smd_bio_gTxt('position')).
fInputCell('position', $position)
).
tr(
td().
td(
fInput('submit', '', gTxt('save'), 'publish')
)
).
endTable().
eInput('smd_bio').
($id ? hInput('id', $id).hInput('name', $name).sInput('smd_bio_meta_save') : sInput('smd_bio_meta_add'))
);
}
// ------------------------
function smd_bio_meta_list() {
global $smd_bio_types;
extract(gpsa(array('sort', 'dir', 'crit', 'search_method')));
if ($sort === '') $sort = get_pref('smd_bio_meta_sort_column', 'name');
if ($dir === '') $dir = get_pref('smd_bio_meta_sort_dir', 'asc');
$dir = ($dir == 'desc') ? 'desc' : 'asc';
if (!in_array($sort, array('name', 'title', 'type', 'size', 'val', 'position'))) $sort = 'position';
$sort_sql = $sort.' '.$dir;
set_pref('smd_bio_meta_sort_column', $sort, 'smd_bio', 2, '', 0, PREF_PRIVATE);
set_pref('smd_bio_meta_sort_dir', $dir, 'smd_bio', 2, '', 0, PREF_PRIVATE);
$switch_dir = ($dir == 'desc') ? 'asc' : 'desc';
$rs = safe_rows_start('*', SMD_BIO_META, '1 = 1 ORDER BY '.$sort_sql);
if ($rs) {
echo '';
}
}
// ------------------------
function smd_bio_meta_save() {
extract(doSlash(psa(array('id', 'name', 'title', 'type', 'size', 'val', 'position'))));
$id = assert_int($id);
$rs = safe_update(SMD_BIO_META, "
title = '$title',
type = '$type',
size = '$size',
val = '$val',
position = '$position'",
"id = $id"
);
if ($rs) {
smd_bio_config(
smd_bio_gTxt('meta_updated', array('{name}' => $name))
);
}
}
// ------------------------
function smd_bio_meta_add() {
extract(doSlash(psa(array('name', 'title', 'type', 'size', 'val', 'position'))));
$name = sanitizeForUrl($name);
if (smd_bio_meta_check($name)) {
$size = (empty($size)) ? 25 : $size;
//TODO: make the selection of column more intelligent based on the given type
if ($type == 'textarea') {
$asize = '4096'; //TODO: Find a way to user specify this
$coltype = 'text';
} else {
$asize = '64'; //TODO: Find a way to user specify this
$coltype = 'varchar';
}
$ret = safe_alter(SMD_BIO, "ADD `$name` $coltype($asize) NULL");
if ($ret) {
$rs = safe_insert(SMD_BIO_META, "
name = '$name',
title = '$title',
type = '$type',
size = '$size',
val = '$val',
position = '$position'
");
if ($rs) {
smd_bio_config(smd_bio_gTxt('meta_added'));
return;
}
}
}
smd_bio_config(array(smd_bio_gTxt('meta_not_added'), E_ERROR));
}
// ------------------------
function smd_bio_meta_del() {
extract(gpsa(array('id','name')));
$id = assert_int($id);
$message = smd_bio_gTxt('meta_not_deleted');
$exists = smd_bio_meta_check($name);
$ret = @safe_alter(SMD_BIO, "DROP COLUMN `$name`");
if ($ret || $exists) {
$ret = safe_delete(SMD_BIO_META, "id='$id'");
$message = smd_bio_gTxt('meta_deleted');
}
smd_bio_config($message);
}
// ------------------------
function smd_bio_meta_check($col) {
$ucols = getThings('describe `'.PFX.'txp_users`');
$bcols = getThings('describe `'.PFX.SMD_BIO.'`');
$cols = array_merge($ucols, $bcols);
return (!in_array($col, $cols));
}
// ************************
// TABLE GENERATION
// ------------------------
function smd_bio_table_exist() {
if (gps('debug')) {
echo "++ TABLE ".SMD_BIO_META." HAS ".count(@safe_show('columns', SMD_BIO_META))." COLUMNS;";
}
return(@safe_show('columns', SMD_BIO_META));
}
// ------------------------
function smd_bio_install($message) {
global $smd_bio_types;
$version = mysql_get_server_info();
$GLOBALS['txp_err_count'] = 0;
if ($version < "4.1.2") {
$GLOBALS['txp_err_count']++;
trigger_error("smd_bio requires MySQL v4.1.2 or greater.");
} else {
$ret = '';
$sql = array();
$sql[] = "CREATE TABLE IF NOT EXISTS `".PFX.SMD_BIO."` (
`user_ref` varchar(64) NOT NULL default '',
UNIQUE KEY `user_ref` (`user_ref`)
) ENGINE=MyISAM PACK_KEYS=1 ";
$sql[] = "CREATE TABLE IF NOT EXISTS `".PFX.SMD_BIO_META."` (
`id` int(4) NOT NULL auto_increment,
`title` varchar(64) NULL default '',
`name` varchar(64) NOT NULL default '',
`type` set(".doQuote(join("','", array_keys($smd_bio_types))).") NOT NULL default 'text_input',
`size` varchar(10) NULL default 0,
`val` text NULL default '',
`position` tinyint(3) NULL default 0,
PRIMARY KEY (`id`),
UNIQUE KEY (`name`)
) ENGINE=MyISAM AUTO_INCREMENT=1";
if(gps('debug')) {
dmp($sql);
}
foreach ($sql as $qry) {
$ret = safe_query($qry);
if ($ret===false) {
$GLOBALS['txp_err_count']++;
echo "".$GLOBALS['txp_err_count'].". ".mysql_error()."
\n";
echo "\n";
}
}
}
if ($GLOBALS['txp_err_count'] == 0) {
$message = smd_bio_gTxt('tbl_installed');
smd_bio_config($message);
} else {
$message = smd_bio_gTxt('tbl_not_installed');
smd_bio_config($message);
}
}
// ************************
// CLIENT-SIDE TAGS
// ------------------------
function smd_bio_info($atts, $thing=NULL) {
global $thisarticle;
extract(lAtts(array(
'author' => '',
'item' => '',
'items' => '',
'exclude' => '',
'options' => '',
'wraptag' => '',
'break' => '',
'class' => '',
'label' => '1',
'labeltag' => '',
'labelclass' => '',
'itemwraptag' => '',
'itemclass' => 'SMD_DFLT',
'debug' => '0',
), $atts));
$author = ($author) ? $author : (isset($thisarticle) ? $thisarticle['authorid'] : '');
$author = do_list($author);
$authors = array();
// Expand any priv levels
foreach ($author as $user) {
if (strpos(strtolower($user), "smd_privs") === 0) {
$aprivs = explode(':', $user);
array_shift($aprivs); // Remove smd_privs token from the array
$authors = array_merge($authors, safe_column('name', 'txp_users', 'privs in (' . doQuote(join("','", $aprivs)) . ')'));
} else {
$authors[] = $user;
}
}
$item = do_list($item);
$items = do_list($items);
$items = array_merge($item, $items);
$exclude = do_list($exclude);
$options = do_list($options);
$final = '';
// The indices are the gTxt() names for the associated item
$coreCols = array('id' => 'user_id', 'name' => 'name', 'real_name' => 'RealName', 'email' => 'email', 'privileges' => 'privs', 'date' => 'last_access');
if ($authors) {
$meta = safe_rows('*', SMD_BIO_META, '1=1');
$cmeta = safe_show('columns', 'txp_users');
$num = count($meta);
foreach($cmeta as $info) {
if (!in_array($info['Field'], $coreCols)) continue;
$meta[$num]['name'] = $info['Field'];
$tField = join('', array_keys($coreCols, $info['Field']));
$meta[$num]['title'] = (gTxt($tField) == $tField) ? smd_bio_gTxt($tField) : gTxt($tField);
$length = (($off = strpos($info['Type'], '(')) !== false) ? $off : strlen($info['Type']); // Find the first open bracket or end of string
$meta[$num]['type'] = substr($info['Type'], 0, $length);
$num++;
}
// Exclusions override given items
if ($exclude[0] != '') {
$items = safe_column('name', SMD_BIO_META, '1=1');
$items = array_merge($items, $coreCols);
}
foreach ($authors as $user) {
$cbio = safe_row('*', 'txp_users', "name='$user'");
$ebio = safe_row('*', SMD_BIO, "user_ref='$user'");
$idx = -1;
$out = '';
foreach ($items as $iref => $whatnot) {
if (in_array($whatnot, $exclude)) continue;
// Find the meta row
foreach ($meta as $num => $data) {
if ($data['name'] == $whatnot) {
$idx = $num;
break;
}
}
if ($idx >- 1) {
if (in_array($whatnot, $coreCols)) {
$row = isset($cbio[$whatnot]) ? $cbio[$whatnot] : '';
} else {
$row = isset($ebio[$whatnot]) ? $ebio[$whatnot] : '';
}
if ($row) {
if ($meta[$idx]['type'] == 'image') {
if (isset($options[$iref]) && $options[$iref] == 'thumb') {
$row = @thumbnail(array('id' => $row));
} else {
$row = @image(array('id' => $row));
}
}
$theClass = ($itemclass=='SMD_DFLT') ? ' smd_bio_'.$meta[$idx]['name'] : (($itemclass) ? $itemclass : '');
$theTitle = $meta[$idx]['title'];
$theName = $meta[$idx]['name'];
$out[] = tag((($label==1) ? $theTitle : (($label=='') ? '' : (($label) ? $label : $theName))), (($labeltag)?$labeltag:'
'), (($labelclass)? ' class="'.$labelclass.'"' : '')).doTag($row, $itemwraptag, $theClass);
}
}
}
$final .= doWrap($out, $wraptag, $break, $class);
}
}
return $final;
}
// ------------------------
// Plugin-specific replacement strings - localise as required
function smd_bio_gTxt($what, $atts = array()) {
$lang = array(
'admin_tab' => 'Bio Config',
'id' => 'ID',
'name' => 'Name',
'meta_add' => 'Add bio item',
'meta_added' => 'Bio item added',
'meta_not_added' => 'Problem adding bio item: check its name has not been used before',
'meta_deleted' => 'Bio item and all its data deleted',
'meta_not_deleted' => 'Problem deleting bio item: it may or may not still exist',
'meta_edit' => 'Edit bio item',
'meta_updated' => 'Bio item "{name}" updated',
'position' => 'Position',
'privileges' => 'Privileges',
'real_name' => 'Full name',
'size' => 'Size',
'tbl_installed' => 'Tables installed.',
'tbl_not_installed' => 'Tables NOT installed.',
'title' => 'Title',
'type' => 'Type',
'value' => 'Value',
);
return strtr($lang[$what], $atts);
}