You are here

profanity.module in Profanity 7

Main {profanity} file.

File

profanity.module
View source
<?php

/**
 * @file
 * Main {profanity} file.
 */

/**
 * Implements hook_permission().
 */
function profanity_permission() {
  return array(
    'administer profanity' => array(
      'title' => 'Administer profanity',
    ),
  );
}

/**
 * Implements hook_menu().
 */
function profanity_menu() {
  $items = array();
  $items['admin/config/content/profanity/settings'] = array(
    'title' => 'Settings',
    'description' => 'Configuration for the Profanity module.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'profanity_admin_settings',
    ),
    'access arguments' => array(
      'administer profanity',
    ),
    'type' => MENU_LOCAL_ACTION,
  );
  return $items;
}

/**
 * Implements of hook_ctools_plugin_directory().
 */
function profanity_ctools_plugin_directory($module, $type) {
  if ($type == 'export_ui') {
    return 'plugins/export_ui';
  }
}

/**
 * Implements of hook_ctools_plugin_api().
 */
function profanity_ctools_plugin_api($owner, $api) {
  if ($owner == 'profanity' && $api == 'default_profanity_lists') {
    return array(
      'version' => 1,
    );
  }
}

/**
 * Implements hook_default_profanity_list().
 *
 * Provide a default list.
 */

/*function profanity_default_profanity_list() {
  $export = array();

  $list = new stdClass();
  $list->api_version = 1;
  $list->name = 'my_default_list';
  $list->title = 'My default list';
  $list->description = 'Default profanity list';
  $list->mydata = 'x';
  $export['my_default_list'] = $list;

  return $export;
}*/

/**
 * Load a single list.
 *
 * @param string $pid
 *   The list identifier.
 */
function profanity_list_load($pid) {
  ctools_include('export');
  $result = ctools_export_load_object('profanity_list', 'names', array(
    $pid,
  ));
  if (isset($result[$pid])) {
    return $result[$pid];
  }
}

/**
 * Export a profanity_list.
 *
 * @param object $obj
 *   The profanity object.
 * @param string $indent
 *   An identifier.
 *
 * @return string
 *   The export definition.
 */
function profanity_list_export($obj, $indent = '') {
  ctools_include('export');
  $output = ctools_export_object('profanity_list', $obj, $indent);
  return $output;
}

/**
 * Implements hook_views_api().
 */
function profanity_views_api() {
  return array(
    'api' => '3.0',
  );
}

/**
 * Implements hook_filter_info().
 */
function profanity_filter_info() {
  $filters = array();
  $filters['profanity'] = array(
    'title' => t('Profanity'),
    'description' => t('Allows you to replace any occurances of profanity or specific words with either a character repeated to the same length as the original or a phrase.'),
    'process callback' => '_profanity_filter',
    'settings callback' => '_profanity_filter_settings',
    'default settings' => array(
      'lists' => array(),
    ),
  );
  return $filters;
}

/**
 * Implements hook_token_info().
 */
function profanity_token_info() {
  $info = array();

  // Build up an array of lists so we can provide helpful description text.
  $wordlists = profanity_get_lists_flat();
  if (!empty($wordlists)) {
    $lists = array();
    foreach ($wordlists as $name => $label) {
      $lists[] = check_plain("{$label} ({$name})");
    }
    $lists_replacement = t('Possible lists: !lists', array(
      '!lists' => implode(', ', $lists),
    ));
  }
  else {
    $lists_replacement = t("There aren't any word lists defined, you'll need to add at least one first before using this token.");
  }

  // Add in tokens for all entities which have a label.
  foreach (entity_get_info() as $entity_type => $entity_info) {
    if (empty($entity_info['entity keys']['label'])) {
      continue;
    }

    // Taxonomy tokens use different type text, not the entity type name.
    if ($entity_type === 'taxonomy_vocabulary') {
      $entity_type = 'vocabulary';
    }
    elseif ($entity_type === 'taxonomy_term') {
      $entity_type = 'term';
    }
    $title_property = $entity_info['entity keys']['label'];
    $info['tokens'][$entity_type][$title_property . '-profanity'] = array(
      'name' => t('@label - profanity filtered', array(
        '@label' => ucfirst($title_property),
      )),
      'description' => t('The @label for this entity processed by a specified profanity word list. Use with the word list machine name. !replacements', array(
        '@label' => $title_property,
        '!replacements' => $lists_replacement,
      )),
      'dynamic' => TRUE,
    );
  }

  // Add in current page token.
  $info['tokens']['current-page']['title-profanity'] = array(
    'name' => t('Title'),
    'description' => t('The title of the current page profanity filtered. Use with the word list machine name. !replacements', array(
      '!replacements' => $lists_replacement,
    )),
    'dynamic' => TRUE,
  );
  return $info;
}

/**
 * Implements hook_tokens().
 */
function profanity_tokens($type, $tokens, array $data = array(), array $options = array()) {
  $replacements = array();
  $sanitize = !empty($options['sanitize']);

  // Current page token.
  if ($type == 'current-page') {
    foreach ($tokens as $name => $original) {
      if (strpos($name, 'title-profanity') === 0) {
        $title = drupal_get_title();
        $name_parts = explode(':', $name);
        if (!empty($name_parts[1])) {
          $page_title = profanity_list_execute($name_parts[1], $title);
          $replacements[$original] = $sanitize ? $page_title : decode_entities($page_title);
        }
      }
    }
  }

  // Entity label tokens.
  $entity_info = entity_get_info();

  // If we can't work out a label property, bail out.
  if (empty($entity_info[$type]['entity keys']['label'])) {
    return $replacements;
  }
  $title_property = $entity_info[$type]['entity keys']['label'];
  if (!empty($data[$type]->{$title_property})) {
    foreach ($tokens as $name => $original) {
      if (strpos($name, $title_property . '-profanity') === 0) {
        $name_parts = explode(':', $name);
        if (!empty($name_parts[1])) {
          $replacements[$original] = profanity_list_execute($name_parts[1], $data[$type]->{$title_property});
        }
      }
    }
  }
  return $replacements;
}

/**
 * Implements hook_entity_property_info_alter().
 */
function profanity_entity_property_info_alter(&$info) {
  $lists = variable_get('profanity_supply_entity_properties_lists', array());
  if (variable_get('profanity_supply_entity_properties', 0) && !empty($lists)) {
    $entity_info = entity_get_info();
    foreach ($entity_info as $entity_type => $entity) {
      if (empty($entity['entity keys']['label'])) {
        continue;
      }
      $title_property = $entity['entity keys']['label'];
      $info[$entity_type]['properties']["profanity_{$title_property}"] = array(
        'label' => t('@label profanity filtered', array(
          '@label' => ucfirst($title_property),
        )),
        'description' => t('The @label property passed through profanity filters.', array(
          '@label' => $title_property,
        )),
        'type' => 'text',
        'computed' => TRUE,
        'getter callback' => 'profanity_entity_property_getter',
      );
    }
  }
}

/**
 * Implements hook_entity_load().
 */
function profanity_entity_prepare_view($entities, $entity_type, $langcode) {

  // If title filtering is enabled check the type.
  if (variable_get('profanity_protect_the_titles', 0) && in_array($entity_type, variable_get('profanity_title_entities', array()), TRUE)) {
    $entity_info = entity_get_info();
    foreach ($entities as $entity_id => $entity) {
      foreach (variable_get('profanity_title_lists', array()) as $list_name) {
        if (empty($entity_info[$entity_type]['entity keys']['label'])) {
          break;
        }
        $title_property = $entity_info[$entity_type]['entity keys']['label'];
        $entity->{'original_' . $title_property} = $entity->{$title_property};
        $entity->{$title_property} = profanity_list_execute($list_name, $entity->{$title_property});
      }
    }
  }
}

/**
 * Function to return a flat list array of machine name indexes and
 * label values.
 */
function profanity_get_lists_flat() {
  static $flat_lists;
  ctools_include('export');
  if (!isset($flat_lists)) {
    $flat_lists = array();
    $lists = ctools_export_load_object('profanity_list');
    if (!empty($lists)) {
      $options = array();
      foreach ($lists as $list) {
        $flat_lists[$list->name] = check_plain($list->title);
      }
    }
  }
  return $flat_lists;
}

/**
 * Filter settings callback for profanity filter.
 */
function _profanity_filter_settings($form, $form_state, $filter, $format, $defaults) {

  // Load lists.
  $options = profanity_get_lists_flat();
  $settings['lists'] = array(
    '#title' => t('Lists to run'),
    '#type' => 'checkboxes',
    '#options' => $options,
    '#default_value' => isset($filter->settings['lists']) ? $filter->settings['lists'] : $defaults['lists'],
    '#description' => t('Select which lists will be applied to text.'),
  );
  return $settings;
}
function _profanity_filter($text, $filter, $format, $langcode, $cache, $cache_id) {
  if (empty($filter->settings['lists'])) {
    return $text;
  }

  // Run through each list and apply to the text.
  foreach ($filter->settings['lists'] as $list_name) {
    $text = profanity_list_execute($list_name, $text);
  }
  return $text;
}

/**
 * Helper function for use in replacing or validating.
 */
function profanity_list_execute($list_name, $text, $return_text = TRUE) {
  $list = profanity_list_load($list_name);
  if (!$list) {
    return $return_text ? $text : FALSE;
  }
  $words = trim($list->words, ',');
  $words = explode(',', $words);
  if (empty($words)) {
    return $return_text ? $text : FALSE;
  }
  $count = 0;

  // Match partials, for example the word "christ" would match in
  // "christmas" and be potentially changed to "******mas".
  if ($list->match_partial) {

    // Character replacement.
    if ($list->replacement_mode == 0) {

      // This following method won out when tracking time to execute.
      $replacements = profanity_list_get_replacements($list);
      if ($list->case_sensitive) {
        $text = str_replace($words, $replacements, $text, $count);
      }
      else {
        $text = str_ireplace($words, $replacements, $text, $count);
      }
    }
    elseif ($list->replacement_mode == 1) {
      if ($list->case_sensitive) {
        $text = str_replace($words, $list->replacement_phrase, $text, $count);
      }
      else {
        $text = str_ireplace($words, $list->replacement_phrase, $text, $count);
      }
    }
  }
  else {
    $wordlist = implode('|', array_map('preg_quote', $words));
    $modifiers = $list->case_sensitive ? NULL : "/i";

    // Character replacement.
    if ($list->replacement_mode == 0) {
      $replace = new ProfanityPregCallback($wordlist, $modifiers, $list->replacement_character);
      $text = $replace
        ->execute($text);
      $count = $replace
        ->get_count();
    }
    elseif ($list->replacement_mode == 1) {
      $text = preg_replace("/\\b({$wordlist})\\b{$modifiers}", $list->replacement_phrase, $text, -1, $count);
    }
  }
  $found = $count > 0;
  return $return_text ? $text : $found;
}

/**
 * Grab a list of replacements from cache or set the cache and return.
 */
function profanity_list_get_replacements($list) {
  $replacements =& drupal_static(__FUNCTION__);
  if (!isset($replacements[$list->name])) {
    if ($cache = cache_get('profanity_list:' . $list->name)) {
      $replacements[$list->name] = $cache->data;
    }
    else {
      $replacements[$list->name] = array();
      $words = trim($list->words, ',');
      $words = explode(',', $words);
      if (empty($words)) {
        return array();
      }

      // Match partials.
      if ($list->match_partial && $list->replacement_mode == 0) {
        foreach ($words as $word) {
          $replacements[$list->name][] = str_repeat($list->replacement_character, strlen(utf8_decode($word)));
        }
      }
      cache_set('profanity_list:' . $list->name, $replacements[$list->name], 'cache');
    }
  }
  return $replacements[$list->name];
}

/**
 * Admin settings form callback.
 */
function profanity_admin_settings() {
  $form = array();
  $form['profanity_protect_the_titles'] = array(
    '#type' => 'checkbox',
    '#title' => t('Run profanity filters on Entity titles?'),
    '#default_value' => variable_get('profanity_protect_the_titles', 0),
    '#description' => t('Tick this to enable more options.'),
  );
  $form['titles'] = array(
    '#type' => 'fieldset',
    '#title' => t('Entity title settings'),
    '#states' => array(
      'visible' => array(
        ':input[id="edit-profanity-protect-the-titles"]' => array(
          'checked' => TRUE,
        ),
      ),
    ),
  );
  $entity_options = array();
  foreach (entity_get_info() as $entity_type => $entity_info) {
    $entity_options[$entity_type] = filter_xss($entity_info['label']);
  }
  $form['titles']['profanity_title_entities'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Which Entity types should have their titles processed?'),
    '#options' => $entity_options,
    '#default_value' => variable_get('profanity_title_entities', array()),
    '#states' => array(
      'required' => array(
        ':input[id="edit-profanity-protect-the-titles"]' => array(
          'checked' => TRUE,
        ),
      ),
    ),
  );
  $form['titles']['profanity_title_lists'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Which lists should be applied?'),
    '#options' => profanity_get_lists_flat(),
    '#default_value' => variable_get('profanity_title_lists', array()),
    '#states' => array(
      'required' => array(
        ':input[id="edit-profanity-protect-the-titles"]' => array(
          'checked' => TRUE,
        ),
      ),
    ),
  );
  $form['profanity_supply_entity_properties'] = array(
    '#type' => 'checkbox',
    '#title' => t('Supply a profanity filtered title property for all entities'),
    '#default_value' => variable_get('profanity_supply_entity_properties', 0),
  );
  $form['profanity_supply_entity_properties_fieldset'] = array(
    '#type' => 'fieldset',
    '#title' => t('User registration validation settings'),
    '#states' => array(
      'visible' => array(
        ':input[id="edit-profanity-supply-entity-properties"]' => array(
          'checked' => TRUE,
        ),
      ),
    ),
  );
  $form['profanity_supply_entity_properties_fieldset']['profanity_supply_entity_properties_lists'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Which lists should be applied?'),
    '#options' => profanity_get_lists_flat(),
    '#default_value' => variable_get('profanity_supply_entity_properties_lists', array()),
    '#states' => array(
      'required' => array(
        ':input[id="edit-profanity-supply-entity-properties"]' => array(
          'checked' => TRUE,
        ),
      ),
    ),
  );
  $form['profanity_protect_user_reg'] = array(
    '#type' => 'checkbox',
    '#title' => t('Prevent users registering with a username that contains matches'),
    '#default_value' => variable_get('profanity_protect_user_reg', 0),
    '#description' => t("Tick this to choose which lists are run against the entered username. Users won't be able to register until they choose a username which doesn't contain a matched word."),
  );
  $form['user_reg'] = array(
    '#type' => 'fieldset',
    '#title' => t('User registration validation settings'),
    '#states' => array(
      'visible' => array(
        ':input[id="edit-profanity-protect-user-reg"]' => array(
          'checked' => TRUE,
        ),
      ),
    ),
  );
  $form['user_reg']['profanity_protect_user_reg_lists'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Which lists should be applied?'),
    '#options' => profanity_get_lists_flat(),
    '#default_value' => variable_get('profanity_protect_user_reg_lists', array()),
    '#states' => array(
      'required' => array(
        ':input[id="edit-profanity-protect-user-reg"]' => array(
          'checked' => TRUE,
        ),
      ),
    ),
  );
  $form['user_reg']['profanity_protect_user_reg_message'] = array(
    '#type' => 'textfield',
    '#title' => t('Error message to display when matched words are found'),
    '#default_value' => variable_get('profanity_protect_user_reg_message', "The username you've entered contains a word or words which aren't allowed."),
    '#states' => array(
      'required' => array(
        ':input[id="edit-profanity-protect-user-reg"]' => array(
          'checked' => TRUE,
        ),
      ),
    ),
  );
  return system_settings_form($form);
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function profanity_form_user_register_form_alter(&$form, $form_state, $form_id) {
  if (!variable_get('profanity_protect_user_reg', 0)) {
    return;
  }
  $form['#validate'][] = 'profanity_user_register_validate';
}

/**
 * Form validation callback.
 */
function profanity_user_register_validate($form, &$form_state) {
  if (variable_get('profanity_protect_user_reg', 0)) {
    $matched = FALSE;
    foreach (variable_get('profanity_protect_user_reg_lists', array()) as $list_name) {
      if (profanity_list_execute($list_name, $form_state['values']['name'], FALSE) > 0) {
        $matched = TRUE;
        break;
      }
    }
    if ($matched) {
      form_set_error('name', variable_get('profanity_protect_user_reg_message', "The username you've entered contains a word or words which aren't allowed."));
    }
  }
}
class ProfanityPregCallback {
  private $wordlist;
  private $modifiers;
  private $character;
  private $count = 0;
  public function __construct($wordlist, $modifiers, $character) {
    $this->wordlist = $wordlist;
    $this->modifiers = $modifiers;
    $this->character = $character;
  }
  public function execute($text) {
    return preg_replace_callback("/\\b(" . $this->wordlist . ")\\b" . $this->modifiers, array(
      $this,
      'match_replace',
    ), $text, -1);
  }
  public function match_replace($matches) {
    $this->count = $this->count + 1;
    return str_repeat($this->character, strlen(utf8_decode($matches[0])));
  }
  public function get_count() {
    return $this->count;
  }

}

/**
 * Get the label property for a passed entity filtered for profanity.
 */
function profanity_entity_property_getter($data, array $options, $name, $type, $info) {
  $entity_info = entity_get_info($type);
  $title_property = $entity_info['entity keys']['label'];
  $text = $data->{$title_property};
  foreach (variable_get('profanity_supply_entity_properties_lists', array()) as $list_name) {
    $text = profanity_list_execute($list_name, $text);
  }
  return $text;
}

/**
 * Form submit handler.
 */
function profanity_crud_form_submit($form, &$form_state) {

  // Carry out the standard submit process.
  $form_state['object']
    ->edit_form_submit($form, $form_state);

  // Clear any caches for this list.
  cache_clear_all('profanity_list:' . $form_state['values']['name'], 'cache');
}

Functions

Namesort descending Description
profanity_admin_settings Admin settings form callback.
profanity_crud_form_submit Form submit handler.
profanity_ctools_plugin_api Implements of hook_ctools_plugin_api().
profanity_ctools_plugin_directory Implements of hook_ctools_plugin_directory().
profanity_entity_prepare_view Implements hook_entity_load().
profanity_entity_property_getter Get the label property for a passed entity filtered for profanity.
profanity_entity_property_info_alter Implements hook_entity_property_info_alter().
profanity_filter_info Implements hook_filter_info().
profanity_form_user_register_form_alter Implements hook_form_FORM_ID_alter().
profanity_get_lists_flat Function to return a flat list array of machine name indexes and label values.
profanity_list_execute Helper function for use in replacing or validating.
profanity_list_export Export a profanity_list.
profanity_list_get_replacements Grab a list of replacements from cache or set the cache and return.
profanity_list_load Load a single list.
profanity_menu Implements hook_menu().
profanity_permission Implements hook_permission().
profanity_tokens Implements hook_tokens().
profanity_token_info Implements hook_token_info().
profanity_user_register_validate Form validation callback.
profanity_views_api Implements hook_views_api().
_profanity_filter
_profanity_filter_settings Filter settings callback for profanity filter.

Classes