e790f13c42e8d77f335119cef97a060ab01d3486
[moodle.git] / mod / forum / locallib.php
1 <?php
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/>.
18 /**
19  * Library of functions for forum outside of the core api
20  */
22 require_once($CFG->dirroot . '/mod/forum/lib.php');
23 require_once($CFG->libdir . '/portfolio/caller.php');
25 /**
26  * @package   mod-forum
27  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
28  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
29  */
30 class forum_portfolio_caller extends portfolio_module_caller_base {
32     protected $postid;
33     protected $discussionid;
34     protected $attachment;
36     private $post;
37     private $forum;
38     private $discussion;
39     private $posts;
40     private $keyedfiles; // just using multifiles isn't enough if we're exporting a full thread
42     /**
43      * @return array
44      */
45     public static function expected_callbackargs() {
46         return array(
47             'postid'       => false,
48             'discussionid' => false,
49             'attachment'   => false,
50         );
51     }
52     /**
53      * @param array $callbackargs
54      */
55     function __construct($callbackargs) {
56         parent::__construct($callbackargs);
57         if (!$this->postid && !$this->discussionid) {
58             throw new portfolio_caller_exception('mustprovidediscussionorpost', 'forum');
59         }
60     }
61     /**
62      * @global object
63      */
64     public function load_data() {
65         global $DB;
67         if ($this->postid) {
68             if (!$this->post = $DB->get_record('forum_posts', array('id' => $this->postid))) {
69                 throw new portfolio_caller_exception('invalidpostid', 'forum');
70             }
71         }
73         $dparams = array();
74         if ($this->discussionid) {
75             $dbparams = array('id' => $this->discussionid);
76         } else if ($this->post) {
77             $dbparams = array('id' => $this->post->discussion);
78         } else {
79             throw new portfolio_caller_exception('mustprovidediscussionorpost', 'forum');
80         }
82         if (!$this->discussion = $DB->get_record('forum_discussions', $dbparams)) {
83             throw new portfolio_caller_exception('invaliddiscussionid', 'forum');
84         }
86         if (!$this->forum = $DB->get_record('forum', array('id' => $this->discussion->forum))) {
87             throw new portfolio_caller_exception('invalidforumid', 'forum');
88         }
90         if (!$this->cm = get_coursemodule_from_instance('forum', $this->forum->id)) {
91             throw new portfolio_caller_exception('invalidcoursemodule');
92         }
94         $this->modcontext = get_context_instance(CONTEXT_MODULE, $this->cm->id);
95         $fs = get_file_storage();
96         if ($this->post) {
97             if ($this->attachment) {
98                 $this->set_file_and_format_data($this->attachment);
99             } else {
100                 $attach = $fs->get_area_files($this->modcontext->id, 'mod_forum', 'attachment', $this->post->id, 'timemodified', false);
101                 $embed  = $fs->get_area_files($this->modcontext->id, 'mod_forum', 'post', $this->post->id, 'timemodified', false);
102                 $files = array_merge($attach, $embed);
103                 $this->set_file_and_format_data($files);
104             }
105             if (!empty($this->multifiles)) {
106                 $this->keyedfiles[$this->post->id] = $this->multifiles;
107             } else if (!empty($this->singlefile)) {
108                 $this->keyedfiles[$this->post->id] = array($this->singlefile);
109             }
110         } else { // whole thread
111             $fs = get_file_storage();
112             $this->posts = forum_get_all_discussion_posts($this->discussion->id, 'p.created ASC');
113             $this->multifiles = array();
114             foreach ($this->posts as $post) {
115                 $attach = $fs->get_area_files($this->modcontext->id, 'mod_forum', 'attachment', $post->id, 'timemodified', false);
116                 $embed  = $fs->get_area_files($this->modcontext->id, 'mod_forum', 'post', $post->id, 'timemodified', false);
117                 $files = array_merge($attach, $embed);
118                 if ($files) {
119                     $this->keyedfiles[$post->id] = $files;
120                 } else {
121                     continue;
122                 }
123                 $this->multifiles = array_merge($this->multifiles, array_values($this->keyedfiles[$post->id]));
124             }
125         }
126         if (empty($this->multifiles) && !empty($this->singlefile)) {
127             $this->multifiles = array($this->singlefile); // copy_files workaround
128         }
129         // depending on whether there are files or not, we might have to change richhtml/plainhtml
130         if (empty($this->attachment)) {
131             if (!empty($this->multifiles)) {
132                 $this->add_format(PORTFOLIO_FORMAT_RICHHTML);
133             } else {
134                 $this->add_format(PORTFOLIO_FORMAT_PLAINHTML);
135             }
136         }
137     }
139     /**
140      * @global object
141      * @return string
142      */
143     function get_return_url() {
144         global $CFG;
145         return $CFG->wwwroot . '/mod/forum/discuss.php?d=' . $this->discussion->id;
146     }
147     /**
148      * @global object
149      * @return array
150      */
151     function get_navigation() {
152         global $CFG;
154         $navlinks = array();
155         $navlinks[] = array(
156             'name' => format_string($this->discussion->name),
157             'link' => $CFG->wwwroot . '/mod/forum/discuss.php?d=' . $this->discussion->id,
158             'type' => 'title'
159         );
160         return array($navlinks, $this->cm);
161     }
162     /**
163      * either a whole discussion
164      * a single post, with or without attachment
165      * or just an attachment with no post
166      *
167      * @global object
168      * @global object
169      * @uses PORTFOLIO_FORMAT_RICH
170      * @return mixed
171      */
172     function prepare_package() {
173         global $CFG;
175         // set up the leap2a writer if we need it
176         $writingleap = false;
177         if ($this->exporter->get('formatclass') == PORTFOLIO_FORMAT_LEAP2A) {
178             $leapwriter = $this->exporter->get('format')->leap2a_writer();
179             $writingleap = true;
180         }
181         if ($this->attachment) { // simplest case first - single file attachment
182             $this->copy_files(array($this->singlefile), $this->attachment);
183             if ($writingleap) { // if we're writing leap, make the manifest to go along with the file
184                 $entry = new portfolio_format_leap2a_file($this->singlefile->get_filename(), $this->singlefile);
185                 $leapwriter->add_entry($entry);
186                 return $this->exporter->write_new_file($leapwriter->to_xml(), $this->exporter->get('format')->manifest_name(), true);
187             }
189         } else if (empty($this->post)) {  // exporting whole discussion
190             $content = ''; // if we're just writing HTML, start a string to add each post to
191             $ids = array(); // if we're writing leap2a, keep track of all entryids so we can add a selection element
192             foreach ($this->posts as $post) {
193                 $posthtml =  $this->prepare_post($post);
194                 if ($writingleap) {
195                     $ids[] = $this->prepare_post_leap2a($leapwriter, $post, $posthtml);
196                 } else {
197                     $content .= $posthtml . '<br /><br />';
198                 }
199             }
200             $this->copy_files($this->multifiles);
201             $name = 'discussion.html';
202             $manifest = ($this->exporter->get('format') instanceof PORTFOLIO_FORMAT_RICH);
203             if ($writingleap) {
204                 // add on an extra 'selection' entry
205                 $selection = new portfolio_format_leap2a_entry('forumdiscussion' . $this->discussionid,
206                     get_string('discussion', 'forum') . ': ' . $this->discussion->name, 'selection');
207                 $leapwriter->add_entry($selection);
208                 $leapwriter->make_selection($selection, $ids, 'Grouping');
209                 $content = $leapwriter->to_xml();
210                 $name = $this->get('exporter')->get('format')->manifest_name();
211             }
212             $this->get('exporter')->write_new_file($content, $name, $manifest);
214         } else { // exporting a single post
215             $posthtml = $this->prepare_post($this->post);
217             $content = $posthtml;
218             $name = 'post.html';
219             $manifest = ($this->exporter->get('format') instanceof PORTFOLIO_FORMAT_RICH);
221             if ($writingleap) {
222                 $this->prepare_post_leap2a($leapwriter, $this->post, $posthtml);
223                 $content = $leapwriter->to_xml();
224                 $name = $this->exporter->get('format')->manifest_name();
225             }
226             $this->copy_files($this->multifiles);
227             $this->get('exporter')->write_new_file($content, $name, $manifest);
228         }
229     }
231     /**
232      * helper function to add a leap2a entry element
233      * that corresponds to a single forum post,
234      * including any attachments
235      *
236      * the entry/ies are added directly to the leapwriter, which is passed by ref
237      *
238      * @param portfolio_format_leap2a_writer $leapwriter writer object to add entries to
239      * @param object $post                               the stdclass object representing the database record
240      * @param string $posthtml                           the content of the post (prepared by {@link prepare_post}
241      *
242      * @return int id of new entry
243      */
244     private function prepare_post_leap2a(portfolio_format_leap2a_writer $leapwriter, $post, $posthtml) {
245         $entry = new portfolio_format_leap2a_entry('forumpost' . $post->id,  $post->subject, 'resource', $posthtml);
246         $entry->published = $post->created;
247         $entry->updated = $post->modified;
248         $entry->author = $post->author;
249         if (is_array($this->keyedfiles) && array_key_exists($post->id, $this->keyedfiles) && is_array($this->keyedfiles[$post->id])) {
250             $leapwriter->link_files($entry, $this->keyedfiles[$post->id], 'forumpost' . $post->id . 'attachment');
251         }
252         $entry->add_category('web', 'resource_type');
253         $leapwriter->add_entry($entry);
254         return $entry->id;
255     }
257     /**
258      * @param array $files
259      * @param mixed $justone false of id of single file to copy
260      * @return bool|void
261      */
262     private function copy_files($files, $justone=false) {
263         if (empty($files)) {
264             return;
265         }
266         foreach ($files as $f) {
267             if ($justone && $f->get_id() != $justone) {
268                 continue;
269             }
270             $this->get('exporter')->copy_existing_file($f);
271             if ($justone && $f->get_id() == $justone) {
272                 return true; // all we need to do
273             }
274         }
275     }
276     /**
277      * this is a very cut down version of what is in forum_make_mail_post
278      *
279      * @global object
280      * @param int $post
281      * @return string
282      */
283     private function prepare_post($post, $fileoutputextras=null) {
284         global $DB;
285         static $users;
286         if (empty($users)) {
287             $users = array($this->user->id => $this->user);
288         }
289         if (!array_key_exists($post->userid, $users)) {
290             $users[$post->userid] = $DB->get_record('user', array('id' => $post->userid));
291         }
292         // add the user object on to the post so we can pass it to the leap writer if necessary
293         $post->author = $users[$post->userid];
294         $viewfullnames = true;
295         // format the post body
296         $options = new stdClass();
297         $options->para = false;
298         $options->filter = false;
299         $format = $this->get('exporter')->get('format');
300         $formattedtext = format_text($post->message, $post->messageformat, $options, $this->get('course')->id);
301         $formattedtext = portfolio_rewrite_pluginfile_urls($formattedtext, $this->modcontext->id, 'mod_forum', 'post', $post->id, $format);
303         $output = '<table border="0" cellpadding="3" cellspacing="0" class="forumpost">';
305         $output .= '<tr class="header"><td>';// can't print picture.
306         $output .= '</td>';
308         if ($post->parent) {
309             $output .= '<td class="topic">';
310         } else {
311             $output .= '<td class="topic starter">';
312         }
313         $output .= '<div class="subject">'.format_string($post->subject).'</div>';
315         $fullname = fullname($users[$post->userid], $viewfullnames);
316         $by = new stdClass();
317         $by->name = $fullname;
318         $by->date = userdate($post->modified, '', $this->user->timezone);
319         $output .= '<div class="author">'.get_string('bynameondate', 'forum', $by).'</div>';
321         $output .= '</td></tr>';
323         $output .= '<tr><td class="left side" valign="top">';
325         $output .= '</td><td class="content">';
327         $output .= $formattedtext;
329         if (is_array($this->keyedfiles) && array_key_exists($post->id, $this->keyedfiles) && is_array($this->keyedfiles[$post->id]) && count($this->keyedfiles[$post->id]) > 0) {
330             $output .= '<div class="attachments">';
331             $output .= '<br /><b>' .  get_string('attachments', 'forum') . '</b>:<br /><br />';
332             foreach ($this->keyedfiles[$post->id] as $file) {
333                 $output .= $format->file_output($file)  . '<br/ >';
334             }
335             $output .= "</div>";
336         }
338         $output .= '</td></tr></table>'."\n\n";
340         return $output;
341     }
342     /**
343      * @return string
344      */
345     function get_sha1() {
346         $filesha = '';
347         try {
348             $filesha = $this->get_sha1_file();
349         } catch (portfolio_caller_exception $e) { } // no files
351         if ($this->post) {
352             return sha1($filesha . ',' . $this->post->subject . ',' . $this->post->message);
353         } else {
354             $sha1s = array($filesha);
355             foreach ($this->posts as $post) {
356                 $sha1s[] = sha1($post->subject . ',' . $post->message);
357             }
358             return sha1(implode(',', $sha1s));
359         }
360     }
362     function expected_time() {
363         $filetime = $this->expected_time_file();
364         if ($this->posts) {
365             $posttime = portfolio_expected_time_db(count($this->posts));
366             if ($filetime < $posttime) {
367                 return $posttime;
368             }
369         }
370         return $filetime;
371     }
372     /**
373      * @uses CONTEXT_MODULE
374      * @return bool
375      */
376     function check_permissions() {
377         $context = get_context_instance(CONTEXT_MODULE, $this->cm->id);
378         if ($this->post) {
379             return (has_capability('mod/forum:exportpost', $context)
380                 || ($this->post->userid == $this->user->id
381                     && has_capability('mod/forum:exportownpost', $context)));
382         }
383         return has_capability('mod/forum:exportdiscussion', $context);
384     }
385     /**
386      * @return string
387      */
388     public static function display_name() {
389         return get_string('modulename', 'forum');
390     }
392     public static function base_supported_formats() {
393         return array(PORTFOLIO_FORMAT_FILE, PORTFOLIO_FORMAT_RICHHTML, PORTFOLIO_FORMAT_PLAINHTML, PORTFOLIO_FORMAT_LEAP2A);
394     }