Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
   1  <?php
   2  
   3  /**
   4   * Spam Cleaner
   5   *
   6   * Helps an admin to clean up spam in Moodle
   7   *
   8   * @author Dongsheng Cai
   9   * @author Martin Dougiamas
  10   * @author Amr Hourani
  11   * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
  12   */
  13  
  14  // List of known spammy keywords, please add more here
  15  
  16  /////////////////////////////////////////////////////////////////////////////////
  17  
  18  require_once('../../../config.php');
  19  require_once($CFG->libdir.'/adminlib.php');
  20  
  21  
  22  // Configuration
  23  
  24  $autokeywords = array(
  25                      "<img",
  26                      "fuck",
  27                      "casino",
  28                      "porn",
  29                      "xxx",
  30                      "cialis",
  31                      "viagra",
  32                      "poker",
  33                      "warcraft"
  34                  );
  35  
  36  $keyword = optional_param('keyword', '', PARAM_RAW);
  37  $autodetect = optional_param('autodetect', '', PARAM_RAW);
  38  $del = optional_param('del', '', PARAM_RAW);
  39  $delall = optional_param('delall', '', PARAM_RAW);
  40  $ignore = optional_param('ignore', '', PARAM_RAW);
  41  $reset = optional_param('reset', '', PARAM_RAW);
  42  $id = optional_param('id', '', PARAM_INT);
  43  
  44  admin_externalpage_setup('toolspamcleaner');
  45  
  46  // Delete one user
  47  if (!empty($del) && confirm_sesskey() && ($id != $USER->id)) {
  48      if (isset($SESSION->users_result[$id])) {
  49          $user = $SESSION->users_result[$id];
  50          if (delete_user($user)) {
  51              unset($SESSION->users_result[$id]);
  52              echo json_encode(true);
  53          } else {
  54              echo json_encode(false);
  55          }
  56      } else {
  57          echo json_encode(false);
  58      }
  59      exit;
  60  }
  61  
  62  // Delete lots of users
  63  if (!empty($delall) && confirm_sesskey()) {
  64      if (!empty($SESSION->users_result)) {
  65          foreach ($SESSION->users_result as $userid => $user) {
  66              if ($userid != $USER->id) {
  67                  if (delete_user($user)) {
  68                      unset($SESSION->users_result[$userid]);
  69                  }
  70              }
  71          }
  72      }
  73      echo json_encode(true);
  74      exit;
  75  }
  76  
  77  if (!empty($ignore) && confirm_sesskey()) {
  78      unset($SESSION->users_result[$id]);
  79      echo json_encode(true);
  80      exit;
  81  }
  82  
  83  $PAGE->requires->js_init_call('M.tool_spamcleaner.init', array(me()), true);
  84  $strings = Array('spaminvalidresult','spamdeleteallconfirm','spamcannotdelete','spamdeleteconfirm');
  85  $PAGE->requires->strings_for_js($strings, 'tool_spamcleaner');
  86  
  87  echo $OUTPUT->header();
  88  
  89  // Print headers and things
  90  echo $OUTPUT->box(get_string('spamcleanerintro', 'tool_spamcleaner'));
  91  
  92  echo $OUTPUT->box_start();     // The forms section at the top
  93  
  94  ?>
  95  
  96  <div class="mdl-align">
  97  
  98  <form method="post" action="index.php" class="form-inline spamcleanerform">
  99    <div>
 100      <label class="accesshide" for="keyword_el"><?php print_string('spamkeyword', 'tool_spamcleaner') ?></label>
 101      <input type="text" class="form-control" name="keyword" id="keyword_el" value="<?php p($keyword) ?>" />
 102      <input type="hidden" name="sesskey" value="<?php echo sesskey();?>" />
 103      <input type="submit" class="btn btn-primary" value="<?php echo get_string('spamsearch', 'tool_spamcleaner')?>" />
 104    </div>
 105  </form>
 106  <p><?php echo get_string('spameg', 'tool_spamcleaner');?></p>
 107  
 108  <hr />
 109  
 110  <form method="post"  action="index.php">
 111    <div>
 112      <input type="submit" class="btn btn-primary" name="autodetect"
 113             value="<?php echo get_string('spamauto', 'tool_spamcleaner');?>" />
 114    </div>
 115  </form>
 116  
 117  
 118  </div>
 119  
 120  <?php
 121  echo $OUTPUT->box_end();
 122  
 123  echo '<div id="result" class="mdl-align">';
 124  
 125  // Print list of resulting profiles
 126  
 127  if (!empty($keyword)) {               // Use the keyword(s) supplied by the user
 128      $keywords = explode(',', $keyword);
 129      foreach ($keywords as $key => $keyword) {
 130          $keywords[$key] = trim($keyword);
 131      }
 132      search_spammers($keywords);
 133  
 134  } else if (!empty($autodetect)) {     // Use the inbuilt keyword list to detect users
 135      search_spammers($autokeywords);
 136  }
 137  
 138  echo '</div>';
 139  
 140  /////////////////////////////////////////////////////////////////////////////////
 141  
 142  
 143  ///  Functions
 144  
 145  
 146  function search_spammers($keywords) {
 147  
 148      global $CFG, $USER, $DB, $OUTPUT;
 149  
 150      if (!is_array($keywords)) {
 151          $keywords = array($keywords);    // Make it into an array
 152      }
 153  
 154      $params = array('userid'=>$USER->id);
 155  
 156      $keywordfull = array();
 157      $i = 0;
 158      foreach ($keywords as $keyword) {
 159          $keywordfull[] = $DB->sql_like('description', ':descpat'.$i, false);
 160          $params['descpat'.$i] = "%$keyword%";
 161          $keywordfull2[] = $DB->sql_like('p.summary', ':sumpat'.$i, false);
 162          $params['sumpat'.$i] = "%$keyword%";
 163          $keywordfull3[] = $DB->sql_like('p.subject', ':subpat'.$i, false);
 164          $params['subpat'.$i] = "%$keyword%";
 165          $keywordfull4[] = $DB->sql_like('c.content', ':contpat'.$i, false);
 166          $params['contpat'.$i] = "%$keyword%";
 167          $keywordfull5[] = $DB->sql_like('m.fullmessage', ':msgpat'.$i, false);
 168          $params['msgpat'.$i] = "%$keyword%";
 169          $keywordfull6[] = $DB->sql_like('fp.message', ':forumpostpat'.$i, false);
 170          $params['forumpostpat'.$i] = "%$keyword%";
 171          $keywordfull7[] = $DB->sql_like('fp.subject', ':forumpostsubpat'.$i, false);
 172          $params['forumpostsubpat'.$i] = "%$keyword%";
 173          $i++;
 174      }
 175      $conditions = '( '.implode(' OR ', $keywordfull).' )';
 176      $conditions2 = '( '.implode(' OR ', $keywordfull2).' )';
 177      $conditions3 = '( '.implode(' OR ', $keywordfull3).' )';
 178      $conditions4 = '( '.implode(' OR ', $keywordfull4).' )';
 179      $conditions5 = '( '.implode(' OR ', $keywordfull5).' )';
 180      $conditions6 = '( '.implode(' OR ', $keywordfull6).' )';
 181      $conditions7 = '( '.implode(' OR ', $keywordfull7).' )';
 182  
 183      $sql  = "SELECT *
 184                 FROM {user}
 185                WHERE deleted = 0
 186                      AND id <> :userid
 187                      AND $conditions";  // Exclude oneself
 188      $sql2 = "SELECT u.*, p.summary
 189                 FROM {user} u, {post} p
 190                WHERE $conditions2
 191                      AND u.deleted = 0
 192                      AND u.id=p.userid
 193                      AND u.id <> :userid";
 194      $sql3 = "SELECT u.*, p.subject AS postsubject
 195                 FROM {user} u, {post} p
 196                WHERE $conditions3
 197                      AND u.deleted = 0
 198                      AND u.id=p.userid
 199                      AND u.id <> :userid";
 200      $sql4 = "SELECT u.*, c.content
 201                 FROM {user} u, {comments} c
 202                 WHERE $conditions4
 203                      AND u.deleted = 0
 204                      AND u.id=c.userid
 205                      AND u.id <> :userid";
 206      $sql5 = "SELECT u.*, m.fullmessage
 207                 FROM {user} u, {message} m
 208                WHERE $conditions5
 209                      AND u.deleted = 0
 210                      AND u.id=m.useridfrom
 211                      AND u.id <> :userid";
 212      $sql6 = "SELECT u.*, fp.message
 213                 FROM {user} u, {forum_posts} fp
 214                WHERE $conditions6
 215                      AND u.deleted = 0
 216                      AND u.id=fp.userid
 217                      AND u.id <> :userid";
 218      $sql7 = "SELECT u.*, fp.subject
 219                 FROM {user} u, {forum_posts} fp
 220                WHERE $conditions7
 221                      AND u.deleted = 0
 222                      AND u.id=fp.userid
 223                      AND u.id <> :userid";
 224  
 225      $spamusers_desc = $DB->get_recordset_sql($sql, $params);
 226      $spamusers_blog = $DB->get_recordset_sql($sql2, $params);
 227      $spamusers_blogsub = $DB->get_recordset_sql($sql3, $params);
 228      $spamusers_comment = $DB->get_recordset_sql($sql4, $params);
 229      $spamusers_message = $DB->get_recordset_sql($sql5, $params);
 230      $spamusers_forumpost = $DB->get_recordset_sql($sql6, $params);
 231      $spamusers_forumpostsub = $DB->get_recordset_sql($sql7, $params);
 232  
 233      $keywordlist = implode(', ', $keywords);
 234      echo $OUTPUT->box(get_string('spamresult', 'tool_spamcleaner').s($keywordlist)).' ...';
 235  
 236      $recordsets = [
 237          $spamusers_desc,
 238          $spamusers_blog,
 239          $spamusers_blogsub,
 240          $spamusers_comment,
 241          $spamusers_message,
 242          $spamusers_forumpost,
 243          $spamusers_forumpostsub
 244      ];
 245      print_user_list($recordsets, $keywords);
 246      foreach ($recordsets as $rs) {
 247          $rs->close();
 248      }
 249  }
 250  
 251  
 252  
 253  function print_user_list($users_rs, $keywords) {
 254      global $CFG, $SESSION;
 255  
 256      // reset session everytime this function is called
 257      $SESSION->users_result = array();
 258      $count = 0;
 259  
 260      foreach ($users_rs as $rs){
 261          foreach ($rs as $user) {
 262              if (!$count) {
 263                  echo '<table class="table table-bordered" border="1" width="100%" id="data-grid"><tr><th>&nbsp;</th>
 264                      <th>'.get_string('user', 'admin').'</th><th>'.get_string('spamdesc', 'tool_spamcleaner').'</th>
 265                      <th>'.get_string('spamoperation', 'tool_spamcleaner').'</th></tr>';
 266              }
 267              $count++;
 268              filter_user($user, $keywords, $count);
 269          }
 270      }
 271  
 272      if (!$count) {
 273          echo get_string('spamcannotfinduser', 'tool_spamcleaner');
 274  
 275      } else {
 276          echo '</table>';
 277          echo '<div class="mld-align">
 278                <button id="removeall_btn" class="btn btn-secondary">'.get_string('spamdeleteall', 'tool_spamcleaner').'</button>
 279                </div>';
 280      }
 281  }
 282  function filter_user($user, $keywords, $count) {
 283      global $CFG;
 284      $image_search = false;
 285      if (in_array('<img', $keywords)) {
 286          $image_search = true;
 287      }
 288      if (isset($user->summary)) {
 289          $user->description = '<h3>'.get_string('spamfromblog', 'tool_spamcleaner').'</h3>'.$user->summary;
 290          unset($user->summary);
 291      } else if (isset($user->postsubject)) {
 292          $user->description = '<h3>'.get_string('spamfromblog', 'tool_spamcleaner').'</h3>'.$user->postsubject;
 293          unset($user->postsubject);
 294      } else if (isset($user->content)) {
 295          $user->description = '<h3>'.get_string('spamfromcomments', 'tool_spamcleaner').'</h3>'.$user->content;
 296          unset($user->content);
 297      } else if (isset($user->fullmessage)) {
 298          $user->description = '<h3>'.get_string('spamfrommessages', 'tool_spamcleaner').'</h3>'.$user->fullmessage;
 299          unset($user->fullmessage);
 300      } else if (isset($user->message)) {
 301          $user->description = '<h3>'.get_string('spamfromforumpost', 'tool_spamcleaner').'</h3>'.$user->message;
 302          unset($user->message);
 303      } else if (isset($user->subject)) {
 304          $user->description = '<h3>'.get_string('spamfromforumpost', 'tool_spamcleaner').'</h3>'.$user->subject;
 305          unset($user->subject);
 306      }
 307  
 308      if (preg_match('#<img.*src=[\"\']('.$CFG->wwwroot.')#', $user->description, $matches)
 309          && $image_search) {
 310          $result = false;
 311          foreach ($keywords as $keyword) {
 312              if (preg_match('#'.$keyword.'#', $user->description)
 313                  && ($keyword != '<img')) {
 314                  $result = true;
 315              }
 316          }
 317          if ($result) {
 318              echo print_user_entry($user, $keywords, $count);
 319          } else {
 320              unset($user);
 321          }
 322      } else {
 323          echo print_user_entry($user, $keywords, $count);
 324      }
 325  }
 326  
 327  
 328  function print_user_entry($user, $keywords, $count) {
 329  
 330      global $SESSION, $CFG;
 331  
 332      $smalluserobject = new stdClass();      // All we need to delete them later
 333      $smalluserobject->id = $user->id;
 334      $smalluserobject->email = $user->email;
 335      $smalluserobject->auth = $user->auth;
 336      $smalluserobject->firstname = $user->firstname;
 337      $smalluserobject->lastname = $user->lastname;
 338      $smalluserobject->username = $user->username;
 339  
 340      if (empty($SESSION->users_result[$user->id])) {
 341          $SESSION->users_result[$user->id] = $smalluserobject;
 342          $html = '<tr valign="top" id="row-'.$user->id.'" class="result-row">';
 343          $html .= '<td width="10">'.$count.'</td>';
 344          $html .= '<td width="30%" align="left"><a href="'.$CFG->wwwroot."/user/view.php?course=1&amp;id=".$user->id.'" title="'.s($user->username).'">'.fullname($user).'</a>';
 345  
 346          $html .= "<ul>";
 347          $profile_set = array('city'=>true, 'country'=>true, 'email'=>true);
 348          foreach ($profile_set as $key=>$value) {
 349              if (isset($user->$key)){
 350                  $html .= '<li>'.$user->$key.'</li>';
 351              }
 352          }
 353          $html .= "</ul>";
 354          $html .= '</td>';
 355  
 356          foreach ($keywords as $keyword) {
 357              $user->description = highlight($keyword, $user->description);
 358          }
 359  
 360          if (!isset($user->descriptionformat)) {
 361              $user->descriptionformat = FORMAT_MOODLE;
 362          }
 363  
 364          $html .= '<td align="left">'.format_text($user->description, $user->descriptionformat, array('overflowdiv'=>true)).'</td>';
 365          $html .= '<td width="100px" align="center">';
 366          $html .= '<button class="btn btn-primary" onclick="M.tool_spamcleaner.del_user(this,'.$user->id.')">'.
 367              get_string('deleteuser', 'admin').'</button><br />';
 368          $html .= '<button class="btn btn-secondary" onclick="M.tool_spamcleaner.ignore_user(this,'.$user->id.')">'.
 369              get_string('ignore', 'admin').'</button>';
 370          $html .= '</td>';
 371          $html .= '</tr>';
 372          return $html;
 373      } else {
 374          return null;
 375      }
 376  
 377  
 378  }
 379  
 380  echo $OUTPUT->footer();