Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]

   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   * @package    tool_xmldb
  19   * @copyright  2003 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
  20   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  21   */
  22  
  23  /**
  24   * This class verifies all the data introduced when editing an index for correctness,
  25   * performing changes / displaying errors depending of the results.
  26   *
  27   * @package    tool_xmldb
  28   * @copyright  2003 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
  29   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  30   */
  31  class edit_index_save extends XMLDBAction {
  32  
  33      /**
  34       * Init method, every subclass will have its own
  35       */
  36      function init() {
  37          parent::init();
  38  
  39          // Set own custom attributes
  40  
  41          // Get needed strings
  42          $this->loadStrings(array(
  43              'indexnameempty' => 'tool_xmldb',
  44              'incorrectindexname' => 'tool_xmldb',
  45              'duplicateindexname' => 'tool_xmldb',
  46              'nofieldsspecified' => 'tool_xmldb',
  47              'duplicatefieldsused' => 'tool_xmldb',
  48              'fieldsnotintable' => 'tool_xmldb',
  49              'fieldsusedinkey' => 'tool_xmldb',
  50              'fieldsusedinindex' => 'tool_xmldb',
  51              'back' => 'tool_xmldb',
  52              'administration' => ''
  53          ));
  54      }
  55  
  56      /**
  57       * Invoke method, every class will have its own
  58       * returns true/false on completion, setting both
  59       * errormsg and output as necessary
  60       */
  61      function invoke() {
  62          parent::invoke();
  63  
  64          $result = true;
  65  
  66          // Set own core attributes
  67          //$this->does_generate = ACTION_NONE;
  68          $this->does_generate = ACTION_GENERATE_HTML;
  69  
  70          // These are always here
  71          global $CFG, $XMLDB;
  72  
  73          // Do the job, setting result as needed
  74  
  75          if (!data_submitted()) { // Basic prevention
  76              print_error('wrongcall', 'error');
  77          }
  78  
  79          // Get parameters
  80          $dirpath = required_param('dir', PARAM_PATH);
  81          $dirpath = $CFG->dirroot . $dirpath;
  82  
  83          $tableparam = strtolower(required_param('table', PARAM_PATH));
  84          $indexparam = strtolower(required_param('index', PARAM_PATH));
  85          $name = trim(strtolower(optional_param('name', $indexparam, PARAM_PATH)));
  86  
  87          $comment = required_param('comment', PARAM_CLEAN);
  88          $comment = trim($comment);
  89  
  90          $unique = required_param('unique', PARAM_INT);
  91          $fields = required_param('fields', PARAM_CLEAN);
  92          $fields = str_replace(' ', '', trim(strtolower($fields)));
  93          $hints = required_param('hints', PARAM_CLEAN);
  94          $hints = str_replace(' ', '', trim(strtolower($hints)));
  95  
  96          $editeddir = $XMLDB->editeddirs[$dirpath];
  97          $structure = $editeddir->xml_file->getStructure();
  98          $table = $structure->getTable($tableparam);
  99          $index = $table->getIndex($indexparam);
 100          $oldhash = $index->getHash();
 101  
 102          $errors = array(); // To store all the errors found
 103  
 104          // Perform some checks
 105          // Check empty name
 106          if (empty($name)) {
 107              $errors[] = $this->str['indexnameempty'];
 108          }
 109          // Check incorrect name
 110          if ($name == 'changeme') {
 111              $errors[] = $this->str['incorrectindexname'];
 112          }
 113          // Check duplicate name
 114          if ($indexparam != $name && $table->getIndex($name)) {
 115              $errors[] = $this->str['duplicateindexname'];
 116          }
 117          $fieldsarr = explode(',', $fields);
 118          // Check the fields isn't empty
 119          if (empty($fieldsarr[0])) {
 120              $errors[] = $this->str['nofieldsspecified'];
 121          } else {
 122              // Check that there aren't duplicate column names
 123              $uniquearr = array_unique($fieldsarr);
 124              if (count($fieldsarr) != count($uniquearr)) {
 125                  $errors[] = $this->str['duplicatefieldsused'];
 126              }
 127              // Check that all the fields in belong to the table
 128              foreach ($fieldsarr as $field) {
 129                  if (!$table->getField($field)) {
 130                      $errors[] = $this->str['fieldsnotintable'];
 131                      break;
 132                  }
 133              }
 134              // Check that there isn't any key using exactly the same fields
 135              $tablekeys = $table->getKeys();
 136              if ($tablekeys) {
 137                  foreach ($tablekeys as $tablekey) {
 138                      $keyfieldsarr = $tablekey->getFields();
 139                      // Compare both arrays, looking for differences
 140                      $diferences = array_merge(array_diff($fieldsarr, $keyfieldsarr), array_diff($keyfieldsarr, $fieldsarr));
 141                      if (empty($diferences)) {
 142                          $errors[] = $this->str['fieldsusedinkey'];
 143                          break;
 144                      }
 145                  }
 146              }
 147              // Check that there isn't any index using exactly the same fields
 148              $tableindexes = $table->getIndexes();
 149              if ($tableindexes) {
 150                  foreach ($tableindexes as $tableindex) {
 151                      // Skip checking against itself
 152                      if ($indexparam == $tableindex->getName()) {
 153                          continue;
 154                      }
 155                      $indexfieldsarr = $tableindex->getFields();
 156                      // Compare both arrays, looking for differences
 157                      $diferences = array_merge(array_diff($fieldsarr, $indexfieldsarr), array_diff($indexfieldsarr, $fieldsarr));
 158                      if (empty($diferences)) {
 159                          $errors[] = $this->str['fieldsusedinindex'];
 160                          break;
 161                      }
 162                  }
 163              }
 164          }
 165          $hintsarr = array();
 166          foreach (explode(',', $hints) as $hint) {
 167              $hint = preg_replace('/[^a-z]/', '', $hint);
 168              if ($hint === '') {
 169                  continue;
 170              }
 171              $hintsarr[] = $hint;
 172          }
 173  
 174          if (!empty($errors)) {
 175              $tempindex = new xmldb_index($name);
 176              $tempindex->setUnique($unique);
 177              $tempindex->setFields($fieldsarr);
 178              $tempindex->setHints($hintsarr);
 179              // Prepare the output
 180              $o = '<p>' .implode(', ', $errors) . '</p>
 181                    <p>' . $tempindex->readableInfo() . '</p>';
 182              $o.= '<a href="index.php?action=edit_index&amp;index=' .$index->getName() . '&amp;table=' . $table->getName() .
 183                   '&amp;dir=' . urlencode(str_replace($CFG->dirroot, '', $dirpath)) . '">[' . $this->str['back'] . ']</a>';
 184              $this->output = $o;
 185          }
 186  
 187          // Continue if we aren't under errors
 188          if (empty($errors)) {
 189              // If there is one name change, do it, changing the prev and next
 190              // attributes of the adjacent fields
 191              if ($indexparam != $name) {
 192                  $index->setName($name);
 193                  if ($index->getPrevious()) {
 194                      $prev = $table->getIndex($index->getPrevious());
 195                      $prev->setNext($name);
 196                      $prev->setChanged(true);
 197                  }
 198                  if ($index->getNext()) {
 199                      $next = $table->getIndex($index->getNext());
 200                      $next->setPrevious($name);
 201                      $next->setChanged(true);
 202                  }
 203              }
 204  
 205              // Set comment
 206              $index->setComment($comment);
 207  
 208              // Set the rest of fields
 209              $index->setUnique($unique);
 210              $index->setFields($fieldsarr);
 211              $index->setHints($hintsarr);
 212  
 213              // If the hash has changed from the old one, change the version
 214              // and mark the structure as changed
 215              $index->calculateHash(true);
 216              if ($oldhash != $index->getHash()) {
 217                  $index->setChanged(true);
 218                  $table->setChanged(true);
 219                  // Recalculate the structure hash
 220                  $structure->calculateHash(true);
 221                  $structure->setVersion(userdate(time(), '%Y%m%d', 99, false));
 222                  // Mark as changed
 223                  $structure->setChanged(true);
 224              }
 225  
 226              // Launch postaction if exists (leave this here!)
 227              if ($this->getPostAction() && $result) {
 228                  return $this->launch($this->getPostAction());
 229              }
 230          }
 231  
 232          // Return ok if arrived here
 233          return $result;
 234      }
 235  }
 236