MDL-31675 File browsing support for the Glossary module
[moodle.git] / mod / data / locallib.php
CommitLineData
24ba58ee
PL
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
c3e1b5bd 24require_once($CFG->dirroot . '/mod/data/lib.php');
ee314f40 25require_once($CFG->libdir . '/portfolio/caller.php');
c3e1b5bd 26
24ba58ee 27/**
c3e1b5bd 28 * The class to handle entry exports of a database module
24ba58ee 29 */
24ba58ee
PL
30class data_portfolio_caller extends portfolio_module_caller_base {
31
c3e1b5bd 32 /** @var int the single record to export */
24ba58ee 33 protected $recordid;
24ba58ee 34
c3e1b5bd 35 /** @var object the record from the data table */
24ba58ee 36 private $data;
c3e1b5bd
PL
37
38 /**#@+ @var array the fields used and their fieldtypes */
24ba58ee
PL
39 private $fields;
40 private $fieldtypes;
c3e1b5bd
PL
41
42 /** @var object the records to export */
43 private $records;
44
45 /** @var int how many records are 'mine' */
46 private $minecount;
47
24ba58ee 48 /**
c3e1b5bd
PL
49 * the required callback arguments for a single-record export
50 *
24ba58ee
PL
51 * @return array
52 */
53 public static function expected_callbackargs() {
54 return array(
c3e1b5bd
PL
55 'id' => true,
56 'recordid' => false,
24ba58ee
PL
57 );
58 }
c3e1b5bd 59
24ba58ee 60 /**
c3e1b5bd 61 * @param array $callbackargs the arguments passed through
24ba58ee
PL
62 */
63 public function __construct($callbackargs) {
64 parent::__construct($callbackargs);
c3e1b5bd 65 // set up the list of fields to export
24ba58ee
PL
66 $this->selectedfields = array();
67 foreach ($callbackargs as $key => $value) {
68 if (strpos($key, 'field_') === 0) {
69 $this->selectedfields[] = substr($key, 6);
70 }
71 }
72 }
73
74 /**
c3e1b5bd
PL
75 * load up the data needed for the export
76 *
77 * @global object $DB
24ba58ee
PL
78 */
79 public function load_data() {
c3e1b5bd 80 global $DB, $USER;
24ba58ee
PL
81 if (!$this->cm = get_coursemodule_from_id('data', $this->id)) {
82 throw new portfolio_caller_exception('invalidid', 'data');
83 }
c3e1b5bd
PL
84 if (!$this->data = $DB->get_record('data', array('id' => $this->cm->instance))) {
85 throw new portfolio_caller_exception('invalidid', 'data');
86 }
87 $fieldrecords = $DB->get_records('data_fields', array('dataid' => $this->cm->instance), 'id');
24ba58ee
PL
88 // populate objets for this databases fields
89 $this->fields = array();
90 foreach ($fieldrecords as $fieldrecord) {
91 $tmp = data_get_field($fieldrecord, $this->data);
92 $this->fields[] = $tmp;
93 $this->fieldtypes[] = $tmp->type;
94 }
95
c3e1b5bd 96 $this->records = array();
24ba58ee 97 if ($this->recordid) {
c3e1b5bd
PL
98 $tmp = $DB->get_record('data_records', array('id' => $this->recordid));
99 $tmp->content = $DB->get_records('data_content', array('recordid' => $this->recordid));
100 $this->records[] = $tmp;
24ba58ee 101 } else {
c3e1b5bd
PL
102 $where = array('dataid' => $this->data->id);
103 if (!has_capability('mod/data:exportallentries', get_context_instance(CONTEXT_MODULE, $this->cm->id))) {
104 $where['userid'] = $USER->id; // get them all in case, we'll unset ones that aren't ours later if necessary
105 }
106 $tmp = $DB->get_records('data_records', $where);
107 foreach ($tmp as $t) {
108 $t->content = $DB->get_records('data_content', array('recordid' => $t->id));
109 $this->records[] = $t;
110 }
111 $this->minecount = $DB->count_records('data_records', array('dataid' => $this->data->id, 'userid' => $USER->id));
24ba58ee 112 }
24ba58ee 113
c3e1b5bd
PL
114 if ($this->recordid) {
115 list($formats, $files) = self::formats($this->fields, $this->records[0]);
116 $this->set_file_and_format_data($files);
117 }
24ba58ee
PL
118 }
119
120 /**
c3e1b5bd
PL
121 * How long we think the export will take
122 * Single entry is probably not too long.
123 * But we check for filesizes
124 * Else base it on the number of records
125 *
126 * @return one of PORTFOLIO_TIME_XX constants
24ba58ee
PL
127 */
128 public function expected_time() {
c3e1b5bd
PL
129 if ($this->recordid) {
130 return $this->expected_time_file();
131 } else {
132 return portfolio_expected_time_db(count($this->records));
24ba58ee 133 }
24ba58ee
PL
134 }
135
136 /**
c3e1b5bd
PL
137 * Calculate the shal1 of this export
138 * Dependent on the export format.
24ba58ee
PL
139 * @return string
140 */
141 public function get_sha1() {
c3e1b5bd
PL
142 // in the case that we're exporting a subclass of 'file' and we have a singlefile,
143 // then we're not exporting any metadata, just the file by itself by mimetype.
144 if ($this->exporter->get('format') instanceof portfolio_format_file && $this->singlefile) {
145 return $this->get_sha1_file();
24ba58ee 146 }
c3e1b5bd 147 // otherwise we're exporting some sort of multipart content so use the data
24ba58ee 148 $str = '';
c3e1b5bd
PL
149 foreach ($this->records as $record) {
150 foreach ($record as $data) {
151 if (is_array($data) || is_object($data)) {
152 $testkey = array_pop(array_keys($data));
153 if (is_array($data[$testkey]) || is_object($data[$testkey])) {
154 foreach ($data as $d) {
155 $str .= implode(',', (array)$d);
156 }
157 } else {
158 $str .= implode(',', (array)$data);
24ba58ee
PL
159 }
160 } else {
c3e1b5bd 161 $str .= $data;
24ba58ee 162 }
24ba58ee
PL
163 }
164 }
c3e1b5bd 165 return sha1($str . ',' . $this->exporter->get('formatclass'));
24ba58ee 166 }
c3e1b5bd 167
24ba58ee 168 /**
c3e1b5bd
PL
169 * Prepare the package for export
170 *
171 * @return stored_file object
24ba58ee
PL
172 */
173 public function prepare_package() {
174 global $DB;
c3e1b5bd 175 $leapwriter = null;
24ba58ee
PL
176 $content = '';
177 $filename = '';
c3e1b5bd
PL
178 $uid = $this->exporter->get('user')->id;
179 $users = array(); //cache
180 $onlymine = $this->get_export_config('mineonly');
181 if ($this->exporter->get('formatclass') == PORTFOLIO_FORMAT_LEAP2A) {
182 $leapwriter = $this->exporter->get('format')->leap2a_writer();
183 $ids = array();
184 }
185
186 if ($this->exporter->get('format') instanceof portfolio_format_file && $this->singlefile) {
187 return $this->get('exporter')->copy_existing_file($this->singlefile);
188 }
189 foreach ($this->records as $key => $record) {
190 if ($onlymine && $record->userid != $uid) {
191 unset($this->records[$key]); // sha1
192 continue;
193 }
194 list($tmpcontent, $files) = $this->exportentry($record);
195 $content .= $tmpcontent;
196 if ($leapwriter) {
197 $entry = new portfolio_format_leap2a_entry('dataentry' . $record->id, $this->data->name, 'resource', $tmpcontent);
198 $entry->published = $record->timecreated;
199 $entry->updated = $record->timemodified;
200 if ($record->userid != $uid) {
201 if (!array_key_exists($record->userid, $users)) {
202 $users[$record->userid] = $DB->get_record('user', array('id' => $record->userid), 'id,firstname,lastname');
203 }
204 $entry->author = $users[$record->userid];
205 }
206 $ids[] = $entry->id;
ebb7e782 207 $leapwriter->link_files($entry, $files, 'dataentry' . $record->id . 'file');
c3e1b5bd
PL
208 $leapwriter->add_entry($entry);
209 }
210 }
211 if ($leapwriter) {
212 if (count($this->records) > 1) { // make a selection element to tie them all together
213 $selection = new portfolio_format_leap2a_entry('datadb' . $this->data->id,
214 get_string('entries', 'data') . ': ' . $this->data->name, 'selection');
215 $leapwriter->add_entry($selection);
216 $leapwriter->make_selection($selection, $ids, 'Grouping');
217 }
218 $filename = $this->exporter->get('format')->manifest_name();
219 $content = $leapwriter->to_xml();
220 } else {
221 if (count($this->records) == 1) {
24ba58ee 222 $filename = clean_filename($this->cm->name . '-entry.html');
c3e1b5bd
PL
223 } else {
224 $filename = clean_filename($this->cm->name . '-full.html');
225 }
24ba58ee
PL
226 }
227 return $this->exporter->write_new_file(
228 $content,
229 $filename,
230 ($this->exporter->get('format') instanceof PORTFOLIO_FORMAT_RICH) // if we have associate files, this is a 'manifest'
231 );
232 }
233
234 /**
c3e1b5bd
PL
235 * Verify the user can still export this entry
236 *
24ba58ee
PL
237 * @return bool
238 */
239 public function check_permissions() {
c3e1b5bd
PL
240 if ($this->recordid) {
241 if (data_isowner($this->recordid)) {
242 return has_capability('mod/data:exportownentry', get_context_instance(CONTEXT_MODULE, $this->cm->id));
243 }
244 return has_capability('mod/data:exportentry', get_context_instance(CONTEXT_MODULE, $this->cm->id));
245 }
246 if ($this->has_export_config() && !$this->get_export_config('mineonly')) {
247 return has_capability('mod/data:exportallentries', get_context_instance(CONTEXT_MODULE, $this->cm->id));
248 }
249 return has_capability('mod/data:exportownentry', get_context_instance(CONTEXT_MODULE, $this->cm->id));
24ba58ee
PL
250 }
251
252 /**
253 * @return string
254 */
255 public static function display_name() {
256 return get_string('modulename', 'data');
257 }
258
259 /**
260 * @global object
261 * @return bool|void
262 */
263 public function __wakeup() {
264 global $CFG;
265 if (empty($CFG)) {
266 return true; // too early yet
267 }
268 foreach ($this->fieldtypes as $key => $field) {
269 require_once($CFG->dirroot . '/mod/data/field/' . $field .'/field.class.php');
270 $this->fields[$key] = unserialize(serialize($this->fields[$key]));
271 }
272 }
273
274 /**
c3e1b5bd
PL
275 * Prepare a single entry for export, replacing all the content etc
276 *
277 * @param stdclass $record the entry to export
278 *
279 * @return array with key 0 = the html content, key 1 = array of attachments
24ba58ee 280 */
c3e1b5bd 281 private function exportentry($record) {
24ba58ee
PL
282 // Replacing tags
283 $patterns = array();
284 $replacement = array();
285 $context = get_context_instance(CONTEXT_MODULE, $this->cm->id);
286
c3e1b5bd 287 $files = array();
24ba58ee
PL
288 // Then we generate strings to replace for normal tags
289 $format = $this->get('exporter')->get('format');
290 foreach ($this->fields as $field) {
291 $patterns[]='[['.$field->field->name.']]';
292 if (is_callable(array($field, 'get_file'))) {
c3e1b5bd 293 if (!$file = $field->get_file($record->id)) {
24ba58ee
PL
294 $replacement[] = '';
295 continue; // probably left empty
296 }
297 $replacement[] = $format->file_output($file);
298 $this->get('exporter')->copy_existing_file($file);
c3e1b5bd 299 $files[] = $file;
24ba58ee 300 } else {
c3e1b5bd 301 $replacement[] = $field->display_browse_field($record->id, 'singletemplate');
24ba58ee
PL
302 }
303 }
304
305 // Replacing special tags (##Edit##, ##Delete##, ##More##)
306 $patterns[]='##edit##';
307 $patterns[]='##delete##';
308 $patterns[]='##export##';
309 $patterns[]='##more##';
310 $patterns[]='##moreurl##';
311 $patterns[]='##user##';
312 $patterns[]='##approve##';
313 $patterns[]='##comments##';
314 $patterns[] = '##timeadded##';
315 $patterns[] = '##timemodified##';
316 $replacement[] = '';
317 $replacement[] = '';
318 $replacement[] = '';
319 $replacement[] = '';
320 $replacement[] = '';
321 $replacement[] = '';
322 $replacement[] = '';
323 $replacement[] = '';
c3e1b5bd
PL
324 $replacement[] = userdate($record->timecreated);
325 $replacement[] = userdate($record->timemodified);
24ba58ee
PL
326
327 // actual replacement of the tags
c3e1b5bd 328 return array(str_ireplace($patterns, $replacement, $this->data->singletemplate), $files);
24ba58ee
PL
329 }
330
331 /**
c3e1b5bd
PL
332 * Given the fields being exported, and the single record,
333 * work out which export format(s) we can use
334 *
335 * @param array $fields array of field objects
336 * @param object $record The data record object
337 *
24ba58ee
PL
338 * @uses PORTFOLIO_FORMAT_PLAINHTML
339 * @uses PORTFOLIO_FORMAT_RICHHTML
c3e1b5bd
PL
340 *
341 * @return array of PORTFOLIO_XX constants
24ba58ee
PL
342 */
343 public static function formats($fields, $record) {
344 $formats = array(PORTFOLIO_FORMAT_PLAINHTML);
345 $includedfiles = array();
346 foreach ($fields as $singlefield) {
347 if (is_callable(array($singlefield, 'get_file'))) {
348 $includedfiles[] = $singlefield->get_file($record->id);
349 }
350 }
351 if (count($includedfiles) == 1 && count($fields) == 1) {
c3e1b5bd 352 $formats = array(portfolio_format_from_mimetype($includedfiles[0]->get_mimetype()));
24ba58ee
PL
353 } else if (count($includedfiles) > 0) {
354 $formats = array(PORTFOLIO_FORMAT_RICHHTML);
355 }
356 return array($formats, $includedfiles);
357 }
358
c3e1b5bd
PL
359 public static function has_files($data) {
360 global $DB;
361 $fieldrecords = $DB->get_records('data_fields', array('dataid' => $data->id), 'id');
362 // populate objets for this databases fields
363 foreach ($fieldrecords as $fieldrecord) {
364 $field = data_get_field($fieldrecord, $data);
365 if (is_callable(array($field, 'get_file'))) {
366 return true;
367 }
368 }
369 return false;
370 }
371
372 /**
373 * base supported formats before we know anything about the export
374 */
24ba58ee 375 public static function base_supported_formats() {
c3e1b5bd
PL
376 return array(PORTFOLIO_FORMAT_RICHHTML, PORTFOLIO_FORMAT_PLAINHTML, PORTFOLIO_FORMAT_LEAP2A);
377 }
378
379 public function has_export_config() {
380 // if we're exporting more than just a single entry,
381 // and we have the capability to export all entries,
382 // then ask whether we want just our own, or all of them
383 return (empty($this->recordid) // multi-entry export
384 && $this->minecount > 0 // some of them are mine
385 && $this->minecount != count($this->records) // not all of them are mine
386 && has_capability('mod/data:exportallentries', get_context_instance(CONTEXT_MODULE, $this->cm->id))); // they actually have a choice in the matter
387 }
388
389 public function export_config_form(&$mform, $instance) {
390 if (!$this->has_export_config()) {
391 return;
392 }
393 $mform->addElement('selectyesno', 'mineonly', get_string('exportownentries', 'data', (object)array('mine' => $this->minecount, 'all' => count($this->records))));
394 $mform->setDefault('mineonly', 1);
395 }
396
397 public function get_allowed_export_config() {
398 return array('mineonly');
24ba58ee
PL
399 }
400}