lib.php 17.1 KB
Newer Older
1
2
3
<?php
/**
 * Mahara: Electronic portfolio, weblog, resume builder and social networking
4
5
 * Copyright (C) 2006-2009 Catalyst IT Ltd and others; see:
 *                         http://wiki.mahara.org/Contributors
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @package    mahara
 * @subpackage artefact-blog-import-leap
 * @author     Catalyst IT Ltd
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL
24
 * @copyright  (C) 2006-2009 Catalyst IT Ltd http://catalyst.net.nz
25
26
27
28
29
30
 *
 */

defined('INTERNAL') || die();

/**
Francois Marier's avatar
Francois Marier committed
31
 * Implements Leap2A import of blog related entries into Mahara
32
 *
Francois Marier's avatar
Francois Marier committed
33
 * For more information about Leap blog importing, see:
34
 * http://wiki.mahara.org/Developer_Area/Import//Export/LEAP_Import/Blog_Artefact_Plugin
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
 *
 * TODO:
 * - Get entries that feel they're part of the blog, not just entries the blog feels are part of it
 * - Import raw ATOM feed entries as blog posts
 */
class LeapImportBlog extends LeapImportArtefactPlugin {

    /**
     * Import an entry as a blog, with associated blog posts and attachments
     */
    const STRATEGY_IMPORT_AS_BLOG = 1;

    /**
     * Import entry as an simple blog post into a catch-all blog
     */
    const STRATEGY_IMPORT_AS_ENTRY = 2;

52
53
54
55
56
57
58
59
60
61
62
63
    private static $firstblogid = null;
    private static $importedablog = false;

    /**
     * Users get a default blog when they're created. We don't want this user
     * to have one if their import includes a blog. So we remember the default
     * blog ID here in order to delete it later if necessary.
     */
    public static function setup(PluginImportLeap $importer) {
        self::$firstblogid = get_field('artefact', 'id', 'owner', $importer->get('usr'), 'artefacttype', 'blog');
    }

64
    public static function get_import_strategies_for_entry(SimpleXMLElement $entry, PluginImportLeap $importer) {
65
66
        $strategies = array();

67
68
69
70
71
72
73
74
75
76
        if (PluginImportLeap::is_rdf_type($entry, $importer, 'selection')) {
            if (PluginImportLeap::is_correct_category_scheme($entry, $importer, 'selection_type', 'Blog')) {
                $score = 100;
            } else {
                // the blog plugin can either fall back to importing single entries
                // or handle the case where things are a selection that have no other strategies either.
                // however, in the case where the otherrequiredentries for the selection have a higher strategy elsewhere,
                // we need to still fallback to importing a selection post as a blog post by itself, to avoid dataloss.
                $score = 20; // other things *can* be imported as blogs
            }
77
78
            $otherrequiredentries = array();

79
            // Get entries that this blog/selection feels are a part of it
80
81
82
83
84
85
            foreach ($entry->link as $link) {
                if ($importer->curie_equals($link['rel'], PluginImportLeap::NS_LEAP, 'has_part') && isset($link['href'])) {
                    $otherrequiredentries[] = (string)$link['href'];
                }
            }

86
            // TODO: Get entries that feel they should be a part of this blog/selection
87
88
89
90
91
92
            // We can compare the lists and perhaps warn if they're different
            //    $otherentries = $importer->xml->xpath('//a:feed/a:entry/a:link[@rel="leap:is_part_of" and @href="' . $entryid . '"]/../a:id');

            $otherrequiredentries = array_unique($otherrequiredentries);
            $strategies[] = array(
                'strategy' => self::STRATEGY_IMPORT_AS_BLOG,
93
                'score'    => $score,
94
95
                'other_required_entries' => $otherrequiredentries,
            );
96
97
98
99
100
101
102
            if ($score == 20) {
                $strategies[] = array(
                    'strategy' => self::STRATEGY_IMPORT_AS_ENTRY,
                    'score'    => 10,
                    'other_required_entries' => array(),
                );
            }
103
104
105
        }
        else {
            // The blog can import any entry as a literal blog post
106
107
108
109
110
111
112
            // Get files that this blogpost/catchall feels are a part of it
            $otherrequiredentries = array();
            foreach ($entry->link as $link) {
                if ($importer->curie_equals($link['rel'], '', 'enclosure') && isset($link['href'])) {
                    $otherrequiredentries[] = (string)$link['href'];
                }
            }
113
114
115
            $strategies[] = array(
                'strategy' => self::STRATEGY_IMPORT_AS_ENTRY,
                'score'    => 10,
116
                'other_required_entries' => $otherrequiredentries,
117
118
119
120
121
122
            );
        }

        return $strategies;
    }

123
    public static function import_using_strategy(SimpleXMLElement $entry, PluginImportLeap $importer, $strategy, array $otherentries) {
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
        $artefactmapping = array();
        switch ($strategy) {
        case self::STRATEGY_IMPORT_AS_BLOG:
            // First, the blog
            $blog = new ArtefactTypeBlog();
            $blog->set('title', (string)$entry->title);
            $blog->set('description', PluginImportLeap::get_entry_content($entry, $importer));
            $blog->set('owner', $importer->get('usr'));
            if ($published = strtotime((string)$entry->published)) {
                $blog->set('ctime', $published);
            }
            if ($updated = strtotime((string)$entry->updated)) {
                $blog->set('mtime', $updated);
            }
            $blog->set('tags', PluginImportLeap::get_entry_tags($entry));
            $blog->commit();
            $artefactmapping[(string)$entry->id] = array($blog->get('id'));
141
            self::$importedablog = true;
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173

            // Then, the blog posts
            foreach ($otherentries as $entryid) {
                $blogentry = $importer->get_entry_by_id($entryid);
                if (!$blogentry) {
                    // TODO: what to do here? Also - should this be checked here or earlier?
                    $importer->trace("WARNING: Blog $entry->id claims to have part $entryid which doesn't exist");
                    continue;
                }

                $artefactmapping[$entryid] = self::create_blogpost($blogentry, $importer, $blog->get('id'));
            }
            break;
        case self::STRATEGY_IMPORT_AS_ENTRY:
            $blogid = self::ensure_catchall_blog($importer);
            $artefactmapping[(string)$entry->id] = self::create_blogpost($entry, $importer, $blogid);
            break;
        default:
            throw new ImportException($importer, 'TODO: get_string: unknown strategy chosen for importing entry');
        }
        return $artefactmapping;
    }

    /**
     * Attaches files to blog posts
     *
     * We look at the leap relationships to add attachments. Currently this 
     * looks explicitly for the has_attachment relationship.
     *
     * If importing an entry resulted in importing a new file (caused by the 
     * entry having out-of-line content), we attach that file to the entry.
     */
174
    public static function setup_relationships(SimpleXMLElement $entry, PluginImportLeap $importer, $strategy, array $otherentries) {
175
        $newartefactmapping = array();
176
177
178
179
180
        switch ($strategy) {
        case self::STRATEGY_IMPORT_AS_BLOG:
            foreach ($otherentries as $entryid) {
                $blogpostentry = $importer->get_entry_by_id($entryid);
                // Get all attachments this blogpost things are attached to it
181
                if (!isset($blogpostentry->link)) {
182
183
                    continue;
                }
184
185
                foreach ($blogpostentry->link as $blogpostlink) {
                    $blogpost = null;
186
                    if ($importer->curie_equals($blogpostlink['rel'], '', 'enclosure') && isset($blogpostlink['href'])) {
187
188
189
190
191
192
                        if (!$blogpost) {
                            $artefactids = $importer->get_artefactids_imported_by_entryid((string)$blogpostentry->id);
                            $blogpost = new ArtefactTypeBlogPost($artefactids[0]);
                        }
                        $importer->trace("Attaching file $blogpostlink[href] to blog post $blogpostentry->id", PluginImportLeap::LOG_LEVEL_VERBOSE);
                        $artefactids = $importer->get_artefactids_imported_by_entryid((string)$blogpostlink['href']);
193
194
                        if (isset($artefactids[0])) {
                            $blogpost->attach($artefactids[0]);
Francois Marier's avatar
Francois Marier committed
195
                        } else { // it may be just an attached file, with no Leap2A element in its own right ....
196
197
                            if ($id = self::attach_linked_file($blogpostentry, $blogpostlink, $importer)) {
                                $blogpost->attach($id);
198
                                $newartefactmapping[(string)$blogpostlink['href']][] = $id;
199
                            }
200
                        }
201
202
203
204
205
206
207
208
209
210
                    }
                    if ($blogpost) {
                        $blogpost->commit();
                    }
                }

                self::setup_outoflinecontent_relationship($blogpostentry, $importer);
            }
            break;
        case self::STRATEGY_IMPORT_AS_ENTRY:
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
            $blogpostids = $importer->get_artefactids_imported_by_entryid((string)$entry->id);
            if (!isset($blogpostids[0])) {
                // weird!
                break;
            }
            $blogpost = new ArtefactTypeBlogPost($blogpostids[0]);
            foreach ($entry->link as $link) {
                if ($importer->curie_equals($link['rel'], '', 'enclosure') && isset($link['href'])) {
                    if (isset($artefactids[0])) {
                        $blogpost->attach($artefactids[0]);
                    } else {
                        if ($id = self::attach_linked_file($entry, $link, $importer)) {
                            $blogpost->attach($id);
                            $newartefactmapping[(string)$link['href']][] = $id;
                        }
                    }
                }
            }
            $blogpost->commit();
230
            self::setup_outoflinecontent_relationship($entry, $importer);
231
232
233
234
            break;
        default:
            throw new ImportException($importer, 'TODO: get_string: unknown strategy chosen for importing entry');
        }
235
236
237
238
        return $newartefactmapping;
    }

    /**
Francois Marier's avatar
Francois Marier committed
239
240
     * Attaches a file to a blogpost entry that was just linked directly, rather than having a Leap2a entry
     * See http://wiki.leapspecs.org/2A/files
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
     *
     * @param SimpleXMLElement $blogpostentry
     * @param SimpleXMLElement $blogpostlink
     * @param PluginImportLeap $importer
     */
    private static function attach_linked_file($blogpostentry, $blogpostlink, PluginImportLeap $importer) {
        $importer->trace($blogpostlink);
        $pathname = urldecode((string)$blogpostlink['href']);
        $dir = dirname($importer->get('filename'));
        $pathname = $dir . DIRECTORY_SEPARATOR . $pathname;
        if (!file_exists($pathname)) {
            return false;
        }
        // Note: this data is passed (eventually) to ArtefactType->__construct,
        // which calls strtotime on the dates for us
        $data = (object)array(
            'title' => (string)$blogpostentry->title . ' ' . get_string('attachment', 'artefact.blog'),
            'owner' => $importer->get('usr'),
            'filetype' => mime_content_type($pathname),
        );
        return ArtefactTypeFile::save_file($pathname, $data, $importer->get('usrobj'), true);
262
263
    }

264
265
266
267
268
269
    /**
     * Deletes the default blog that is created for all users
     */
    public static function cleanup(PluginImportLeap $importer) {
        if (self::$importedablog && self::$firstblogid) {
            $blog = artefact_instance_from_id(self::$firstblogid);
270
271
272
            if (!$blog->has_children()) { // TODO see #544160
                $blog->delete();
            }
273
        }
274
275
276
277
        $userid = $importer->get('usr');
        if (count_records('artefact', 'artefacttype', 'blog', 'owner', $userid) != 1) {
            set_account_preference($userid, 'multipleblogs', 1);
        }
278
279
    }

280
281
282
283
284
285
286
287
288
289
290
291
    /**
     * Creates a catch-all blog if one doesn't exist already
     *
     * @param PluginImportLeap $importer The importer
     * @return int The artefact ID of the catch-all blog
     */
    private static function ensure_catchall_blog(PluginImportLeap $importer) {
        static $blogid = null;
        if (is_null($blogid)) {
            $time = time(); // TODO maybe the importer will get a time field to record time of import
            $blog = new ArtefactTypeBlog();
            $title = $importer->get('xml')->xpath('//a:feed/a:title');
Nigel McNie's avatar
Nigel McNie committed
292
293
            $blog->set('title', get_string('dataimportedfrom', 'artefact.blog', (string)$title[0]));
            $blog->set('description', get_string('entriesimportedfromleapexport', 'artefact.blog'));
294
295
296
297
298
            $blog->set('owner', $importer->get('usr'));
            $blog->set('ctime', $time);
            $blog->set('mtime', $time);
            $blog->commit();
            $blogid = $blog->get('id');
299
            self::$importedablog = true;
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
        }

        return $blogid;
    }

    /**
     * Creates a blogpost from the given entry
     *
     * @param SimpleXMLElement $entry    The entry to create the blogpost from
     * @param PluginImportLeap $importer The importer
     * @param int $blogid                The blog in which to put the post
     * @return array A list of artefact IDs created, to be used with the artefact mapping. 
     *               There will either be one (the blogpost ID), or two. If there is two, the 
     *               second one will be the ID of the file created to hold the out-of-line 
     *               content associated with the blogpost
     */
    private static function create_blogpost(SimpleXMLElement $entry, PluginImportLeap $importer, $blogid) {
        $createdartefacts = array();
        $blogpost = new ArtefactTypeBlogPost();
        $blogpost->set('title', (string)$entry->title);
        // If the entry has out of line content, we import that separately as a 
        // file and set the content to refer to it
        if (isset($entry->content['src']) && isset($entry->content['type'])) {
            $file = LeapImportFile::create_file($entry, $importer);
            $createdartefacts[] = $file->get('id');

326
            $content = '<a href="' . get_config('wwwroot') . 'artefact/file/download.php?file=' . $file->get('id') . '"'
327
328
329
330
331
332
333
334
335
336
                . ' title="' . hsc($file->get('title')) . '">';
            if (is_image_mime_type($file->get('filetype'))) {
                $content .= '<img src="' . get_config('wwwroot') 
                    . 'artefact/file/download.php?file=' . $file->get('id') . '&amp;maxwidth=500&amp;maxheight=500"'
                    . ' alt="' . hsc($file->get('title')) . '">';
            }
            $content .= '</a>';
            $blogpost->set('description', $content);
        }
        else {
337
338
339
340
341
342
            $description = PluginImportLeap::get_entry_content($entry, $importer);
            $type = isset($entry->content['type']) ? (string)$entry->content['type'] : 'text';
            if ($type == 'text') {
                $description = format_whitespace($description);
            }
            $blogpost->set('description', $description);
343
344
345
346
347
348
349
350
        }
        if ($published = strtotime((string)$entry->published)) {
            $blogpost->set('ctime', $published);
        }
        if ($updated = strtotime((string)$entry->updated)) {
            $blogpost->set('mtime', $updated);
        }

351
        $draftpost = PluginImportLeap::is_correct_category_scheme($entry, $importer, 'readiness', 'Unready');
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
        $blogpost->set('published', $draftpost ? 0 : 1);

        $blogpost->set('owner', $importer->get('usr'));
        $blogpost->set('parent', $blogid);
        $blogpost->set('tags', PluginImportLeap::get_entry_tags($entry));
        $blogpost->commit();
        array_unshift($createdartefacts, $blogpost->get('id'));

        return $createdartefacts;
    }

    /**
     * Checks to see if a blogpost had out-of-line content, and if it did, 
     * attaches the generated file to it
     *
     * @param SimpleXMLElement $entry    The entry to check
     * @param PluginImportLeap $importer The importer
     */
    private static function setup_outoflinecontent_relationship(SimpleXMLElement $entry, PluginImportLeap $importer) {
        $artefactids = $importer->get_artefactids_imported_by_entryid((string)$entry->id);
        if (count($artefactids) == 2) {
            // In this case, a file was created as a result of 
            // importing a blog entry with out-of-line content. We 
            // attach the file to this post.
            $blogpost = new ArtefactTypeBlogPost($artefactids[0]);
377
            $blogpost->attach($artefactids[1]);
378
379
380
381
382
383
384
            $blogpost->commit();
        }
    }

}

?>