[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/mod/data/ -> locallib.php (source)

   1  <?php
   2  
   3  // This file is part of Moodle - http://moodle.org/
   4  //
   5  // Moodle is free software: you can redistribute it and/or modify
   6  // it under the terms of the GNU General Public License as published by
   7  // the Free Software Foundation, either version 3 of the License, or
   8  // (at your option) any later version.
   9  //
  10  // Moodle is distributed in the hope that it will be useful,
  11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13  // GNU General Public License for more details.
  14  //
  15  // You should have received a copy of the GNU General Public License
  16  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  17  
  18  /**
  19   * @package   mod_data
  20   * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  21   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  22   */
  23  
  24  defined('MOODLE_INTERNAL') || die();
  25  
  26  require_once($CFG->dirroot . '/mod/data/lib.php');
  27  require_once($CFG->libdir . '/portfolio/caller.php');
  28  require_once($CFG->libdir . '/filelib.php');
  29  
  30  /**
  31   * The class to handle entry exports of a database module
  32   */
  33  class data_portfolio_caller extends portfolio_module_caller_base {
  34  
  35      /** @var int the single record to export */
  36      protected $recordid;
  37  
  38      /** @var object the record from the data table */
  39      private $data;
  40  
  41      /**#@+ @var array the fields used and their fieldtypes */
  42      private $fields;
  43      private $fieldtypes;
  44  
  45      /** @var object the records to export */
  46      private $records;
  47  
  48      /** @var int how many records are 'mine' */
  49      private $minecount;
  50  
  51      /**
  52       * the required callback arguments for a single-record export
  53       *
  54       * @return array
  55       */
  56      public static function expected_callbackargs() {
  57          return array(
  58              'id'       => true,
  59              'recordid' => false,
  60          );
  61      }
  62  
  63      /**
  64       * @param array $callbackargs the arguments passed through
  65       */
  66      public function __construct($callbackargs) {
  67          parent::__construct($callbackargs);
  68          // set up the list of fields to export
  69          $this->selectedfields = array();
  70          foreach ($callbackargs as $key => $value) {
  71              if (strpos($key, 'field_') === 0) {
  72                  $this->selectedfields[] = substr($key, 6);
  73              }
  74          }
  75      }
  76  
  77      /**
  78       * load up the data needed for the export
  79       *
  80       * @global object $DB
  81       */
  82      public function load_data() {
  83          global $DB, $USER;
  84          if (!$this->cm = get_coursemodule_from_id('data', $this->id)) {
  85              throw new portfolio_caller_exception('invalidid', 'data');
  86          }
  87          if (!$this->data = $DB->get_record('data', array('id' => $this->cm->instance))) {
  88              throw new portfolio_caller_exception('invalidid', 'data');
  89          }
  90          $fieldrecords = $DB->get_records('data_fields', array('dataid' => $this->cm->instance), 'id');
  91          // populate objets for this databases fields
  92          $this->fields = array();
  93          foreach ($fieldrecords as $fieldrecord) {
  94              $tmp = data_get_field($fieldrecord, $this->data);
  95              $this->fields[] = $tmp;
  96              $this->fieldtypes[]  = $tmp->type;
  97          }
  98  
  99          $this->records = array();
 100          if ($this->recordid) {
 101              $tmp = $DB->get_record('data_records', array('id' => $this->recordid));
 102              $tmp->content = $DB->get_records('data_content', array('recordid' => $this->recordid));
 103              $this->records[] = $tmp;
 104          } else {
 105              $where = array('dataid' => $this->data->id);
 106              if (!has_capability('mod/data:exportallentries', context_module::instance($this->cm->id))) {
 107                  $where['userid'] = $USER->id; // get them all in case, we'll unset ones that aren't ours later if necessary
 108              }
 109              $tmp = $DB->get_records('data_records', $where);
 110              foreach ($tmp as $t) {
 111                  $t->content = $DB->get_records('data_content', array('recordid' => $t->id));
 112                  $this->records[] = $t;
 113              }
 114              $this->minecount = $DB->count_records('data_records', array('dataid' => $this->data->id, 'userid' => $USER->id));
 115          }
 116  
 117          if ($this->recordid) {
 118              list($formats, $files) = self::formats($this->fields, $this->records[0]);
 119              $this->set_file_and_format_data($files);
 120          }
 121      }
 122  
 123      /**
 124       * How long we think the export will take
 125       * Single entry is probably not too long.
 126       * But we check for filesizes
 127       * Else base it on the number of records
 128       *
 129       * @return one of PORTFOLIO_TIME_XX constants
 130       */
 131      public function expected_time() {
 132          if ($this->recordid) {
 133              return $this->expected_time_file();
 134          } else {
 135              return portfolio_expected_time_db(count($this->records));
 136          }
 137      }
 138  
 139      /**
 140       * Calculate the shal1 of this export
 141       * Dependent on the export format.
 142       * @return string
 143       */
 144      public function get_sha1() {
 145          // in the case that we're exporting a subclass of 'file' and we have a singlefile,
 146          // then we're not exporting any metadata, just the file by itself by mimetype.
 147          if ($this->exporter->get('format') instanceof portfolio_format_file && $this->singlefile) {
 148              return $this->get_sha1_file();
 149          }
 150          // otherwise we're exporting some sort of multipart content so use the data
 151          $str = '';
 152          foreach ($this->records as $record) {
 153              foreach ($record as $data) {
 154                  if (is_array($data) || is_object($data)) {
 155                      $keys = array_keys($data);
 156                      $testkey = array_pop($keys);
 157                      if (is_array($data[$testkey]) || is_object($data[$testkey])) {
 158                          foreach ($data as $d) {
 159                              $str .= implode(',', (array)$d);
 160                          }
 161                      } else {
 162                          $str .= implode(',', (array)$data);
 163                      }
 164                  } else {
 165                      $str .= $data;
 166                  }
 167              }
 168          }
 169          return sha1($str . ',' . $this->exporter->get('formatclass'));
 170      }
 171  
 172      /**
 173       * Prepare the package for export
 174       *
 175       * @return stored_file object
 176       */
 177      public function prepare_package() {
 178          global $DB;
 179          $leapwriter = null;
 180          $content = '';
 181          $filename = '';
 182          $uid = $this->exporter->get('user')->id;
 183          $users = array(); //cache
 184          $onlymine = $this->get_export_config('mineonly');
 185          if ($this->exporter->get('formatclass') == PORTFOLIO_FORMAT_LEAP2A) {
 186              $leapwriter = $this->exporter->get('format')->leap2a_writer();
 187              $ids = array();
 188          }
 189  
 190          if ($this->exporter->get('format') instanceof portfolio_format_file && $this->singlefile) {
 191              return $this->get('exporter')->copy_existing_file($this->singlefile);
 192          }
 193          foreach ($this->records  as $key => $record) {
 194              if ($onlymine && $record->userid != $uid) {
 195                  unset($this->records[$key]); // sha1
 196                  continue;
 197              }
 198              list($tmpcontent, $files)  = $this->exportentry($record);
 199              $content .= $tmpcontent;
 200              if ($leapwriter) {
 201                  $entry = new portfolio_format_leap2a_entry('dataentry' . $record->id, $this->data->name, 'resource', $tmpcontent);
 202                  $entry->published = $record->timecreated;
 203                  $entry->updated = $record->timemodified;
 204                  if ($record->userid != $uid) {
 205                      if (!array_key_exists($record->userid, $users)) {
 206                          $users[$record->userid] = $DB->get_record('user', array('id' => $record->userid), 'id,firstname,lastname');
 207                      }
 208                      $entry->author = $users[$record->userid];
 209                  }
 210                  $ids[] = $entry->id;
 211                  $leapwriter->link_files($entry, $files, 'dataentry' . $record->id . 'file');
 212                  $leapwriter->add_entry($entry);
 213              }
 214          }
 215          if ($leapwriter) {
 216              if (count($this->records) > 1) { // make a selection element to tie them all together
 217                  $selection = new portfolio_format_leap2a_entry('datadb' . $this->data->id,
 218                      get_string('entries', 'data') . ': ' . $this->data->name, 'selection');
 219                  $leapwriter->add_entry($selection);
 220                  $leapwriter->make_selection($selection, $ids, 'Grouping');
 221              }
 222              $filename = $this->exporter->get('format')->manifest_name();
 223              $content = $leapwriter->to_xml();
 224          } else {
 225              if (count($this->records) == 1) {
 226                  $filename = clean_filename($this->cm->name . '-entry.html');
 227              } else {
 228                  $filename = clean_filename($this->cm->name . '-full.html');
 229              }
 230          }
 231          return $this->exporter->write_new_file(
 232              $content,
 233              $filename,
 234              ($this->exporter->get('format') instanceof PORTFOLIO_FORMAT_RICH) // if we have associate files, this is a 'manifest'
 235          );
 236      }
 237  
 238      /**
 239       * Verify the user can still export this entry
 240       *
 241       * @return bool
 242       */
 243      public function check_permissions() {
 244          if ($this->recordid) {
 245              if (data_isowner($this->recordid)) {
 246                  return has_capability('mod/data:exportownentry', context_module::instance($this->cm->id));
 247              }
 248              return has_capability('mod/data:exportentry', context_module::instance($this->cm->id));
 249          }
 250          if ($this->has_export_config() && !$this->get_export_config('mineonly')) {
 251              return has_capability('mod/data:exportallentries', context_module::instance($this->cm->id));
 252          }
 253          return has_capability('mod/data:exportownentry', context_module::instance($this->cm->id));
 254      }
 255  
 256      /**
 257       *  @return string
 258       */
 259      public static function display_name() {
 260          return get_string('modulename', 'data');
 261      }
 262  
 263      /**
 264       * @global object
 265       * @return bool|void
 266       */
 267      public function __wakeup() {
 268          global $CFG;
 269          if (empty($CFG)) {
 270              return true; // too early yet
 271          }
 272          foreach ($this->fieldtypes as $key => $field) {
 273              require_once($CFG->dirroot . '/mod/data/field/' . $field .'/field.class.php');
 274              $this->fields[$key] = unserialize(serialize($this->fields[$key]));
 275          }
 276      }
 277  
 278      /**
 279       * Prepare a single entry for export, replacing all the content etc
 280       *
 281       * @param stdclass $record the entry to export
 282       *
 283       * @return array with key 0 = the html content, key 1 = array of attachments
 284       */
 285      private function exportentry($record) {
 286      // Replacing tags
 287          $patterns = array();
 288          $replacement = array();
 289  
 290          $files = array();
 291      // Then we generate strings to replace for normal tags
 292          $format = $this->get('exporter')->get('format');
 293          foreach ($this->fields as $field) {
 294              $patterns[]='[['.$field->field->name.']]';
 295              if (is_callable(array($field, 'get_file'))) {
 296                  if (!$file = $field->get_file($record->id)) {
 297                      $replacement[] = '';
 298                      continue; // probably left empty
 299                  }
 300                  $replacement[] = $format->file_output($file);
 301                  $this->get('exporter')->copy_existing_file($file);
 302                  $files[] = $file;
 303              } else {
 304                  $replacement[] = $field->display_browse_field($record->id, 'singletemplate');
 305              }
 306          }
 307  
 308      // Replacing special tags (##Edit##, ##Delete##, ##More##)
 309          $patterns[]='##edit##';
 310          $patterns[]='##delete##';
 311          $patterns[]='##export##';
 312          $patterns[]='##more##';
 313          $patterns[]='##moreurl##';
 314          $patterns[]='##user##';
 315          $patterns[]='##approve##';
 316          $patterns[]='##disapprove##';
 317          $patterns[]='##comments##';
 318          $patterns[] = '##timeadded##';
 319          $patterns[] = '##timemodified##';
 320          $replacement[] = '';
 321          $replacement[] = '';
 322          $replacement[] = '';
 323          $replacement[] = '';
 324          $replacement[] = '';
 325          $replacement[] = '';
 326          $replacement[] = '';
 327          $replacement[] = '';
 328          $replacement[] = '';
 329          $replacement[] = userdate($record->timecreated);
 330          $replacement[] = userdate($record->timemodified);
 331  
 332          // actual replacement of the tags
 333          return array(str_ireplace($patterns, $replacement, $this->data->singletemplate), $files);
 334      }
 335  
 336      /**
 337       * Given the fields being exported, and the single record,
 338       * work out which export format(s) we can use
 339       *
 340       * @param array $fields array of field objects
 341       * @param object $record The data record object
 342       *
 343       * @uses PORTFOLIO_FORMAT_PLAINHTML
 344       * @uses PORTFOLIO_FORMAT_RICHHTML
 345       *
 346       * @return array of PORTFOLIO_XX constants
 347       */
 348      public static function formats($fields, $record) {
 349          $formats = array(PORTFOLIO_FORMAT_PLAINHTML);
 350          $includedfiles = array();
 351          foreach ($fields as $singlefield) {
 352              if (is_callable(array($singlefield, 'get_file'))) {
 353                  if ($file = $singlefield->get_file($record->id)) {
 354                      $includedfiles[] = $file;
 355                  }
 356              }
 357          }
 358          if (count($includedfiles) == 1 && count($fields) == 1) {
 359              $formats = array(portfolio_format_from_mimetype($includedfiles[0]->get_mimetype()));
 360          } else if (count($includedfiles) > 0) {
 361              $formats = array(PORTFOLIO_FORMAT_RICHHTML);
 362          }
 363          return array($formats, $includedfiles);
 364      }
 365  
 366      public static function has_files($data) {
 367          global $DB;
 368          $fieldrecords = $DB->get_records('data_fields', array('dataid' => $data->id), 'id');
 369          // populate objets for this databases fields
 370          foreach ($fieldrecords as $fieldrecord) {
 371              $field = data_get_field($fieldrecord, $data);
 372              if (is_callable(array($field, 'get_file'))) {
 373                  return true;
 374              }
 375          }
 376          return false;
 377      }
 378  
 379      /**
 380       * base supported formats before we know anything about the export
 381       */
 382      public static function base_supported_formats() {
 383          return array(PORTFOLIO_FORMAT_RICHHTML, PORTFOLIO_FORMAT_PLAINHTML, PORTFOLIO_FORMAT_LEAP2A);
 384      }
 385  
 386      public function has_export_config() {
 387          // if we're exporting more than just a single entry,
 388          // and we have the capability to export all entries,
 389          // then ask whether we want just our own, or all of them
 390          return (empty($this->recordid) // multi-entry export
 391              && $this->minecount > 0    // some of them are mine
 392              && $this->minecount != count($this->records) // not all of them are mine
 393              && has_capability('mod/data:exportallentries', context_module::instance($this->cm->id))); // they actually have a choice in the matter
 394      }
 395  
 396      public function export_config_form(&$mform, $instance) {
 397          if (!$this->has_export_config()) {
 398              return;
 399          }
 400          $mform->addElement('selectyesno', 'mineonly', get_string('exportownentries', 'data', (object)array('mine' => $this->minecount, 'all' => count($this->records))));
 401          $mform->setDefault('mineonly', 1);
 402      }
 403  
 404      public function get_allowed_export_config() {
 405          return array('mineonly');
 406      }
 407  }
 408  
 409  
 410  /**
 411   * Class representing the virtual node with all itemids in the file browser
 412   *
 413   * @category  files
 414   * @copyright 2012 David Mudrak <david@moodle.com>
 415   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 416   */
 417  class data_file_info_container extends file_info {
 418      /** @var file_browser */
 419      protected $browser;
 420      /** @var stdClass */
 421      protected $course;
 422      /** @var stdClass */
 423      protected $cm;
 424      /** @var string */
 425      protected $component;
 426      /** @var stdClass */
 427      protected $context;
 428      /** @var array */
 429      protected $areas;
 430      /** @var string */
 431      protected $filearea;
 432  
 433      /**
 434       * Constructor (in case you did not realize it ;-)
 435       *
 436       * @param file_browser $browser
 437       * @param stdClass $course
 438       * @param stdClass $cm
 439       * @param stdClass $context
 440       * @param array $areas
 441       * @param string $filearea
 442       */
 443      public function __construct($browser, $course, $cm, $context, $areas, $filearea) {
 444          parent::__construct($browser, $context);
 445          $this->browser = $browser;
 446          $this->course = $course;
 447          $this->cm = $cm;
 448          $this->component = 'mod_data';
 449          $this->context = $context;
 450          $this->areas = $areas;
 451          $this->filearea = $filearea;
 452      }
 453  
 454      /**
 455       * @return array with keys contextid, filearea, itemid, filepath and filename
 456       */
 457      public function get_params() {
 458          return array(
 459              'contextid' => $this->context->id,
 460              'component' => $this->component,
 461              'filearea' => $this->filearea,
 462              'itemid' => null,
 463              'filepath' => null,
 464              'filename' => null,
 465          );
 466      }
 467  
 468      /**
 469       * Can new files or directories be added via the file browser
 470       *
 471       * @return bool
 472       */
 473      public function is_writable() {
 474          return false;
 475      }
 476  
 477      /**
 478       * Should this node be considered as a folder in the file browser
 479       *
 480       * @return bool
 481       */
 482      public function is_directory() {
 483          return true;
 484      }
 485  
 486      /**
 487       * Returns localised visible name of this node
 488       *
 489       * @return string
 490       */
 491      public function get_visible_name() {
 492          return $this->areas[$this->filearea];
 493      }
 494  
 495      /**
 496       * Returns list of children nodes
 497       *
 498       * @return array of file_info instances
 499       */
 500      public function get_children() {
 501          return $this->get_filtered_children('*', false, true);
 502      }
 503  
 504      /**
 505       * Help function to return files matching extensions or their count
 506       *
 507       * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
 508       * @param bool|int $countonly if false returns the children, if an int returns just the
 509       *    count of children but stops counting when $countonly number of children is reached
 510       * @param bool $returnemptyfolders if true returns items that don't have matching files inside
 511       * @return array|int array of file_info instances or the count
 512       */
 513      private function get_filtered_children($extensions = '*', $countonly = false, $returnemptyfolders = false) {
 514          global $DB;
 515          $params = array('contextid' => $this->context->id,
 516              'component' => $this->component,
 517              'filearea' => $this->filearea);
 518          $sql = 'SELECT DISTINCT itemid
 519                      FROM {files}
 520                      WHERE contextid = :contextid
 521                      AND component = :component
 522                      AND filearea = :filearea';
 523          if (!$returnemptyfolders) {
 524              $sql .= ' AND filename <> :emptyfilename';
 525              $params['emptyfilename'] = '.';
 526          }
 527          list($sql2, $params2) = $this->build_search_files_sql($extensions);
 528          $sql .= ' '.$sql2;
 529          $params = array_merge($params, $params2);
 530          if ($countonly === false) {
 531              $sql .= ' ORDER BY itemid DESC';
 532          }
 533  
 534          $rs = $DB->get_recordset_sql($sql, $params);
 535          $children = array();
 536          foreach ($rs as $record) {
 537              if ($child = $this->browser->get_file_info($this->context, 'mod_data', $this->filearea, $record->itemid)) {
 538                  $children[] = $child;
 539              }
 540              if ($countonly !== false && count($children) >= $countonly) {
 541                  break;
 542              }
 543          }
 544          $rs->close();
 545          if ($countonly !== false) {
 546              return count($children);
 547          }
 548          return $children;
 549      }
 550  
 551      /**
 552       * Returns list of children which are either files matching the specified extensions
 553       * or folders that contain at least one such file.
 554       *
 555       * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
 556       * @return array of file_info instances
 557       */
 558      public function get_non_empty_children($extensions = '*') {
 559          return $this->get_filtered_children($extensions, false);
 560      }
 561  
 562      /**
 563       * Returns the number of children which are either files matching the specified extensions
 564       * or folders containing at least one such file.
 565       *
 566       * @param string|array $extensions, for example '*' or array('.gif','.jpg')
 567       * @param int $limit stop counting after at least $limit non-empty children are found
 568       * @return int
 569       */
 570      public function count_non_empty_children($extensions = '*', $limit = 1) {
 571          return $this->get_filtered_children($extensions, $limit);
 572      }
 573  
 574      /**
 575       * Returns parent file_info instance
 576       *
 577       * @return file_info or null for root
 578       */
 579      public function get_parent() {
 580          return $this->browser->get_file_info($this->context);
 581      }
 582  }
 583  
 584  /**
 585   * This creates new calendar events given as timeavailablefrom and timeclose by $data.
 586   *
 587   * @param stdClass $data
 588   * @return void
 589   */
 590  function data_set_events($data) {
 591      global $DB, $CFG;
 592  
 593      require_once($CFG->dirroot.'/calendar/lib.php');
 594  
 595      // Get CMID if not sent as part of $data.
 596      if (!isset($data->coursemodule)) {
 597          $cm = get_coursemodule_from_instance('data', $data->id, $data->course);
 598          $data->coursemodule = $cm->id;
 599      }
 600      // Data start calendar events.
 601      $event = new stdClass();
 602      if ($event->id = $DB->get_field('event', 'id',
 603              array('modulename' => 'data', 'instance' => $data->id, 'eventtype' => 'open'))) {
 604          if ($data->timeavailablefrom > 0) {
 605              // Calendar event exists so update it.
 606              $event->name         = get_string('calendarstart', 'data', $data->name);
 607              $event->description  = format_module_intro('data', $data, $data->coursemodule);
 608              $event->timestart    = $data->timeavailablefrom;
 609              $event->visible      = instance_is_visible('data', $data);
 610              $event->timeduration = 0;
 611              $calendarevent = calendar_event::load($event->id);
 612              $calendarevent->update($event);
 613          } else {
 614              // Calendar event is on longer needed.
 615              $calendarevent = calendar_event::load($event->id);
 616              $calendarevent->delete();
 617          }
 618      } else {
 619          // Event doesn't exist so create one.
 620          if (isset($data->timeavailablefrom) && $data->timeavailablefrom > 0) {
 621              $event->name         = get_string('calendarstart', 'data', $data->name);
 622              $event->description  = format_module_intro('data', $data, $data->coursemodule);
 623              $event->courseid     = $data->course;
 624              $event->groupid      = 0;
 625              $event->userid       = 0;
 626              $event->modulename   = 'data';
 627              $event->instance     = $data->id;
 628              $event->eventtype    = 'open';
 629              $event->timestart    = $data->timeavailablefrom;
 630              $event->visible      = instance_is_visible('data', $data);
 631              $event->timeduration = 0;
 632              calendar_event::create($event);
 633          }
 634      }
 635  
 636      // Data end calendar events.
 637      $event = new stdClass();
 638      if ($event->id = $DB->get_field('event', 'id',
 639              array('modulename' => 'data', 'instance' => $data->id, 'eventtype' => 'close'))) {
 640          if ($data->timeavailableto > 0) {
 641              // Calendar event exists so update it.
 642              $event->name         = get_string('calendarend', 'data', $data->name);
 643              $event->description  = format_module_intro('data', $data, $data->coursemodule);
 644              $event->timestart    = $data->timeavailableto;
 645              $event->visible      = instance_is_visible('data', $data);
 646              $event->timeduration = 0;
 647              $calendarevent = calendar_event::load($event->id);
 648              $calendarevent->update($event);
 649          } else {
 650              // Calendar event is on longer needed.
 651              $calendarevent = calendar_event::load($event->id);
 652              $calendarevent->delete();
 653          }
 654      } else {
 655          // Event doesn't exist so create one.
 656          if (isset($data->timeavailableto) && $data->timeavailableto > 0) {
 657              $event = new stdClass();
 658              $event->name         = get_string('calendarend', 'data', $data->name);
 659              $event->description  = format_module_intro('data', $data, $data->coursemodule);
 660              $event->courseid     = $data->course;
 661              $event->groupid      = 0;
 662              $event->userid       = 0;
 663              $event->modulename   = 'data';
 664              $event->instance     = $data->id;
 665              $event->eventtype    = 'close';
 666              $event->timestart    = $data->timeavailableto;
 667              $event->visible      = instance_is_visible('data', $data);
 668              $event->timeduration = 0;
 669              calendar_event::create($event);
 670          }
 671      }
 672  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1