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.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Check that, as in the coding guidelines, every to-do comment links to a tracker issue.
  19   *
  20   * As required by https://moodledev.io/general/development/policies/codingstyle.
  21   *
  22   * @package    core
  23   * @copyright  2009 Tim Hunt
  24   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25   */
  26  
  27  require(__DIR__ . '/../../../config.php');
  28  
  29  require_login();
  30  $context = context_system::instance();
  31  require_capability('moodle/site:config', $context);
  32  
  33  $PAGE->set_url('/lib/tests/other/todochecker.php');
  34  $PAGE->set_context($context);
  35  $PAGE->set_title('To-do checker');
  36  $PAGE->set_heading('To-do checker');
  37  
  38  $thirdparty = load_third_party_lib_list();
  39  $extensionstotest = array('php');
  40  $extensionsregex = '/\.(?:' . implode('|', $extensionstotest) . ')$/';
  41  $patterntofind = 'TO' . 'DO'; // Make it not match the regex.
  42  $found = array();
  43  
  44  echo $OUTPUT->header();
  45  echo $OUTPUT->heading('To-do checker', 2);
  46  
  47  echo $OUTPUT->box_start();
  48  echo 'Checking code ...';
  49  flush();
  50  recurseFolders($CFG->dirroot, 'check_to_dos', $extensionsregex, false, array_keys($thirdparty));
  51  echo ' done.';
  52  echo $OUTPUT->box_end();
  53  
  54  if (empty($found)) {
  55      echo '<p>No to-dos found.</p>';
  56  } else {
  57      $total = 0;
  58      foreach ($found as $filepath => $matches) {
  59          $total += count($matches);
  60      }
  61  
  62      echo '<p>' . $total . ' to-dos found:</p><dl>';
  63      foreach ($found as $filepath => $matches) {
  64          echo '<dt>' . $filepath . ' <b>(' . count($matches) . ')</b></dt><dd><ul>';
  65          foreach ($matches as $lineno => $line) {
  66              $url = 'http://cvs.moodle.org/moodle/' . $filepath . '?view=annotate#l' . $lineno;
  67              $error = '';
  68  
  69              // Make sure there is a tracker issue id mentioned
  70              $matches = array();
  71              if (preg_match('/\bTODO\b.*?\b(MDL-\d+)/', $line, $matches)) {
  72                  $issueid = $matches[1];
  73                  $issueurl = 'http://tracker.moodle.org/browse/' . $issueid;
  74  
  75                  // Make sure the issue is still open.
  76                  list($issueopen, $issuesummary) = issue_info($issueid);
  77                  if ($issueopen) {
  78                      $issuename = $issueid;
  79                  } else {
  80                      $issuename = '<strike>' . $issueid . '</strike>';
  81                      $error = 'The associated tracker issue is Resolved.';
  82                  }
  83  
  84                  $line = str_replace($issueid, '<a href="' . $issueurl . '" title="' . s($issuesummary) .
  85                          '">' . $issuename . '</a>', htmlspecialchars($line, ENT_COMPAT));
  86              } else {
  87                  $line = htmlspecialchars($line, ENT_COMPAT);
  88                  $error = 'No associated tracker issue.';
  89              }
  90  
  91              if ($error) {
  92                  $error = '<span class="error">' . $error . '</span>';
  93              }
  94              echo '<li><a href="' . $url . '">' . $lineno . '</a>: ' . $line . $error . '</li>';
  95          }
  96          echo '</ul></dd>';
  97      }
  98      echo '</dl>';
  99  }
 100  
 101  echo $OUTPUT->footer();
 102  
 103  function check_to_dos($filepath) {
 104      global $CFG, $found, $thirdparty;
 105      if (isset($thirdparty[$filepath])) {
 106          return; // Skip this file.
 107      }
 108      $lines = file($filepath);
 109      $matchesinfile = array();
 110      foreach ($lines as $lineno => $line) {
 111          if (preg_match('/(?<!->|\$)\bTODO\b/i', $line)) {
 112              $matchesinfile[$lineno] = $line;
 113          }
 114      }
 115      if (!empty($matchesinfile)) {
 116          $shortpath = str_replace($CFG->dirroot . '/', '', $filepath);
 117          $found[$shortpath] = $matchesinfile;
 118      }
 119  }
 120  
 121  function issue_info($issueid) {
 122      static $cache = array();
 123      if (array_key_exists($issueid, $cache)) {
 124          return $cache[$issueid];
 125      }
 126  
 127      $xmlurl = 'http://tracker.moodle.org/si/jira.issueviews:issue-xml/' . $issueid . '/' . $issueid . '.xml';
 128      $content = download_file_content($xmlurl);
 129  
 130      // Get the status.
 131      $open = preg_match('/Unresolved<\/resolution>/', $content);
 132  
 133      // Get the summary.
 134      $matches = array();
 135      preg_match('/<title>\[' . $issueid . '\]\s+(.*?)<\/title>/', $content, $matches);
 136      $summary = $matches[1];
 137      preg_match('/<assignee[^>]*>(.*?)<\/assignee>/', $content, $matches);
 138      $summary .= ' - Assignee: ' . $matches[1];
 139  
 140      $cache[$issueid] = array($open, $summary);
 141      return $cache[$issueid];
 142  }
 143  
 144  function load_third_party_lib_list() {
 145      global $CFG;
 146      $libs = array();
 147      $xml = simplexml_load_file($CFG->libdir . '/thirdpartylibs.xml');
 148      foreach ($xml->library as $libobject) {
 149          $libs[$CFG->libdir . '/' . $libobject->location] = 1;
 150      }
 151      return $libs;
 152  }
 153  
 154  function recurseFolders($path, $callback, $fileregexp = '/.*/', $exclude = false, $ignorefolders = array()) {
 155      $files = scandir($path);
 156  
 157      foreach ($files as $file) {
 158          $filepath = $path .'/'. $file;
 159          if (strpos($file, '.') === 0) {
 160              /// Don't check hidden files.
 161              continue;
 162          } else if (is_dir($filepath)) {
 163              if (!in_array($filepath, $ignorefolders)) {
 164                  recurseFolders($filepath, $callback, $fileregexp, $exclude, $ignorefolders);
 165              }
 166          } else if ($exclude xor preg_match($fileregexp, $filepath)) {
 167              call_user_func($callback, $filepath);
 168          }
 169      }
 170  }