1<?php
2/* Copyright (c) 1998-2012 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4class ilForumXMLParser extends ilSaxParser
5{
6    /**
7     *
8     * An instance of ilObjForum
9     *
10     * @var ilObjForum
11     */
12    private $forum;
13    private $entity;
14    private $mapping = array(
15        'frm' => array(),
16        'thr' => array(),
17        'pos' => array()
18    );
19    private $import_install_id = null;
20    private $user_id_mapping = array();
21    protected $mediaObjects = array();
22
23    /**
24     * @var null|string
25     */
26    protected $schema_version = null;
27
28    private $db;
29    /**
30     * Constructor
31     *
32     * @param	ilObjForum	$forum	 existing forum object
33     * @param	string		$a_xml_file	xml data
34     * @access	public
35     */
36    public function __construct($forum, $a_xml_data)
37    {
38        global $DIC;
39
40        parent::__construct();
41        $this->forum = $forum;
42        $this->setXMLContent('<?xml version="1.0" encoding="utf-8"?>' . $a_xml_data);
43        $this->aobject = new ilObjUser(ANONYMOUS_USER_ID);
44        $this->db = $DIC->database();
45    }
46
47    /**
48     * Set import directory
49     *
50     * @param	string	import directory
51     */
52    public function setImportDirectory($a_val)
53    {
54        $this->importDirectory = $a_val;
55    }
56
57    /**
58     * Get import directory
59     *
60     * @return	string	import directory
61     */
62    public function getImportDirectory()
63    {
64        return $this->importDirectory;
65    }
66
67    /**
68     * @return null|string
69     */
70    public function getSchemaVersion()
71    {
72        return $this->schema_version;
73    }
74
75    /**
76     * @param null|string $schema_version
77     */
78    public function setSchemaVersion($schema_version)
79    {
80        $this->schema_version = $schema_version;
81    }
82
83    /**
84    * set event handlers
85    *
86    * @param	resource	reference to the xml parser
87    * @access	private
88    */
89    public function setHandlers($a_xml_parser)
90    {
91        xml_set_object($a_xml_parser, $this);
92        xml_set_element_handler($a_xml_parser, 'handlerBeginTag', 'handlerEndTag');
93        xml_set_character_data_handler($a_xml_parser, 'handlerCharacterData');
94    }
95
96    /**
97    * handler for begin of element
98    *
99    * @param	resource	$a_xml_parser		xml parser
100    * @param	string		$a_name				element name
101    * @param	array		$a_attribs			element attributes array
102    */
103    public function handlerBeginTag($a_xml_parser, $a_name, $a_attribs)
104    {
105        switch ($a_name) {
106            case 'Forum':
107                $this->entity = 'forum';
108                $this->forumArray = array();
109                break;
110
111            case 'Thread':
112                $this->entity = 'thread';
113                $this->threadArray = array();
114                break;
115
116            case 'Post':
117                $this->mediaObjects = array();
118                $this->entity = 'post';
119                $this->postArray = array();
120                break;
121
122            case 'Content':
123                $this->entity = 'content';
124                $this->contentArray = array();
125                break;
126
127            case 'MediaObject':
128                $this->mediaObjects[] = $a_attribs;
129                break;
130        }
131    }
132
133    /**
134     * handler for end of element
135     *
136     * @param	resource	$a_xml_parser		xml parser
137     * @param	string		$a_name				element name
138     */
139    public function handlerEndTag($a_xml_parser, $a_name)
140    {
141        $this->cdata = trim($this->cdata);
142        $arrayname = strtolower($this->entity) . 'Array';
143        $x = &$this->$arrayname;
144
145        switch ($a_name) {
146            case 'Forum':
147                $query_num_posts = "SELECT COUNT(pos_pk) cnt
148										FROM frm_posts
149									WHERE pos_top_fk = " . $this->db->quote(
150                    $this->lastHandledForumId,
151                    'integer'
152                );
153
154                $res_pos = $this->db->query($query_num_posts);
155                $data_pos = $this->db->fetchAssoc($res_pos);
156                $num_posts = $data_pos['cnt'];
157
158                $query_num_threads = "SELECT COUNT(thr_pk) cnt
159										FROM frm_threads
160									  WHERE thr_top_fk = " . $this->db->quote(
161                    $this->lastHandledForumId,
162                    'integer'
163                );
164
165                $res_thr = $this->db->query($query_num_threads);
166                $data_thr = $this->db->fetchAssoc($res_thr);
167                $num_threads = $data_thr['cnt'];
168
169                $update_str = "$this->lastHandledForumId#$this->lastHandledThreadId#$this->lastHandledPostId";
170                $this->db->manipulateF(
171                    "UPDATE frm_data
172						SET top_last_post = %s,
173							top_num_posts = %s,
174							top_num_threads = %s,
175							top_usr_id = %s
176					WHERE top_frm_fk = %s",
177                    array('text', 'integer', 'integer', 'integer', 'integer'),
178                    array($update_str, $num_posts, $num_threads, $this->frm_last_mapped_top_usr_id, $this->forum_obj_id)
179                );
180                break;
181
182            case 'Id':
183                $x['Id'] = $this->cdata;
184                break;
185
186            case 'ObjId':
187                $x['ObjId'] = $this->cdata;
188                break;
189
190            case 'Title':
191                $x['Title'] = $this->cdata;
192                break;
193
194            case 'Description':
195                $x['Description'] = $this->cdata;
196                break;
197
198            case 'DefaultView':
199                $x['DefaultView'] = $this->cdata;
200                break;
201
202            case 'Pseudonyms':
203                $x['Pseudonyms'] = $this->cdata;
204                break;
205
206            case 'Statistics':
207                $x['Statistics'] = $this->cdata;
208                break;
209
210            case 'ThreadRatings':
211                $x['ThreadRatings'] = $this->cdata;
212                break;
213
214            case 'PostingActivation':
215                $x['PostingActivation'] = $this->cdata;
216                break;
217
218            case 'PresetSubject':
219                $x['PresetSubject'] = $this->cdata;
220                break;
221
222            case 'PresetRe':
223                $x['PresetRe'] = $this->cdata;
224                break;
225
226            case 'NotificationType':
227                $x['NotificationType'] = $this->cdata;
228                break;
229
230            case 'ForceNotification':
231                $x['ForceNotification'] = $this->cdata;
232                break;
233
234            case 'ToggleNotification':
235                $x['ToggleNotification'] = $this->cdata;
236                break;
237
238            case 'LastPost':
239                $x['LastPost'] = $this->cdata;
240                break;
241
242            case 'Moderator':
243                $x['Moderator'] = $this->cdata;
244                break;
245
246            case 'CreateDate':
247                $x['CreateDate'] = $this->cdata;
248                break;
249
250            case 'UpdateDate':
251                $x['UpdateDate'] = $this->cdata;
252                break;
253
254            case 'FileUpload':
255                $x['FileUpload'] = $this->cdata;
256                break;
257
258            case 'UpdateUserId':
259                $x['UpdateUserId'] = $this->cdata;
260                break;
261
262            case 'AuthorId':
263                $x['AuthorId'] = $this->cdata;
264                break;
265            case 'isAuthorModerator':
266                $x['isAuthorModerator'] = $this->cdata;
267                break;
268
269            case 'UserId':
270                $x['UserId'] = $this->cdata;
271                if ($this->entity == 'forum' && $this->forumArray) {
272                    //// @todo: Maybe problems if the forum xml is imported as content of a course
273                    // createSettings accesses superglobal $_GET  array, which can cause problems
274                    // with public_notifications of block settings
275                    $this->forum->createSettings();
276
277                    $forum_array = $this->getUserIdAndAlias(
278                        $this->forumArray['UserId'],
279                        ''
280                    );
281
282                    $this->frm_last_mapped_top_usr_id = $forum_array['usr_id'];
283
284                    $update_forum_array = $this->getUserIdAndAlias(
285                        $this->forumArray['UpdateUserId'],
286                        ''
287                    );
288                    // Store old user id
289                    // Manipulate user object
290                    // changed smeyer 28.7.16: the session id is not manipulated
291                    // anymore. Instead the user is passwd ilObjForum::update()
292                    $this->forum->setTitle($this->forumArray["Title"]);
293                    $this->forum->setDescription($this->forumArray["Description"]);
294                    $this->forum->update($update_forum_array['usr_id']);
295
296                    // create frm_settings
297                    $newObjProp = ilForumProperties::getInstance($this->forum->getId());
298                    $newObjProp->setDefaultView((int) $this->forumArray['DefaultView']);
299                    $newObjProp->setAnonymisation((int) $this->forumArray['Pseudonyms']);
300                    $newObjProp->setStatisticsStatus((int) $this->forumArray['Statistics']);
301                    $newObjProp->setIsThreadRatingEnabled((int) $this->forumArray['ThreadRatings']);
302                    $newObjProp->setPostActivation((int) $this->forumArray['PostingActivation']);
303                    $newObjProp->setPresetSubject((int) $this->forumArray['PresetSubject']);
304                    $newObjProp->setAddReSubject((int) $this->forumArray['PresetRe']);
305                    $newObjProp->setNotificationType($this->forumArray['NotificationType'] ? $this->forumArray['NotificationType'] : 'all_users');
306                    $newObjProp->setAdminForceNoti((int) $this->forumArray['ForceNotification']);
307                    $newObjProp->setUserToggleNoti((int) $this->forumArray['ToggleNotification']);
308                    $newObjProp->setFileUploadAllowed((int) $this->forumArray['FileUpload']);
309                    $newObjProp->setThreadSorting((int)$this->forumArray['Sorting']);
310                    $newObjProp->setMarkModeratorPosts((int)$this->forumArray['MarkModeratorPosts']);
311                    $newObjProp->update();
312
313                    $id = $this->getNewForumPk();
314                    $this->forum_obj_id = $newObjProp->getObjId();
315                    $this->mapping['frm'][$this->forumArray['Id']] = $id;
316                    $this->lastHandledForumId = $id;
317
318                    unset($this->forumArray);
319                }
320
321                break;
322
323            case 'Thread':
324                $update_str = "$this->lastHandledForumId#$this->lastHandledThreadId#$this->lastHandledPostId";
325                $this->db->manipulateF(
326                    "UPDATE frm_threads
327						SET thr_last_post = %s
328					WHERE thr_pk = %s",
329                    array('text', 'integer'),
330                    array($update_str, $this->lastHandledThreadId)
331                );
332                break;
333
334            case 'Subject':
335                $x['Subject'] = $this->cdata;
336                break;
337
338            case 'Alias':
339                $x['Alias'] = $this->cdata;
340                break;
341
342            case 'Sticky':
343                $x['Sticky'] = $this->cdata;
344                break;
345
346            case 'Sorting':
347                $x['Sorting'] = $this->cdata;
348                break;
349
350            case 'MarkModeratorPosts':
351                $x['MarkModeratorPosts'] = $this->cdata;
352                break;
353
354            case 'Closed':
355                $x['Closed'] = $this->cdata;
356
357                if ($this->entity == 'thread' && $this->threadArray) {
358                    $this->forumThread = new ilForumTopic();
359                    $this->forumThread->setId($this->threadArray['Id']);
360                    $this->forumThread->setForumId($this->lastHandledForumId);
361                    $this->forumThread->setSubject($this->threadArray['Subject']);
362                    $this->forumThread->setSticky($this->threadArray['Sticky']);
363                    $this->forumThread->setClosed($this->threadArray['Closed']);
364                    $this->forumThread->setCreateDate($this->threadArray['CreateDate']);
365                    $this->forumThread->setChangeDate($this->threadArray['UpdateDate']);
366                    $this->forumThread->setImportName($this->threadArray['ImportName']);
367
368                    $usr_data = $this->getUserIdAndAlias(
369                        $this->threadArray['UserId'],
370                        $this->threadArray['Alias']
371                    );
372
373                    $this->forumThread->setDisplayUserId($usr_data['usr_id']);
374                    $this->forumThread->setUserAlias($usr_data['usr_alias']);
375
376                    if (version_compare($this->getSchemaVersion(), '4.5.0', '<=')) {
377                        $this->threadArray['AuthorId'] = $this->threadArray['UserId'];
378                    }
379
380                    $author_id_data = $this->getUserIdAndAlias(
381                        $this->threadArray['AuthorId']
382                    );
383                    $this->forumThread->setThrAuthorId((int) $author_id_data['usr_id']);
384
385                    $this->forumThread->insert();
386
387                    $this->mapping['thr'][$this->threadArray['Id']] = $this->forumThread->getId();
388                    $this->lastHandledThreadId = $this->forumThread->getId();
389
390                    unset($this->threadArray);
391                }
392
393                break;
394
395            case 'Post':
396                break;
397
398            case 'Censorship':
399                $x['Censorship'] = $this->cdata;
400                break;
401
402            case 'CensorshipMessage':
403                $x['CensorshipMessage'] = $this->cdata;
404                break;
405
406            case 'Notification':
407                $x['Notification'] = $this->cdata;
408                break;
409
410            case 'ImportName':
411                $x['ImportName'] = $this->cdata;
412                break;
413
414            case 'Status':
415                $x['Status'] = $this->cdata;
416                break;
417
418            case 'Message':
419                $x['Message'] = $this->cdata;
420                break;
421
422            case 'Lft':
423                $x['Lft'] = $this->cdata;
424                break;
425
426            case 'Rgt':
427                $x['Rgt'] = $this->cdata;
428                break;
429
430            case 'Depth':
431                $x['Depth'] = $this->cdata;
432                break;
433
434            case 'ParentId':
435                $x['ParentId'] = $this->cdata;
436
437                if ($this->entity == 'post' && $this->postArray) {
438                    $this->forumPost = new ilForumPost();
439                    $this->forumPost->setId($this->postArray['Id']);
440                    $this->forumPost->setCensorship($this->postArray['Censorship']);
441                    $this->forumPost->setCensorshipComment($this->postArray['CensorshipMessage']);
442                    $this->forumPost->setNotification($this->postArray['Notification']);
443                    $this->forumPost->setImportName($this->postArray['ImportName']);
444                    $this->forumPost->setStatus($this->postArray['Status']);
445                    $this->forumPost->setMessage($this->postArray['Message']);
446                    $this->forumPost->setSubject($this->postArray['Subject']);
447                    $this->forumPost->setLft($this->postArray['Lft']);
448                    $this->forumPost->setRgt($this->postArray['Rgt']);
449                    $this->forumPost->setDepth($this->postArray['Depth']);
450                    $this->forumPost->setParentId($this->postArray['ParentId']);
451                    $this->forumPost->setThread($this->forumThread);
452                    $this->forumPost->setThreadId($this->lastHandledThreadId);
453                    $this->forumPost->setForumId($this->lastHandledForumId);
454                    $this->forumPost->setCreateDate($this->postArray['CreateDate']);
455                    $this->forumPost->setChangeDate($this->postArray['UpdateDate']);
456
457                    $usr_data = $this->getUserIdAndAlias(
458                        $this->postArray['UserId'],
459                        $this->postArray['Alias']
460                    );
461                    $update_usr_data = $this->getUserIdAndAlias(
462                        $this->postArray['UpdateUserId']
463                    );
464                    $this->forumPost->setDisplayUserId($usr_data['usr_id']);
465                    $this->forumPost->setUserAlias($usr_data['usr_alias']);
466                    $this->forumPost->setUpdateUserId($update_usr_data['usr_id']);
467
468                    if (version_compare($this->getSchemaVersion(), '4.5.0', '<=')) {
469                        $this->postArray['AuthorId'] = $this->postArray['UserId'];
470                    }
471                    $author_id_data = $this->getUserIdAndAlias(
472                        $this->postArray['AuthorId']
473                    );
474                    $this->forumPost->setPosAuthorId((int) $author_id_data['usr_id']);
475
476                    if ($this->postArray['isAuthorModerator'] === 'NULL') {
477                        $this->forumPost->setIsAuthorModerator(null);
478                    } else {
479                        $this->forumPost->setIsAuthorModerator((int) $this->postArray['isAuthorModerator']);
480                    }
481
482                    $this->forumPost->insert();
483
484                    if ($this->mapping['pos'][$this->postArray['ParentId']]) {
485                        $parentId = $this->mapping['pos'][$this->postArray['ParentId']];
486                    } else {
487                        $parentId = 0;
488                    }
489
490                    $postTreeNodeId = $this->db->nextId('frm_posts_tree');
491                    $this->db->insert('frm_posts_tree', array(
492                        'fpt_pk' => array('integer', $postTreeNodeId),
493                        'thr_fk' => array('integer', $this->lastHandledThreadId),
494                        'pos_fk' => array('integer', $this->forumPost->getId()),
495                        'parent_pos' => array('integer', $parentId),
496                        'lft' => array('integer', $this->postArray['Lft']),
497                        'rgt' => array('integer', $this->postArray['Rgt']),
498                        'depth' => array('integer', $this->postArray['Depth']),
499                        'fpt_date' => array('timestamp', date('Y-m-d H:i:s'))
500                    ));
501
502                    $this->mapping['pos'][$this->postArray['Id']] = $this->forumPost->getId();
503                    $this->lastHandledPostId = $this->forumPost->getId();
504
505                    $media_objects_found = false;
506                    foreach ($this->mediaObjects as $mob_attr) {
507                        $importfile = $this->getImportDirectory() . '/' . $mob_attr['uri'];
508                        if (file_exists($importfile)) {
509                            $mob = ilObjMediaObject::_saveTempFileAsMediaObject(basename($importfile), $importfile, false);
510                            ilObjMediaObject::_saveUsage($mob->getId(), "frm:html", $this->forumPost->getId());
511
512                            $this->forumPost->setMessage(
513                                str_replace(
514                                    array(
515                                        "src=\"" . $mob_attr["label"] . "\"",
516                                        "src=\"" . preg_replace("/(il)_[\d]+_(mob)_([\d]+)/", "$1_0_$2_$3", $mob_attr["label"]) . "\""
517                                    ),
518                                    "src=\"" . "il_" . IL_INST_ID . "_mob_" . $mob->getId() . "\"",
519                                    $this->forumPost->getMessage()
520                                )
521                            );
522                            $media_objects_found = true;
523                        }
524                    }
525
526                    if ($media_objects_found) {
527                        $this->forumPost->update();
528                    }
529
530                    unset($this->postArray);
531                }
532
533                break;
534
535                case 'Content':
536                    $x['content'] = $this->cdata;
537                    break;
538
539                case 'Attachment':
540                    $filedata = new ilFileDataForum($this->forum->getId(), $this->lastHandledPostId);
541
542                    $importPath = $this->contentArray['content'];
543
544                    if (strlen($importPath)) {
545                        $importPath = $this->getImportDirectory() . '/' . $importPath;
546
547                        $newFilename = preg_replace("/^\d+_\d+(_.*)/ims", $this->forum->getId() . "_" . $this->lastHandledPostId . "$1", basename($importPath));
548                        $path = $filedata->getForumPath();
549                        $newPath = $path . '/' . $newFilename;
550                        @copy($importPath, $newPath);
551                    }
552                    break;
553        }
554
555        $this->cdata = '';
556
557        return;
558    }
559
560    private function getIdAndAliasArray($imp_usr_id, $param = 'import')
561    {
562        $select = 'SELECT od.obj_id, ud.login
563					FROM object_data od
564						INNER JOIN usr_data ud
565							ON od.obj_id = ud.usr_id';
566
567        if ($param == 'import') {
568            $where = ' WHERE od.import_id = ' . $this->db->quote(
569                'il_' . $this->import_install_id . '_usr_' . $imp_usr_id,
570                'text'
571            );
572        }
573
574        if ($param == 'user') {
575            $where = ' WHERE ud.usr_id = ' . $this->db->quote(
576                $imp_usr_id,
577                'integer'
578            );
579        }
580
581        $query = $this->db->query($select . $where);
582
583        while ($res = $this->db->fetchAssoc($query)) {
584            break;
585        }
586
587        if ($res) {
588            return array(
589                'usr_id' => $res['obj_id'],
590                'usr_alias' => $res['login']
591            );
592        } else {
593            return false;
594        }
595    }
596
597    private function getAnonymousArray()
598    {
599        return array(
600            'usr_id' => $this->aobject->getId(),
601            'usr_alias' => $this->aobject->getLogin()
602        );
603    }
604
605
606    private function getUserIdAndAlias($imp_usr_id, $imp_usr_alias = '')
607    {
608        if ((int) $imp_usr_id > 0) {
609            $newUsrId = -1;
610
611            if ($this->import_install_id != IL_INST_ID && IL_INST_ID > 0) {
612                // Different installations
613                if ($this->user_id_mapping[$imp_usr_id]) {
614                    return $this->user_id_mapping[$imp_usr_id];
615                } else {
616                    $res = $this->getIdAndAliasArray($imp_usr_id, 'import');
617
618                    if ($res) {
619                        $this->user_id_mapping[$imp_usr_id] = $res;
620
621                        return $res;
622                    } else {
623                        $return_value = $this->getAnonymousArray();
624                        $this->user_id_mapping[$imp_usr_id] = $return_value;
625
626                        return $return_value;
627                    }
628                }
629            } elseif ($this->import_install_id == IL_INST_ID && IL_INST_ID == 0) {
630                // Eventually different installations. We cannot determine it.
631                if ($this->user_id_mapping[$imp_usr_id]) {
632                    return $this->user_id_mapping[$imp_usr_id];
633                } else {
634                    $res = $this->getIdAndAliasArray($imp_usr_id, 'import');
635
636                    if ($res) {
637                        $this->user_id_mapping[$imp_usr_id] = $res;
638
639                        return $res;
640                    } else {
641                        // Same installation
642                        if ($this->user_id_mapping[$imp_usr_id]) {
643                            return $this->user_id_mapping[$imp_usr_id];
644                        } else {
645                            $res = $this->getIdAndAliasArray($imp_usr_id, 'user');
646
647                            if ($res) {
648                                $this->user_id_mapping[$imp_usr_id] = $res;
649
650                                return $res;
651                            } else {
652                                $return_value = $this->getAnonymousArray();
653                                $this->user_id_mapping[$imp_usr_id] = $return_value;
654
655                                return $return_value;
656                            }
657                        }
658                    }
659                }
660            } else {
661                // Same installation
662                if ($this->user_id_mapping[$imp_usr_id]) {
663                    return $this->user_id_mapping[$imp_usr_id];
664                } else {
665                    $res = $this->getIdAndAliasArray($imp_usr_id, 'user');
666
667                    if ($res) {
668                        $this->user_id_mapping[$imp_usr_id] = $res;
669
670                        return $res;
671                    } else {
672                        $return_value = $this->getAnonymousArray();
673                        $this->user_id_mapping[$imp_usr_id] = $return_value;
674
675                        return $return_value;
676                    }
677                }
678            }
679        } else {
680            return array(
681                'usr_id' => $imp_usr_id,
682                'usr_alias' => $imp_usr_alias
683            );
684        }
685    }
686
687    public function setImportInstallId($id)
688    {
689        $this->import_install_id = $id;
690    }
691
692    private function getNewForumPk()
693    {
694        $query = "SELECT top_pk FROM frm_data
695					WHERE top_frm_fk = " . $this->db->quote(
696            $this->forum->getId(),
697            'integer'
698        );
699        $res = $this->db->query($query);
700        $data = $this->db->fetchAssoc($res);
701
702        return $data['top_pk'];
703    }
704
705    /**
706    * handler for character data
707    *
708    * @param	resource	$a_xml_parser		xml parser
709    * @param	string		$a_data				character data
710    */
711    public function handlerCharacterData($a_xml_parser, $a_data)
712    {
713        if ($a_data != "\n") {
714            // Replace multiple tabs with one space
715            $a_data = preg_replace("/\t+/", " ", $a_data);
716
717            $this->cdata .= $a_data;
718        }
719    }
720
721    /**
722     * starts parsing an changes object by side effect.
723     *
724     * @return boolean true, if no errors happend.
725     *
726     */
727    public function start()
728    {
729        $this->startParsing();
730        return $this->result > 0;
731    }
732}
733