# PLUGIN PREVIEW BY TEXTPATTERN.INFO
/**
* smd_article_stats
*
* A Textpattern CMS plugin for counting words in article fields and optionally displaying them to visitors
* -> Choose which fields to count on the admin side
* -> Customize where you want the count to be displayed
* -> Shows ID of currently edited article.
*
* @author Stef Dawson
* @link http://stefdawson.com/
*/
// TODO:
// * TinyMCE -- accessing fields inside iframes?
if (@txpinterface == 'admin') {
$all_privs = array_keys(get_groups());
unset($all_privs[array_search('0', $all_privs)]); // Remove 'none'
$all_joined = join(',', $all_privs);
add_privs('smd_artstat_prefs', $all_joined);
add_privs('prefs.smd_artstat', $all_joined);
register_callback('smd_artstat_prefs', 'prefs', '', 1);
register_callback('smd_article_info', 'article');
$smd_ai_prefs = smd_article_info_prefs();
foreach ($smd_ai_prefs as $key => $prefobj) {
register_callback('smd_article_info_pophelp', 'admin_help', $key);
}
}
// Public side info
function smd_article_stats($atts, $thing=NULL) {
global $thisarticle;
assert_article();
extract(lAtts(array(
'wraptag' => '',
'class' => __FUNCTION__,
'break' => '',
'label' => '',
'labeltag' => '',
'item' => '',
), $atts));
$out = array();
// item not specified? Use the array 'keys' from the pref
if (empty($item)) {
$fldList = do_list(get_pref('smd_artstat_fields'));
$cfs = getCustomFields();
foreach ($fldList as $fld) {
$fldInfo = do_list($fld, '->');
$field = $fldInfo[0];
if (strpos($field, 'custom_') !== false) {
$cfnum = str_replace('custom_', '', $field);
if (array_key_exists($cfnum, $cfs)) {
$field = $cfs[$cfnum];
} else {
// Bogus CF: skip it
continue;
}
}
$item[] = strtolower($field);
}
} else {
$item = do_list($item);
}
$out[] = smd_article_info_count($item, $thisarticle);
return doLabel($label, $labeltag).doWrap($out, $wraptag, $break, $class);
}
// Admin-side info -- auto-updated via jQuery
function smd_article_info($event, $step) {
global $app_mode;
extract(gpsa(array('view')));
include_once txpath.'/publish/taghandlers.php';
if(!$view || gps('save') || gps('publish')) {
$view = 'text';
}
if ($view == 'text') {
$screen_locs = array(
'none' => '',
'excerpt_below' => 'jq|.excerpt|after',
'author_below' => 'jq|.author|after',
'status_above' => 'jq|#write-status|before',
'title_above' => 'jq|#article-main|prepend',
'textfilter_help_above' => 'jq|#textfilter_group|before',
'textfilter_help_below' => 'jq|#textfilter_group|after', // For 4.6.x+
'textile_help_above' => 'jq|#article-col-1|prepend',
'textile_help_below' => 'jq|#textile_help|after', // For 4.5.x
);
// Check hidden pref and sanitize
$posn = get_pref('smd_artstat_pos', 'status_above');
$posn = (array_key_exists($posn, $screen_locs)) ? $posn : 'status_above';
$placer = explode('|', $screen_locs[$posn]);
doArray($placer, 'escape_js');
// Split and recombine to get rid of spaces
// TODO: error detection if missing entries
$fldList = do_list(get_pref('smd_artstat_fields', 'Body -> #body, Excerpt -> #excerpt'));
$fldAnchors = array('0'); // Placeholder since Status isn't a countable field, but we need it later
$db_fields = array('Status');
foreach ($fldList as $fld) {
$fldInfo = do_list($fld, '->');
$db_fields[] = $fldInfo[0];
if (isset($fldInfo[1])) {
$fldAnchors[] = $fldInfo[1];
}
}
array_shift($fldAnchors); // Goodbye Status anchor
$js_fields = escape_js(implode(',', $fldAnchors));
$js_array_fields = implode(',', doArray(doArray($fldAnchors, 'escape_js'), 'doQuote'));
$id = (empty($GLOBALS['ID']) ? gps('ID') : $GLOBALS['ID']);
if (empty($id)) {
$rs = $db_fields;
} else {
$rs = safe_row(join(',', doArray($db_fields, 'doSlash')), 'textpattern', 'ID='.doSlash($id));
}
$idlink = (get_pref('smd_artstat_id') === '1') ? (($id && in_array($rs['Status'], array(STATUS_LIVE, STATUS_STICKY))) ? href($id, permlinkurl_id($id)) : $id) : '';
$indiv = array();
$words = 0;
array_shift($db_fields); // Goodbye Status field
foreach ($db_fields as $idx => $fld) {
$wc = smd_article_info_count($fld, $rs);
$words += $wc;
$indiv[] = '<span class="smd_article_stats_' . $idx . '">'.$wc.'</span>';
}
gTxtScript(array('smd_artstat_word_singular', 'smd_artstat_word_plural'));
$singstring = get_pref('smd_artstat_singular', '1');
$singles = do_list($singstring);
$out1 = escape_js(
defined('PREF_PLUGIN')
? wrapGroup('smd_artstat', '<span class="smd_article_stats_wc">'.$words.'</span> <span class="smd_article_stats_wd">'.(in_array($words, $singles) ? gTxt('smd_artstat_word_singular') : gTxt('smd_artstat_word_plural')).'</span>: ( ' . join(' / ', $indiv) .' )'.(($idlink) ? ' | ' . gTxt('id') .n. $idlink : ''), 'smd_artstat')
: '<fieldset><legend>'.gTxt('smd_artstat_legend').'</legend><p><span class="smd_article_stats_wc">'.$words.'</span> <span class="smd_article_stats_wd">'.(in_array($words, $singles) ? gTxt('smd_artstat_word_singular') : gTxt('smd_artstat_word_plural')).'</span>: ( ' . join(' / ', $indiv) .' )'.(($idlink) ? ' | ' . gTxt('id') .n. $idlink : '').'</p></fieldset>'
);
$out2 = script_js(<<<EOJS
jQuery(function() {
var singlist = [{$singstring}];
jQuery("{$js_fields}").keyup(function() {
var flds = [{$js_array_fields}];
var wds = 0;
var content = '';
for (idx = 0; idx < flds.length; idx++) {
if (jQuery(flds[idx]).length > 0) {
content = jQuery(flds[idx]).val();
content += (content.length > 0) ? " " : "";
word_count = content.replace(/(<([^>]+)>)/ig,"").split(/\s+/).length-1
jQuery(".smd_article_stats_"+idx).text(word_count);
wds += word_count;
}
}
jQuery(".smd_article_stats_wc").text(wds);
jQuery(".smd_article_stats_wd").text(((jQuery.inArray(wds, singlist) > -1) ? textpattern.gTxt('smd_artstat_word_singular') : textpattern.gTxt('smd_artstat_word_plural')));
}).keyup();
});
EOJS
);
if ($placer[0] == 'jq' && $app_mode != 'async') {
echo '<script type="text/javascript">jQuery(function() { jQuery("'.$placer[1].'").'.$placer[2].'(\''.$out1.'\'); });</script>'.$out2;
}
}
}
// Get pophelp content from stefdawson.com
function smd_article_info_pophelp($evt, $stp, $ui, $vars) {
return str_replace(HELP_URL, 'http://stefdawson.com/downloads/support/', $ui);
}
// Install prefs if they don't already exist
function smd_artstat_prefs($evt, $stp) {
$smd_ai_prefs = smd_article_info_prefs();
foreach ($smd_ai_prefs as $key => $prefobj) {
if (get_pref($key) === '') {
set_pref($key, doSlash($prefobj['default']), 'smd_artstat', $prefobj['type'], $prefobj['html'], $prefobj['position'], $prefobj['visibility']);
}
}
}
// Only render the pref if enough privs exist
function smd_artstat_restricted($key, $val) {
global $txp_user;
static $smd_artstat_privs = array();
if (array_key_exists($txp_user, $smd_artstat_privs)) {
$privs = $smd_artstat_privs[$txp_user];
} else {
$safe_user = doSlash($txp_user);
$privs = safe_field('privs', 'txp_users', "name='$safe_user'");
$smd_artstat_privs[$txp_user] = $privs;
}
if ($privs === '1') {
return fInput('text', $key, $val, '', '', '', INPUT_REGULAR);
} else {
return gTxt('smd_artstat_set_by_admin');
}
}
// Render the position pref
function smd_artstat_pos($key, $val) {
$smd_ai_prefs = smd_article_info_prefs();
$obj = $smd_ai_prefs[$key];
return selectInput($key, $obj['content'], $val);
}
// Settings for the plugin
// TODO: Use PREF_PLUGIN constant after 4.6.0 released
function smd_article_info_prefs() {
$smd_ai_prefs = array(
'smd_artstat_fields' => array(
'html' => 'smd_artstat_restricted',
'type' => PREF_ADVANCED,
'position' => 10,
'default' => 'Body -> #body, Excerpt -> #excerpt',
'group' => 'smd_artstat_settings',
'visibility' => PREF_GLOBAL,
),
'smd_artstat_pos' => array(
'html' => 'smd_artstat_pos',
'type' => PREF_ADVANCED,
'position' => 20,
'content' => array(
'none' => gTxt('none'),
'excerpt_below' => gTxt('smd_artstat_pos_below_excerpt'),
'author_below' => gTxt('smd_artstat_pos_below_author'),
'status_above' => gTxt('smd_artstat_pos_above_status'),
'title_above' => gTxt('smd_artstat_pos_above_title'),
'textile_help_above' => gTxt('smd_artstat_pos_above_textile'),
// 'textfilter_help_above' => gTxt('smd_artstat_pos_above_textfilter'), // Uncomment after 4.6.0 release and remove textile_help_above. Upgrade anyone using this setting to the new one automatically
'textile_help_below' => gTxt('smd_artstat_pos_below_textile'),
// 'textfilter_help_below' => gTxt('smd_artstat_pos_below_textfilter'), // Uncomment after 4.6.0 release and remove textile_help_below. Upgrade anyone using this setting to the new one automatically
),
'default' => 'status_above',
'group' => 'smd_artstat_settings',
'visibility' => PREF_PRIVATE,
),
'smd_artstat_id' => array(
'html' => 'yesnoradio',
'type' => PREF_ADVANCED,
'position' => 30,
'default' => '0',
'group' => 'smd_artstat_settings',
'visibility' => PREF_PRIVATE,
),
'smd_artstat_singular' => array(
'html' => 'smd_artstat_restricted',
'type' => PREF_ADVANCED,
'position' => 40,
'default' => '1',
'group' => 'smd_artstat_settings',
'visibility' => PREF_GLOBAL,
),
);
return $smd_ai_prefs;
}
// Library function to count words in the given field items
function smd_article_info_count($item, $from) {
$words = 0;
$notags = '/(<([^>]+?)>)/';
$item = is_array($item) ? $item : array($item);
foreach ($item as $whatnot) {
$content = (isset($from[$whatnot])) ? preg_replace($notags, '', trim($from[$whatnot])) . ((strlen($from[$whatnot])==0) ? '' : ' ') : '';
if ($content) {
$words += preg_match_all('@\s+@', $content, $m);
}
}
return $words;
}