array('token_list' => array(), 'prefix' => '[', 'suffix' => ']')
);
return $theme;
}
/**
* Implementation of hook_help().
*/
function comment_subject_help($path, $arg) {
switch ($path) {
case 'admin/help#comment_subject':
$output = '
' . t('Provides a default comment subject like: "Re: [parent comment/node title]", auto-incremental numbering, and optional integration with token for author, date, etc. This option can be set on a per content type basis.') . '
';
$output .= '' . t('Users can still edit the comment subject to their liking if comment titles are enabled in the comment settings.') . '
';
$output .= '' . t('When using [comment-parent-title-raw] comment\'s subject is derived from the node title, or the comment that the new comment is a reply to. In addition enabling "strip prefix/suffix from parent\'s title" allows (for instance) avoiding repeated "Re:" prefix and/or "by username" suffix.') . '
';
$output .= '' . t('Note that stripping prefix/suffix fully suppports [comment-*] replacements, while global replacements won\'t let the stripping algorithm behave as expected. Nevertheless, some immutable tokens might be used (for instance [site-name], [site-mail]), but if their values change in time the prefix/suffix stripping shall fail.') . '
';
$output .= '' . t('When using [comment-auto-numbering] option comment\'s subject are auto-incremental (e.g. "#1", "#2", an so on).')
. '
' . t('CAUTION: to achieve [comment-auto-numbering] replacement works as expected previous existing comments shouldn\'t be deleted. Otherwise numbering sequence won\'t be guaranted (i.e. existing comments won\'t get updated and new comments will count just existing ones).') . '
';
return $output;
}
}
/**
* Implementation of hook_node_type().
*/
function comment_subject_node_type($op, $info) {
switch ($op) {
case 'delete':
$node_type = $info->type;
variable_del('comment_subject_field:default_subject_pattern_' . $node_type);
variable_del('comment_subject_field:strip_parent_title_' . $node_type);
break;
}
}
/**
* Implementation of hook_form_alter().
*/
function comment_subject_form_alter(&$form, &$form_state, $form_id) {
// keep this condition consistent with comment.module
if ($form_id == 'node_type_form' && isset($form['identity']['type'])) {
$form['comment']['comment_subject_field']['#weight'] = 10;
$node_type = $form['#node_type']->type;
$form['comment']['comment_subject_field:default_subject_pattern'] = array(
'#title' => 'Default subject pattern',
'#description' => t('Default value for subject field (e.g. "Re: [comment-parent-title-raw]", "#[comment-auto-numbering]").'),
'#type' => 'textfield',
'#default_value' => variable_get('comment_subject_field:default_subject_pattern_' . $node_type, COMMENT_SUBJECT__DEFAULT_SUBJECT_PATTERN),
'#weight' => 12,
'#maxlength' => NULL, // avoid maxlength=128
);
$form['comment']['comment_subject_field:strip_parent_title'] = array(
'#title' => 'Strip prefix/suffix from parent\'s title',
'#description' => t('Avoids repetitive prefix/suffix when using the parent\'s title in the default subject pattern (recommended).'),
'#type' => 'checkbox',
'#default_value' => variable_get('comment_subject_field:strip_parent_title_' . $node_type, TRUE),
'#weight' => 13,
);
$form['comment']['token_replacements'] = array(
'#type' => 'fieldset',
'#title' => t('Replacements for default subject pattern'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#weight' => 13,
);
if (module_exists('token')) {
$token_list = token_get_list('comment');
$form['comment']['token_replacements']['#description'] = t('Note that not every available pattern makes sense for comment\'s default subject.');
}
else {
$token_list = comment_subject_token_list('comment');
$form['comment']['token_replacements']['#description'] = t('If !token_module was enabled more replacements would be available.', array('!token_module' => 'token module'));
}
foreach($token_list['comment'] as $key => $value) {
if (comment_subject_requires_comment_subject_field_disabled($key)) {
$token_list['comment'][$key] .= '**';
}
}
$form['#validate'][] = 'comment_subject_validate_replacement';
$form['comment']['token_replacements']['#description'] .= '
' . t('Use -raw replacements for text to avoid problems with HTML entities.');
$form['comment']['token_replacements']['#description'] .= '
' . t('Replacements marked with "**" will only be available after submission, therefore require comment subject field to be disabled.');
$form['comment']['token_replacements']['table'] = array(
// if issue #576592 gets into token module this would be theme('token_list', ...) instead
'#value' => theme('comment_subject_token_list', $token_list),
);
}
elseif($form_id == 'comment_form' && empty($form['subject']['#default_value'])) {
// if $form['subject'] doesn't exists it means it is disabled and will be handled on hook_comment insert/update
if (!isset($form['subject'])) {
// nevertheless support comment preview
$form['subject']['#type'] = 'value'; // use 'hidden' when testing for quick inspect DOM
$node_type = node_load($form['nid']['#value'])->type;
$pattern = variable_get('comment_subject_field:default_subject_pattern_' . $node_type, COMMENT_SUBJECT__DEFAULT_SUBJECT_PATTERN);
$unsupported = comment_subject_unsupported_on_preview($pattern);
if (!empty($unsupported)) {
$form['subject']['#default_value'] = t('(generated subject)');
return; // don't fall through
}
// fall through (the subject is disable, but there are no unsupported tokens for preview)
}
$comment = (object)array('nid' => $form['nid']['#value'], 'pid' => $form['pid']['#value'], 'cid' => $form['cid']['#value']);
$form['subject']['#default_value'] = _comment_subject_get_subject($comment);
}
}
function comment_subject_requires_comment_subject_field_disabled($key) {
return (!in_array($key, array('comment-nid', 'comment-pid', 'comment-parent-title', 'comment-parent-title-raw')));
}
function _comment_subject_token_replace($str, $comment) {
if (module_exists('token')) {
$result = token_replace($str, 'comment', $comment);
}
else {
// Note: duplicating code should be avoided,
// but this is the (little) cost to be independent from token module
// and not having it as a required dependency
$tokens = comment_subject_token_values('comment', $comment);
foreach ($tokens as $key => $value) {
$tokens[$key] = '[' . $key . ']';
$values[] = $value;
}
$result = str_replace($tokens, $values, $str);
}
return $result;
}
function _comment_subject_get_subject($comment) {
$node_type = node_load($comment->nid)->type;
$pattern = variable_get('comment_subject_field:default_subject_pattern_' . $node_type, COMMENT_SUBJECT__DEFAULT_SUBJECT_PATTERN);
$strip_parent_title = variable_get('comment_subject_field:strip_parent_title_' . $node_type, TRUE);
if ($strip_parent_title && $comment->pid) {
// stripping algorithm
// supporting both sanitized and unsanitized version,
// even though HTML entities will come up double encoded for the sanitized version
$using_parent_title_raw = (strpos($pattern, '[comment-parent-title-raw]') !== FALSE);
$using_parent_title_encoded = (strpos($pattern, '[comment-parent-title]') !== FALSE);
if ($using_parent_title_raw || $using_parent_title_encoded) {
// comment_subject_validate_replacement ensures both tokens can't be used at the same time
list($prefix, $suffix) = explode($using_parent_title_raw ? '[comment-parent-title-raw]' : '[comment-parent-title]', $pattern);
$parent_comment = _comment_load($comment->pid);
$parent_prefix = _comment_subject_token_replace($prefix, $parent_comment);
$parent_suffix = _comment_subject_token_replace($suffix, $parent_comment);
$parent_title = _comment_subject_token_replace('[comment-parent-title-raw]', $comment);
if (!empty($parent_prefix) && strpos($parent_title, $parent_prefix) === 0) {
$parent_title = substr($parent_title, strlen($parent_prefix));
}
// strrpos only works for a single character in PHP4
if (!empty($parent_suffix) && strpos(strrev($parent_title), strrev($parent_suffix)) === 0) {
$parent_title = strrev(substr(strrev($parent_title), strlen($parent_suffix)));
}
// $parent_title was already stripped of $parent_prefix and $parent_suffix
if ($using_parent_title_raw) {
$pattern = str_replace('[comment-parent-title-raw]', $parent_title, $pattern);
}
else {
// also support the sanitized version, even though HTML entities will come up double encoded
$pattern = str_replace('[comment-parent-title]', check_plain($parent_title), $pattern);
}
}
}
$subject = _comment_subject_token_replace($pattern, $comment);
// comment subjects can not be longer than 64 characters
$subject = truncate_utf8($subject, 64, TRUE, TRUE);
return $subject;
}
/**
* Implementation of hook_comment().
*/
function comment_subject_comment($edit, $op) {
switch($op) {
case 'insert': // no break;
case 'update':
$node_type = node_load($edit['nid'])->type;
// if user subject is enabled can't figure out whether should be updated or leave user input as is
if (!variable_get('comment_subject_field_' . $node_type, 1)) {
// update required for every [comment-*] replacement requiring subject to be disabled
$comment = _comment_load($edit['cid']);
$subject = _comment_subject_get_subject($comment);
$query = "UPDATE {comments} SET subject = '%s' WHERE cid = %d";
db_query($query, $subject, $edit['cid']);
}
break;
}
}
function comment_subject_validate_replacement($form, &$form_state) {
// when stripping algorithm is enabled check for unsupported use of both
// [comment-parent-title-raw] and [comment-parent-title] at the same time
if ($form_state['values']['comment_subject_field:strip_parent_title']) {
$element_key = 'comment_subject_field:default_subject_pattern';
$pattern = $form_state['values'][$element_key];
if (strpos($pattern, '[comment-parent-title-raw]') != FALSE && strpos($pattern, '[comment-parent-title]') != FALSE) {
form_set_error($element_key, t('It makes no sense to use these tokens [comment-parent-title-raw] and [comment-parent-title] at the same time.'));
}
}
// if "comment subject field" is enabled
// check for tokens which only will be available after submission
$matches = array();
if ($form_state['values']['comment_subject_field']) {
$element_key = 'comment_subject_field:default_subject_pattern';
$unsupported = comment_subject_unsupported_on_preview($form_state['values'][$element_key]);
if (!empty($unsupported)) {
form_set_error($element_key, t('There are some token replacements in use which requires comment subject field to be disabled: @unsupported', array('@unsupported' => '[' . implode('], [', $unsupported) . ']')));
}
}
}
function comment_subject_unsupported_on_preview($default_subject_pattern) {
$unsupported = array();
if (preg_match_all('/\\[(comment-.*)\\]/U', $default_subject_pattern, $matches)) {
foreach($matches[1] as $replacement) {
if (comment_subject_requires_comment_subject_field_disabled($replacement)) {
$unsupported[] = $replacement;
}
}
}
return $unsupported;
}
// if issue #576592 gets into token module this will require a rollback
// to conditional declare theme_token_list (if !module_exists('token'))
function theme_comment_subject_token_list($token_list, $prefix = '[', $suffix = ']') {
$headers = array(t('Token'), t('Replacement value'));
$rows = array();
foreach ($token_list as $key => $category) {
$rows[] = array(array('data' => drupal_ucfirst($key) .' '. t('tokens'), 'class' => 'region', 'colspan' => 2));
foreach ($category as $token => $description) {
$row = array();
$row[] = $prefix . $token . $suffix;
$row[] = $description;
$rows[] = $row;
}
}
$output = theme('table', $headers, $rows, array('class' => 'description'));
return $output;
}