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; }