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   * @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 a key 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_key_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              'keynameempty' => 'tool_xmldb',
  44              'incorrectkeyname' => 'tool_xmldb',
  45              'duplicatekeyname' => 'tool_xmldb',
  46              'nofieldsspecified' => 'tool_xmldb',
  47              'duplicatefieldsused' => 'tool_xmldb',
  48              'fieldsnotintable' => 'tool_xmldb',
  49              'fieldsusedinkey' => 'tool_xmldb',
  50              'fieldsusedinindex' => 'tool_xmldb',
  51              'noreftablespecified' => 'tool_xmldb',
  52              'wrongnumberofreffields' => 'tool_xmldb',
  53              'noreffieldsspecified' => 'tool_xmldb',
  54              'nomasterprimaryuniquefound' => 'tool_xmldb',
  55              'masterprimaryuniqueordernomatch' => 'tool_xmldb',
  56              'primarykeyonlyallownotnullfields' => 'tool_xmldb',
  57              'back' => 'tool_xmldb',
  58              'administration' => ''
  59          ));
  60      }
  61  
  62      /**
  63       * Invoke method, every class will have its own
  64       * returns true/false on completion, setting both
  65       * errormsg and output as necessary
  66       */
  67      function invoke() {
  68          parent::invoke();
  69  
  70          $result = true;
  71  
  72          // Set own core attributes
  73          //$this->does_generate = ACTION_NONE;
  74          $this->does_generate = ACTION_GENERATE_HTML;
  75  
  76          // These are always here
  77          global $CFG, $XMLDB;
  78  
  79          // Do the job, setting result as needed
  80  
  81          if (!data_submitted()) { // Basic prevention
  82              throw new \moodle_exception('wrongcall', 'error');
  83          }
  84  
  85          // Get parameters
  86          $dirpath = required_param('dir', PARAM_PATH);
  87          $dirpath = $CFG->dirroot . $dirpath;
  88  
  89          $tableparam = strtolower(required_param('table', PARAM_PATH));
  90          $keyparam = strtolower(required_param('key', PARAM_PATH));
  91          $name = trim(strtolower(optional_param('name', $keyparam, PARAM_PATH)));
  92  
  93          $comment = required_param('comment', PARAM_CLEAN);
  94          $comment = trim($comment);
  95  
  96          $type = required_param('type', PARAM_INT);
  97          $fields = required_param('fields', PARAM_CLEAN);
  98          $fields = str_replace(' ', '', trim(strtolower($fields)));
  99  
 100          if ($type == XMLDB_KEY_FOREIGN ||
 101              $type == XMLDB_KEY_FOREIGN_UNIQUE) {
 102              $reftable = trim(strtolower(required_param('reftable', PARAM_PATH)));
 103              $reffields= required_param('reffields', PARAM_CLEAN);
 104              $reffields = str_replace(' ', '', trim(strtolower($reffields)));
 105          }
 106  
 107          $editeddir = $XMLDB->editeddirs[$dirpath];
 108          $structure = $editeddir->xml_file->getStructure();
 109          $table = $structure->getTable($tableparam);
 110          $key = $table->getKey($keyparam);
 111          $oldhash = $key->getHash();
 112  
 113          $errors = array(); // To store all the errors found
 114  
 115          // Perform some checks
 116          // Check empty name
 117          if (empty($name)) {
 118              $errors[] = $this->str['keynameempty'];
 119          }
 120          // Check incorrect name
 121          if ($name == 'changeme') {
 122              $errors[] = $this->str['incorrectkeyname'];
 123          }
 124          // Check duplicate name
 125          if ($keyparam != $name && $table->getKey($name)) {
 126              $errors[] = $this->str['duplicatekeyname'];
 127          }
 128          $fieldsarr = explode(',', $fields);
 129          // Check the fields isn't empty
 130          if (empty($fieldsarr[0])) {
 131              $errors[] = $this->str['nofieldsspecified'];
 132          } else {
 133              // Check that there aren't duplicate column names
 134              $uniquearr = array_unique($fieldsarr);
 135              if (count($fieldsarr) != count($uniquearr)) {
 136                  $errors[] = $this->str['duplicatefieldsused'];
 137              }
 138              // Check that all the fields in belong to the table
 139              foreach ($fieldsarr as $field) {
 140                  if (!$table->getField($field)) {
 141                      $errors[] = $this->str['fieldsnotintable'];
 142                      break;
 143                  }
 144              }
 145              // If primary, check that all the fields are not null
 146              if ($type == XMLDB_KEY_PRIMARY) {
 147                  foreach ($fieldsarr as $field) {
 148                      if ($fi = $table->getField($field)) {
 149                          if (!$fi->getNotNull()) {
 150                              $errors[] = $this->str['primarykeyonlyallownotnullfields'];
 151                              break;
 152                          }
 153                      }
 154                  }
 155              }
 156              // Check that there isn't any key using exactly the same fields
 157              $tablekeys = $table->getKeys();
 158              if ($tablekeys) {
 159                  foreach ($tablekeys as $tablekey) {
 160                      // Skip checking against itself
 161                      if ($keyparam == $tablekey->getName()) {
 162                          continue;
 163                      }
 164                      $keyfieldsarr = $tablekey->getFields();
 165                      // Compare both arrays, looking for diferences
 166                      $diferences = array_merge(array_diff($fieldsarr, $keyfieldsarr), array_diff($keyfieldsarr, $fieldsarr));
 167                      if (empty($diferences)) {
 168                          $errors[] = $this->str['fieldsusedinkey'];
 169                          break;
 170                      }
 171                  }
 172              }
 173              // Check that there isn't any index using exactlt the same fields
 174              $tableindexes = $table->getIndexes();
 175              if ($tableindexes) {
 176                  foreach ($tableindexes as $tableindex) {
 177                      $indexfieldsarr = $tableindex->getFields();
 178                      // Compare both arrays, looking for diferences
 179                      $diferences = array_merge(array_diff($fieldsarr, $indexfieldsarr), array_diff($indexfieldsarr, $fieldsarr));
 180                      if (empty($diferences)) {
 181                          $errors[] = $this->str['fieldsusedinindex'];
 182                          break;
 183                      }
 184                  }
 185              }
 186              // If foreign key
 187              if ($type == XMLDB_KEY_FOREIGN ||
 188                  $type == XMLDB_KEY_FOREIGN_UNIQUE) {
 189                  $reffieldsarr = explode(',', $reffields);
 190                  // Check reftable is not empty
 191                  if (empty($reftable)) {
 192                      $errors[] = $this->str['noreftablespecified'];
 193                  } else
 194                  // Check reffields are not empty
 195                  if (empty($reffieldsarr[0])) {
 196                      $errors[] = $this->str['noreffieldsspecified'];
 197                  } else
 198                  // Check the number of fields is correct
 199                  if (count($fieldsarr) != count($reffieldsarr)) {
 200                      $errors[] = $this->str['wrongnumberofreffields'];
 201                  } else {
 202                  // Check, if pointing to one structure table, that there is one master key for this key
 203                      if ($rt = $structure->getTable($reftable)) {
 204                          $masterfound = false;
 205                          $reftablekeys = $rt->getKeys();
 206                          if ($reftablekeys) {
 207                              foreach ($reftablekeys as $reftablekey) {
 208                                  // Only compare with primary and unique keys
 209                                  if ($reftablekey->getType() != XMLDB_KEY_PRIMARY && $reftablekey->getType() != XMLDB_KEY_UNIQUE) {
 210                                      continue;
 211                                  }
 212                                  $keyfieldsarr = $reftablekey->getFields();
 213                                  // Compare both arrays, looking for diferences
 214                                  $diferences = array_merge(array_diff($reffieldsarr, $keyfieldsarr), array_diff($keyfieldsarr, $reffieldsarr));
 215                                  if (empty($diferences)) {
 216                                      $masterfound = true;
 217                                      break;
 218                                  }
 219                              }
 220                              if (!$masterfound) {
 221                                  $errors[] = $this->str['nomasterprimaryuniquefound'];
 222                              } else {
 223                                  // Quick test of the order
 224                                 if (implode(',', $reffieldsarr) != implode(',', $keyfieldsarr)) {
 225                                     $errors[] = $this->str['masterprimaryuniqueordernomatch'];
 226                                 }
 227                              }
 228                          }
 229                      }
 230                  }
 231              }
 232          }
 233  
 234  
 235          if (!empty($errors)) {
 236              $tempkey = new xmldb_key($name);
 237              $tempkey->setType($type);
 238              $tempkey->setFields($fieldsarr);
 239              if ($type == XMLDB_KEY_FOREIGN ||
 240                  $type == XMLDB_KEY_FOREIGN_UNIQUE) {
 241                  $tempkey->setRefTable($reftable);
 242                  $tempkey->setRefFields($reffieldsarr);
 243              }
 244              // Prepare the output
 245              $o = '<p>' .implode(', ', $errors) . '</p>
 246                    <p>' . $name . ': ' . $tempkey->readableInfo() . '</p>';
 247              $o.= '<a href="index.php?action=edit_key&amp;key=' .$key->getName() . '&amp;table=' . $table->getName() .
 248                   '&amp;dir=' . urlencode(str_replace($CFG->dirroot, '', $dirpath)) . '">[' . $this->str['back'] . ']</a>';
 249              $this->output = $o;
 250          }
 251  
 252          // Continue if we aren't under errors
 253          if (empty($errors)) {
 254              // If there is one name change, do it, changing the prev and next
 255              // attributes of the adjacent fields
 256              if ($keyparam != $name) {
 257                  $key->setName($name);
 258                  if ($key->getPrevious()) {
 259                      $prev = $table->getKey($key->getPrevious());
 260                      $prev->setNext($name);
 261                      $prev->setChanged(true);
 262                  }
 263                  if ($key->getNext()) {
 264                      $next = $table->getKey($key->getNext());
 265                      $next->setPrevious($name);
 266                      $next->setChanged(true);
 267                  }
 268              }
 269  
 270              // Set comment
 271              $key->setComment($comment);
 272  
 273              // Set the rest of fields
 274              $key->setType($type);
 275              $key->setFields($fieldsarr);
 276              if ($type == XMLDB_KEY_FOREIGN ||
 277                  $type == XMLDB_KEY_FOREIGN_UNIQUE) {
 278                  $key->setRefTable($reftable);
 279                  $key->setRefFields($reffieldsarr);
 280              }
 281  
 282              // If the hash has changed from the old one, change the version
 283              // and mark the structure as changed
 284              $key->calculateHash(true);
 285              if ($oldhash != $key->getHash()) {
 286                  $key->setChanged(true);
 287                  $table->setChanged(true);
 288                  // Recalculate the structure hash
 289                  $structure->calculateHash(true);
 290                  $structure->setVersion(userdate(time(), '%Y%m%d', 99, false));
 291                  // Mark as changed
 292                  $structure->setChanged(true);
 293              }
 294  
 295              // Launch postaction if exists (leave this here!)
 296              if ($this->getPostAction() && $result) {
 297                  return $this->launch($this->getPostAction());
 298              }
 299          }
 300  
 301          // Return ok if arrived here
 302          return $result;
 303      }
 304  }
 305