1<?php
2/* Copyright (c) 1998-2012 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4define("NEWS_NOTICE", 0);
5define("NEWS_MESSAGE", 1);
6define("NEWS_WARNING", 2);
7
8define("NEWS_TEXT", "text");
9define("NEWS_HTML", "html");
10define("NEWS_AUDIO", "audio");
11define("NEWS_USERS", "users");
12define("NEWS_PUBLIC", "public");
13
14
15
16/**
17 * @defgroup ServicesNews Services/News
18 *
19 * A news item can be created by different sources. E.g. when
20 * a new forum posting is created, or when a change in a
21 * learning module is announced.
22 *
23 * Please note that this class contains a lot of deprectated functions that
24 * will be move to other classes in the future. Please avoid to use these functions. This class should
25 * be a pure data class without persistence in the future.
26 *
27 * @author Alex Killing <alex.killing@gmx.de>
28 * @version $Id$
29 *
30 * @ingroup ServicesNews
31 */
32class ilNewsItem
33{
34    /**
35     * @var ilDB
36     */
37    protected $db;
38
39    /**
40     * @var ilTree
41     */
42    protected $tree;
43
44    /**
45     * @var ilAccessHandler
46     */
47    protected $access;
48
49    /**
50     * @var ilObjectDataCache
51     */
52    protected $obj_data_cache;
53
54    /**
55     * @var ilObjUser
56     */
57    protected $user;
58
59    /**
60     * @var ilLanguage
61     */
62    protected $lng;
63
64    /**
65     * @var ilCtrl
66     */
67    protected $ctrl;
68
69    protected $id;
70    protected $title;
71    protected $content;
72    /**
73     * @var bool
74     */
75    protected $content_html;
76    protected $context_obj_id;
77    protected $context_obj_type;
78    protected $context_sub_obj_id;
79    protected $context_sub_obj_type;
80    protected $content_type = "text";
81    protected $creation_date;
82    protected $update_date;
83    protected $user_id;
84    /**
85     * @var int
86     */
87    protected $update_user_id;
88    protected $visibility = "users";
89    protected $content_long;
90    protected $priority = 1;
91    protected $content_is_lang_var = 0;
92    protected $mob_id;
93    protected $playtime;
94
95    private static $privFeedId = false;
96    private $limitation;
97
98    /**
99    * Constructor.
100    *
101    * @param	int	$a_id
102    */
103    public function __construct($a_id = 0)
104    {
105        global $DIC;
106
107        $this->db = $DIC->database();
108        $this->tree = $DIC->repositoryTree();
109        $this->access = $DIC->access();
110        $this->obj_data_cache = $DIC["ilObjDataCache"];
111        $this->user = $DIC->user();
112        $this->lng = $DIC->language();
113        $this->ctrl = $DIC->ctrl();
114        if ($a_id > 0) {
115            $this->setId($a_id);
116            $this->read();
117        }
118        $this->limitation = true;
119    }
120
121    /**
122     * Set Id.
123     *
124     * @param	int	$a_id
125     */
126    public function setId($a_id)
127    {
128        $this->id = $a_id;
129    }
130
131    /**
132     * Get Id.
133     *
134     * @return	int
135     */
136    public function getId()
137    {
138        return $this->id;
139    }
140
141    /**
142     * Set Title.
143     *
144     * @param	string	$a_title	Title of news item.
145     */
146    public function setTitle($a_title)
147    {
148        $this->title = $a_title;
149    }
150
151    /**
152     * Get Title.
153     *
154     * @return	string	Title of news item.
155     */
156    public function getTitle()
157    {
158        return $this->title;
159    }
160
161    /**
162     * Set Content.
163     *
164     * @param	string	$a_content	Content of news.
165     */
166    public function setContent($a_content)
167    {
168        $this->content = $a_content;
169    }
170
171    /**
172     * Get Content.
173     *
174     * @return	string	Content of news.
175     */
176    public function getContent()
177    {
178        return $this->content;
179    }
180
181    /**
182     * Set ContextObjId.
183     *
184     * @param	int	$a_context_obj_id
185     */
186    public function setContextObjId($a_context_obj_id)
187    {
188        $this->context_obj_id = $a_context_obj_id;
189    }
190
191    /**
192     * Get ContextObjId.
193     *
194     * @return	int
195     */
196    public function getContextObjId()
197    {
198        return $this->context_obj_id;
199    }
200
201    /**
202     * Set ContextObjType.
203     *
204     * @param	int	$a_context_obj_type
205     */
206    public function setContextObjType($a_context_obj_type)
207    {
208        $this->context_obj_type = $a_context_obj_type;
209    }
210
211    /**
212     * Get ContextObjType.
213     *
214     * @return	int
215     */
216    public function getContextObjType()
217    {
218        return $this->context_obj_type;
219    }
220
221    /**
222     * Set ContextSubObjId.
223     *
224     * @param	int	$a_context_sub_obj_id
225     */
226    public function setContextSubObjId($a_context_sub_obj_id)
227    {
228        $this->context_sub_obj_id = $a_context_sub_obj_id;
229    }
230
231    /**
232     * Get ContextSubObjId.
233     *
234     * @return	int
235     */
236    public function getContextSubObjId()
237    {
238        return $this->context_sub_obj_id;
239    }
240
241    /**
242     * Set ContextSubObjType.
243     *
244     * @param	int	$a_context_sub_obj_type
245     */
246    public function setContextSubObjType($a_context_sub_obj_type)
247    {
248        $this->context_sub_obj_type = $a_context_sub_obj_type;
249    }
250
251    /**
252     * Get ContextSubObjType.
253     *
254     * @return	int
255     */
256    public function getContextSubObjType()
257    {
258        return $this->context_sub_obj_type;
259    }
260
261    /**
262     * Set ContentType.
263     *
264     * @param	string	$a_content_type	Content type.
265     */
266    public function setContentType($a_content_type = "text")
267    {
268        $this->content_type = $a_content_type;
269    }
270
271    /**
272     * Get ContentType.
273     *
274     * @return	string	Content type.
275     */
276    public function getContentType()
277    {
278        return $this->content_type;
279    }
280
281    /**
282     * Set CreationDate.
283     *
284     * @param	string	$a_creation_date	Date of creation.
285     */
286    public function setCreationDate($a_creation_date)
287    {
288        $this->creation_date = $a_creation_date;
289    }
290
291    /**
292     * Get CreationDate.
293     *
294     * @return	string	Date of creation.
295     */
296    public function getCreationDate()
297    {
298        return $this->creation_date;
299    }
300
301    /**
302     * Set UpdateDate.
303     *
304     * @param	string	$a_update_date	Date of last update.
305     */
306    public function setUpdateDate($a_update_date)
307    {
308        $this->update_date = $a_update_date;
309    }
310
311    /**
312     * Get UpdateDate.
313     *
314     * @return	string	Date of last update.
315     */
316    public function getUpdateDate()
317    {
318        return $this->update_date;
319    }
320
321    /**
322     * Set UserId.
323     *
324     * @param	int	$a_user_id	User Id of last update.
325     */
326    public function setUserId($a_user_id)
327    {
328        $this->user_id = $a_user_id;
329    }
330
331    /**
332     * Get UserId.
333     *
334     * @return	int	User Id of last update.
335     */
336    public function getUserId()
337    {
338        return $this->user_id;
339    }
340
341    /**
342     * Set update user id
343     *
344     * @param int $a_val update user id
345     */
346    public function setUpdateUserId($a_val)
347    {
348        $this->update_user_id = $a_val;
349    }
350
351    /**
352     * Get update user id
353     *
354     * @return int update user id
355     */
356    public function getUpdateUserId()
357    {
358        return $this->update_user_id;
359    }
360
361    /**
362     * Set Visibility.
363     *
364     * @param	string	$a_visibility	Access level of news.
365     */
366    public function setVisibility($a_visibility = "users")
367    {
368        $this->visibility = $a_visibility;
369    }
370
371    /**
372     * Get Visibility.
373     *
374     * @return	string	Access level of news.
375     */
376    public function getVisibility()
377    {
378        return $this->visibility;
379    }
380
381    /**
382     * Set ContentLong.
383     *
384     * @param	string	$a_content_long	Long content of news
385     */
386    public function setContentLong($a_content_long)
387    {
388        $this->content_long = $a_content_long;
389    }
390
391    /**
392     * Get ContentLong.
393     *
394     * @return	string	Long content of news
395     */
396    public function getContentLong()
397    {
398        return $this->content_long;
399    }
400
401    /**
402     * Set Priority.
403     *
404     * @param	int	$a_priority	News Priority
405     */
406    public function setPriority($a_priority = 1)
407    {
408        $this->priority = $a_priority;
409    }
410
411    /**
412     * Get Priority.
413     *
414     * @return	int	News Priority
415     */
416    public function getPriority()
417    {
418        return $this->priority;
419    }
420
421    /**
422     * Set ContentIsLangVar.
423     *
424     * @param	boolean	$a_content_is_lang_var
425     */
426    public function setContentIsLangVar($a_content_is_lang_var = 0)
427    {
428        $this->content_is_lang_var = $a_content_is_lang_var;
429    }
430
431    /**
432     * Get ContentIsLangVar.
433     *
434     * @return	boolean
435     */
436    public function getContentIsLangVar()
437    {
438        return $this->content_is_lang_var;
439    }
440
441    /**
442     * Set MobId.
443     *
444     * @param	int	$a_mob_id	Media Object ID (if news includes attachement)
445     */
446    public function setMobId($a_mob_id)
447    {
448        $this->mob_id = $a_mob_id;
449    }
450
451    /**
452     * Get MobId.
453     *
454     * @return	int	Media Object ID (if news includes attachement)
455     */
456    public function getMobId()
457    {
458        return $this->mob_id;
459    }
460
461    /**
462     * Set Playtime.
463     *
464     * @param	string	$a_playtime	Play Time, hh:mm:ss (of attached media file)
465     */
466    public function setPlaytime($a_playtime)
467    {
468        $this->playtime = $a_playtime;
469    }
470
471    /**
472     * Get Playtime.
473     *
474     * @return	string	Play Time, hh:mm:ss (of attached media file)
475     */
476    public function getPlaytime()
477    {
478        return $this->playtime;
479    }
480
481
482    /**
483    * Set Limitation for number of items.
484    *
485    * @param	boolean	$a_limitation	Limitation for number of items
486    */
487    public function setLimitation($a_limitation)
488    {
489        $this->limitation = $a_limitation;
490    }
491
492    /**
493    * Get Limitation for number of items.
494    *
495    * @return	boolean	Limitation for number of items
496    */
497    public function getLimitation()
498    {
499        return $this->limitation;
500    }
501
502    /**
503     * Set content text ist lang var
504     *
505     * @param boolean $a_content_is_lang_var
506     */
507    public function setContentTextIsLangVar($a_val = 0)
508    {
509        $this->content_text_is_lang_var = $a_val;
510    }
511
512    /**
513     * Get content text ist lang var
514     *
515     * @return	boolean
516     */
517    public function getContentTextIsLangVar()
518    {
519        return $this->content_text_is_lang_var;
520    }
521
522    /**
523     * Set mob play counter
524     *
525     * @param int $a_val counter
526     */
527    public function setMobPlayCounter($a_val)
528    {
529        $this->mob_cnt_play = $a_val;
530    }
531
532    /**
533     * Get mob play counter
534     *
535     * @return int counter
536     */
537    public function getMobPlayCounter()
538    {
539        return $this->mob_cnt_play;
540    }
541
542    /**
543     * Set mob download counter
544     *
545     * @param int $a_val counter
546     */
547    public function setMobDownloadCounter($a_val)
548    {
549        $this->mob_cnt_download = $a_val;
550    }
551
552    /**
553     * Get mob download counter
554     *
555     * @return int counter
556     */
557    public function getMobDownloadCounter()
558    {
559        return $this->mob_cnt_download;
560    }
561
562    /**
563     * Is content HTML (tiny used?)
564     *
565     * @param bool $a_val
566     */
567    public function setContentHtml($a_val)
568    {
569        $this->content_html = $a_val;
570    }
571
572    /**
573     * Get content as html
574     *
575     * @return bool
576     */
577    public function getContentHtml()
578    {
579        return $this->content_html;
580    }
581
582    /**
583     * Read item from database.
584     * @deprecated (will migrate to ilNewsData or other class taking care of persistence)
585     */
586    public function read()
587    {
588        $ilDB = $this->db;
589
590        $query = "SELECT * FROM il_news_item WHERE id = " .
591            $ilDB->quote($this->getId(), "integer");
592        $set = $ilDB->query($query);
593        $rec = $ilDB->fetchAssoc($set);
594
595        $this->setTitle($rec["title"]);
596        $this->setContent($rec["content"]);
597        $this->setContextObjId((int) $rec["context_obj_id"]);
598        $this->setContextObjType($rec["context_obj_type"]);
599        $this->setContextSubObjId((int) $rec["context_sub_obj_id"]);
600        $this->setContextSubObjType($rec["context_sub_obj_type"]);
601        $this->setContentType($rec["content_type"]);
602        $this->setCreationDate($rec["creation_date"]);
603        $this->setUpdateDate($rec["update_date"]);
604        $this->setUserId($rec["user_id"]);
605        $this->setUpdateUserId($rec["update_user_id"]);
606        $this->setVisibility($rec["visibility"]);
607        $this->setContentLong($rec["content_long"]);
608        $this->setPriority($rec["priority"]);
609        $this->setContentIsLangVar($rec["content_is_lang_var"]);
610        $this->setContentTextIsLangVar((int) $rec["content_text_is_lang_var"]);
611        $this->setMobId($rec["mob_id"]);
612        $this->setPlaytime($rec["playtime"]);
613        $this->setMobPlayCounter($rec["mob_cnt_play"]);
614        $this->setMobDownloadCounter($rec["mob_cnt_download"]);
615        $this->setContentHtml($rec["content_html"]);
616    }
617
618    /**
619     * Create
620     * @deprecated (will migrate to ilNewsData or other class taking care of persistence)
621     */
622    public function create()
623    {
624        $ilDB = $this->db;
625
626        // insert new record into db
627        $this->setId($ilDB->nextId("il_news_item"));
628        $ilDB->insert("il_news_item", array(
629            "id" => array("integer", $this->getId()),
630            "title" => array("text", $this->getTitle()),
631            "content" => array("clob", $this->getContent()),
632            "content_html" => array("integer", (int) $this->getContentHtml()),
633            "context_obj_id" => array("integer", (int) $this->getContextObjId()),
634            "context_obj_type" => array("text", $this->getContextObjType()),
635            "context_sub_obj_id" => array("integer", (int) $this->getContextSubObjId()),
636            "context_sub_obj_type" => array("text", $this->getContextSubObjType()),
637            "content_type" => array("text", $this->getContentType()),
638            "creation_date" => array("timestamp", ilUtil::now()),
639            "update_date" => array("timestamp", ilUtil::now()),
640            "user_id" => array("integer", $this->getUserId()),
641            "update_user_id" => array("integer", (int) $this->getUpdateUserId()),
642            "visibility" => array("text", $this->getVisibility()),
643            "content_long" => array("clob", $this->getContentLong()),
644            "priority" => array("integer", $this->getPriority()),
645            "content_is_lang_var" => array("integer", $this->getContentIsLangVar()),
646            "content_text_is_lang_var" => array("integer", (int) $this->getContentTextIsLangVar()),
647            "mob_id" => array("integer", $this->getMobId()),
648            "playtime" => array("text", $this->getPlaytime())
649        ));
650
651
652        $news_set = new ilSetting("news");
653        $max_items = $news_set->get("max_items");
654        if ($max_items <= 0) {
655            $max_items = 50;
656        }
657
658        // limit number of news
659        if ($this->getLimitation()) {
660            // Determine how many rows should be deleted
661            $query = "SELECT count(*) cnt " .
662                "FROM il_news_item " .
663                "WHERE " .
664                    "context_obj_id = " . $ilDB->quote($this->getContextObjId(), "integer") .
665                    " AND context_obj_type = " . $ilDB->quote($this->getContextObjType(), "text") .
666                    " AND context_sub_obj_id = " . $ilDB->quote($this->getContextSubObjId(), "integer") .
667                    " AND " . $ilDB->equals("context_sub_obj_type", $this->getContextSubObjType(), "text", true) . " ";
668
669            $set = $ilDB->query($query);
670            $rec = $ilDB->fetchAssoc($set);
671
672            // if we have more records than allowed, delete them
673            if (($rec["cnt"] > $max_items) && $this->getContextObjId() > 0) {
674                $query = "SELECT * " .
675                    "FROM il_news_item " .
676                    "WHERE " .
677                        "context_obj_id = " . $ilDB->quote($this->getContextObjId(), "integer") .
678                        " AND context_obj_type = " . $ilDB->quote($this->getContextObjType(), "text") .
679                        " AND context_sub_obj_id = " . $ilDB->quote($this->getContextSubObjId(), "integer") .
680                        " AND " . $ilDB->equals("context_sub_obj_type", $this->getContextSubObjType(), "text", true) .
681                        " ORDER BY creation_date ASC";
682
683                $ilDB->setLimit($rec["cnt"] - $max_items);
684                $del_set = $ilDB->query($query);
685                while ($del_item = $ilDB->fetchAssoc($del_set)) {
686                    $del_news = new ilNewsItem($del_item["id"]);
687                    $del_news->delete();
688                }
689            }
690        }
691    }
692
693    /**
694     * Update item in database
695     *
696     * @deprecated (will migrate to ilNewsData or other class taking care of persistence)
697     * @param boolean $a_as_new If true, creation date is set "now"
698     */
699    public function update($a_as_new = false)
700    {
701        $ilDB = $this->db;
702
703        $fields = array(
704            "title" => array("text", $this->getTitle()),
705            "content" => array("clob", $this->getContent()),
706            "content_html" => array("integer", (int) $this->getContentHtml()),
707            "context_obj_id" => array("integer", $this->getContextObjId()),
708            "context_obj_type" => array("text", $this->getContextObjType()),
709            "context_sub_obj_id" => array("integer", $this->getContextSubObjId()),
710            "context_sub_obj_type" => array("text", $this->getContextSubObjType()),
711            "content_type" => array("text", $this->getContentType()),
712            "user_id" => array("integer", $this->getUserId()),
713            "update_user_id" => array("integer", (int) $this->getUpdateUserId()),
714            "visibility" => array("text", $this->getVisibility()),
715            "content_long" => array("clob", $this->getContentLong()),
716            "priority" => array("integer", $this->getPriority()),
717            "content_is_lang_var" => array("integer", $this->getContentIsLangVar()),
718            "content_text_is_lang_var" => array("integer", (int) $this->getContentTextIsLangVar()),
719            "mob_id" => array("integer", $this->getMobId()),
720            "mob_cnt_play" => array("integer", $this->getMobPlayCounter()),
721            "mob_cnt_download" => array("integer", $this->getMobDownloadCounter()),
722            "playtime" => array("text", $this->getPlaytime())
723        );
724
725        $now = ilUtil::now();
726        if ($a_as_new) {
727            $fields["creation_date"] = array("timestamp", $now);
728            $fields["update_date"] = array("timestamp", $now);
729        } else {
730            $fields["update_date"] = array("timestamp", $now);
731        }
732
733        $ilDB->update("il_news_item", $fields, array(
734            "id" => array("integer", $this->getId())
735        ));
736    }
737
738
739    /**
740     * Get all news items for a user.
741     * @deprecated (will migrate to ilNewsData)
742     */
743    public static function _getNewsItemsOfUser(
744        $a_user_id,
745        $a_only_public = false,
746        $a_prevent_aggregation = false,
747        $a_per = 0,
748        &$a_cnt = null
749    ) {
750        global $DIC;
751
752        $ilAccess = $DIC->access();
753
754        $fav_rep = new ilFavouritesDBRepository();
755
756        $news_item = new ilNewsItem();
757        $news_set = new ilSetting("news");
758
759        $per = $a_per;
760
761        include_once("./Services/News/classes/class.ilNewsSubscription.php");
762        include_once("./Services/Block/classes/class.ilBlockSetting.php");
763
764        // this is currently not used
765        $ref_ids = ilNewsSubscription::_getSubscriptionsOfUser($a_user_id);
766
767        if (ilObjUser::_lookupPref($a_user_id, "pd_items_news") != "n") {
768            // get all items of the personal desktop
769            $pd_items = $fav_rep->getFavouritesOfUser($a_user_id);
770            foreach ($pd_items as $item) {
771                if (!in_array($item["ref_id"], $ref_ids)) {
772                    $ref_ids[] = $item["ref_id"];
773                }
774            }
775
776            // get all memberships
777            include_once 'Services/Membership/classes/class.ilParticipants.php';
778            $crs_mbs = ilParticipants::_getMembershipByType($a_user_id, 'crs');
779            $grp_mbs = ilParticipants::_getMembershipByType($a_user_id, 'grp');
780            $items = array_merge($crs_mbs, $grp_mbs);
781            foreach ($items as $i) {
782                $item_references = ilObject::_getAllReferences($i);
783                if (is_array($item_references) && count($item_references)) {
784                    foreach ($item_references as $ref_id) {
785                        if (!in_array($ref_id, $ref_ids)) {
786                            $ref_ids[] = $ref_id;
787                        }
788                    }
789                }
790            }
791        }
792
793        $data = array();
794
795        foreach ($ref_ids as $ref_id) {
796            if (!$a_only_public) {
797                // this loop should not cost too much performance
798                $acc = $ilAccess->checkAccessOfUser($a_user_id, "read", "", $ref_id);
799
800                if (!$acc) {
801                    continue;
802                }
803            }
804            if (ilNewsItem::getPrivateFeedId() != false) {
805                global $DIC;
806
807                $rbacsystem = $DIC->rbac()->system();
808                $acc = $rbacsystem->checkAccessOfUser(ilNewsItem::getPrivateFeedId(), "read", $ref_id);
809
810                if (!$acc) {
811                    continue;
812                }
813            }
814
815            $obj_id = ilObject::_lookupObjId($ref_id);
816            $obj_type = ilObject::_lookupType($obj_id);
817            $news = $news_item->getNewsForRefId(
818                $ref_id,
819                $a_only_public,
820                false,
821                $per,
822                $a_prevent_aggregation,
823                false,
824                false,
825                false,
826                $a_user_id
827            );
828
829            // counter
830            if (!is_null($a_cnt)) {
831                $a_cnt[$ref_id] = count($news);
832            }
833
834            $data = ilNewsItem::mergeNews($data, $news);
835        }
836
837        $data = ilUtil::sortArray($data, "creation_date", "desc", false, true);
838
839        return $data;
840    }
841
842    /**
843     * Get News For Ref Id.
844     *
845     * @deprecated (will migrate to ilNewsData)
846     *
847     * @param int $a_ref_id
848     * @param bool $a_only_public
849     * @param bool $a_stopnesting
850     * @param int $a_time_period
851     * @param bool $a_prevent_aggregation
852     * @param bool $a_forum_group_sequences
853     * @param bool $a_no_auto_generated
854     * @param bool $a_ignore_date_filter
855     * @param null $a_user_id
856     * @param int $a_limit currently only supported for groups and courses
857     * @param int[] $a_excluded currently only supported for groups and courses (news ids)
858     * @return array|mixed
859     */
860    public function getNewsForRefId(
861        $a_ref_id,
862        $a_only_public = false,
863        $a_stopnesting = false,
864        $a_time_period = 0,
865        $a_prevent_aggregation = true,
866        $a_forum_group_sequences = false,
867        $a_no_auto_generated = false,
868        $a_ignore_date_filter = false,
869        $a_user_id = null,
870        $a_limit = 0,
871        $a_excluded = array()
872    ) {
873        $obj_id = ilObject::_lookupObjId($a_ref_id);
874        $obj_type = ilObject::_lookupType($obj_id);
875
876        // get starting date
877        $starting_date = "";
878        if ($obj_type == "grp" || $obj_type == "crs") {
879            // see #31471, #30687, and ilMembershipNotification
880            if (!ilContainer::_lookupContainerSetting(
881                $obj_id,
882                'cont_use_news',
883                true
884            ) || (
885                !ilContainer::_lookupContainerSetting(
886                    $obj_id,
887                    'cont_show_news',
888                    true
889                ) && !ilContainer::_lookupContainerSetting(
890                    $obj_id,
891                    'news_timeline'
892                )
893            )) {
894                return [];
895            }
896
897            include_once("./Services/Block/classes/class.ilBlockSetting.php");
898            $hide_news_per_date = ilBlockSetting::_lookup(
899                "news",
900                "hide_news_per_date",
901                0,
902                $obj_id
903            );
904            if ($hide_news_per_date && !$a_ignore_date_filter) {
905                $starting_date = ilBlockSetting::_lookup(
906                    "news",
907                    "hide_news_date",
908                    0,
909                    $obj_id
910                );
911            }
912        }
913
914        if ($obj_type == "cat" && !$a_stopnesting) {
915            $news = $this->getAggregatedChildNewsData(
916                $a_ref_id,
917                $a_only_public,
918                $a_time_period,
919                $a_prevent_aggregation,
920                $starting_date,
921                $a_no_auto_generated
922            );
923        } elseif (($obj_type == "grp" || $obj_type == "crs") &&
924            !$a_stopnesting) {
925            $news = $this->getAggregatedNewsData(
926                $a_ref_id,
927                $a_only_public,
928                $a_time_period,
929                $a_prevent_aggregation,
930                $starting_date,
931                $a_no_auto_generated,
932                $a_user_id,
933                $a_limit,
934                $a_excluded
935            );
936        } else {
937            $news_item = new ilNewsItem();
938            $news_item->setContextObjId($obj_id);
939            $news_item->setContextObjType($obj_type);
940            $news = $news_item->queryNewsForContext(
941                $a_only_public,
942                $a_time_period,
943                $starting_date,
944                $a_no_auto_generated
945            );
946            $unset = array();
947            foreach ($news as $k => $v) {
948                if (!$a_only_public || $v["visibility"] == NEWS_PUBLIC ||
949                    ($v["priority"] == 0 &&
950                        ilBlockSetting::_lookup(
951                            "news",
952                            "public_notifications",
953                            0,
954                            $obj_id
955                        ))) {
956                    $news[$k]["ref_id"] = $a_ref_id;
957                } else {
958                    $unset[] = $k;
959                }
960            }
961            foreach ($unset as $un) {
962                unset($news[$un]);
963            }
964        }
965
966        if (!$a_prevent_aggregation) {
967            $news = $this->aggregateForums($news);
968        } elseif ($a_forum_group_sequences) {
969            $news = $this->aggregateForums($news, true);
970        }
971
972        return $news;
973    }
974
975    /**
976     * Get news aggregation (e.g. for courses, groups)
977     * @deprecated (will migrate to ilNewsData)
978     */
979    public function getAggregatedNewsData(
980        $a_ref_id,
981        $a_only_public = false,
982        $a_time_period = 0,
983        $a_prevent_aggregation = false,
984        $a_starting_date = "",
985        $a_no_auto_generated = false,
986        $a_user_id = null,
987        $a_limit = 0,
988        $a_exclude = array()
989    ) {
990        $tree = $this->tree;
991        $ilAccess = $this->access;
992        $ilObjDataCache = $this->obj_data_cache;
993
994        // get news of parent object
995
996        $data = array();
997
998        // get subtree
999        $cur_node = $tree->getNodeData($a_ref_id);
1000
1001        // do not check for lft (materialized path)
1002        if ($cur_node) {
1003            $nodes = (array) $tree->getSubTree($cur_node, true);
1004        } else {
1005            $nodes = array();
1006        }
1007
1008        // preload object data cache
1009        $ref_ids = array();
1010        $obj_ids = array();
1011        foreach ($nodes as $node) {
1012            $ref_ids[] = $node["child"];
1013            $obj_ids[] = $node["obj_id"];
1014        }
1015
1016        $ilObjDataCache->preloadReferenceCache($ref_ids);
1017        if (!$a_only_public) {
1018            include_once "Services/Object/classes/class.ilObjectActivation.php";
1019            ilObjectActivation::preloadData($ref_ids);
1020        }
1021
1022        // no check, for which of the objects any news are available
1023        $news_obj_ids = ilNewsItem::filterObjIdsPerNews($obj_ids, $a_time_period, $a_starting_date);
1024        //$news_obj_ids = $obj_ids;
1025
1026        // get news for all subtree nodes
1027        $contexts = array();
1028        foreach ($nodes as $node) {
1029            // only go on, if news are available
1030            if (!in_array($node["obj_id"], $news_obj_ids)) {
1031                continue;
1032            }
1033
1034            if (!$a_only_public) {
1035                if (!$a_user_id) {
1036                    $acc = $ilAccess->checkAccess("read", "", $node["child"]);
1037                } else {
1038                    $acc = $ilAccess->checkAccessOfUser(
1039                        $a_user_id,
1040                        "read",
1041                        "",
1042                        $node["child"]
1043                    );
1044                }
1045                if (!$acc) {
1046                    continue;
1047                }
1048            }
1049
1050            $ref_id[$node["obj_id"]] = $node["child"];
1051            $contexts[] = array("obj_id" => $node["obj_id"],
1052                "obj_type" => $node["type"]);
1053        }
1054
1055        // sort and return
1056        $news = $this->queryNewsForMultipleContexts(
1057            $contexts,
1058            $a_only_public,
1059            $a_time_period,
1060            $a_starting_date,
1061            $a_no_auto_generated,
1062            $a_user_id,
1063            $a_limit,
1064            $a_exclude
1065        );
1066
1067        $to_del = array();
1068        foreach ($news as $k => $v) {
1069            $news[$k]["ref_id"] = $ref_id[$v["context_obj_id"]];
1070        }
1071
1072        $data = ilNewsItem::mergeNews($data, $news);
1073        $data = ilUtil::sortArray($data, "creation_date", "desc", false, true);
1074
1075        if (!$a_prevent_aggregation) {
1076            $data = $this->aggregateFiles($data, $a_ref_id);
1077        }
1078
1079        return $data;
1080    }
1081
1082    /**
1083     * @deprecated will move to ilNewsData
1084     * @param $news
1085     * @param bool $a_group_posting_sequence
1086     * @return mixed
1087     */
1088    protected function aggregateForums($news, $a_group_posting_sequence = false)
1089    {
1090        $to_del = array();
1091        $forums = array();
1092
1093        // aggregate
1094        foreach ($news as $k => $v) {
1095            if ($a_group_posting_sequence && $last_aggregation_forum > 0 &&
1096                $last_aggregation_forum != $news[$k]["context_obj_id"]) {
1097                $forums[$last_aggregation_forum] = "";
1098            }
1099
1100            if ($news[$k]["context_obj_type"] == "frm") {
1101                if ($forums[$news[$k]["context_obj_id"]] == "") {
1102                    // $forums[forum_id] = news_id;
1103                    $forums[$news[$k]["context_obj_id"]] = $k;
1104                    $last_aggregation_forum = $news[$k]["context_obj_id"];
1105                } else {
1106                    $to_del[] = $k;
1107                }
1108
1109                $news[$k]["no_context_title"] = true;
1110
1111                // aggregate every forum into it's "k" news
1112                $news[$forums[$news[$k]["context_obj_id"]]]["aggregation"][$k]
1113                    = $news[$k];
1114                $news[$k]["agg_ref_id"]
1115                    = $news[$k]["ref_id"];
1116                $news[$k]["content"] = "";
1117                $news[$k]["content_long"] = "";
1118            }
1119        }
1120
1121        // delete double entries
1122        foreach ($to_del as $k) {
1123            unset($news[$k]);
1124        }
1125        //var_dump($news[14]["aggregation"]);
1126
1127
1128        return $news;
1129    }
1130
1131    /**
1132     * @deprecated will move to ilNewsData
1133     * @param $news
1134     * @param $a_ref_id
1135     * @return mixed
1136     */
1137    protected function aggregateFiles($news, $a_ref_id)
1138    {
1139        $first_file = "";
1140        $to_del = array();
1141        foreach ($news as $k => $v) {
1142            // aggregate file related news
1143            if ($news[$k]["context_obj_type"] == "file") {
1144                if ($first_file == "") {
1145                    $first_file = $k;
1146                } else {
1147                    $to_del[] = $k;
1148                }
1149                $news[$first_file]["aggregation"][$k] = $news[$k];
1150                $news[$first_file]["agg_ref_id"] = $a_ref_id;
1151                $news[$first_file]["ref_id"] = $a_ref_id;
1152            }
1153        }
1154
1155        foreach ($to_del as $v) {
1156            unset($news[$v]);
1157        }
1158
1159        return $news;
1160    }
1161
1162
1163    /**
1164     * Get news aggregation for child objects (e.g. for categories)
1165     * @deprecated will move to ilNewsData
1166     * @param $a_ref_id
1167     * @param bool $a_only_public
1168     * @param int $a_time_period
1169     * @param bool $a_prevent_aggregation
1170     * @param string $a_starting_date
1171     * @param bool $a_no_auto_generated
1172     * @return array|mixed
1173     */
1174    protected function getAggregatedChildNewsData(
1175        $a_ref_id,
1176        $a_only_public = false,
1177        $a_time_period = 0,
1178        $a_prevent_aggregation = false,
1179        $a_starting_date = "",
1180        $a_no_auto_generated = false
1181    ) {
1182        $tree = $this->tree;
1183        $ilAccess = $this->access;
1184
1185        // get news of parent object
1186        $data = $this->getNewsForRefId(
1187            $a_ref_id,
1188            $a_only_public,
1189            true,
1190            $a_time_period,
1191            true,
1192            false,
1193            false,
1194            $a_no_auto_generated
1195        );
1196        foreach ($data as $k => $v) {
1197            $data[$k]["ref_id"] = $a_ref_id;
1198        }
1199
1200        // get childs
1201        $nodes = $tree->getChilds($a_ref_id);
1202
1203        // no check, for which of the objects any news are available
1204        $obj_ids = array();
1205        foreach ($nodes as $node) {
1206            $obj_ids[] = $node["obj_id"];
1207        }
1208        $news_obj_ids = ilNewsItem::filterObjIdsPerNews($obj_ids, $a_time_period, $a_starting_date);
1209        //$news_obj_ids = $obj_ids;
1210
1211        // get news for all subtree nodes
1212        $contexts = array();
1213        foreach ($nodes as $node) {
1214            // only go on, if news are available
1215            if (!in_array($node["obj_id"], $news_obj_ids)) {
1216                continue;
1217            }
1218
1219            if (!$a_only_public && !$ilAccess->checkAccess("read", "", $node["child"])) {
1220                continue;
1221            }
1222            $ref_id[$node["obj_id"]] = $node["child"];
1223            $contexts[] = array("obj_id" => $node["obj_id"],
1224                "obj_type" => $node["type"]);
1225        }
1226
1227        $news = $this->queryNewsForMultipleContexts(
1228            $contexts,
1229            $a_only_public,
1230            $a_time_period,
1231            $a_starting_date,
1232            $a_no_auto_generated
1233        );
1234        foreach ($news as $k => $v) {
1235            $news[$k]["ref_id"] = $ref_id[$v["context_obj_id"]];
1236        }
1237        $data = ilNewsItem::mergeNews($data, $news);
1238
1239        // sort and return
1240        $data = ilUtil::sortArray($data, "creation_date", "desc", false, true);
1241
1242        if (!$a_prevent_aggregation) {
1243            $data = $this->aggregateFiles($data, $a_ref_id);
1244        }
1245
1246        return $data;
1247    }
1248
1249    /**
1250     * Set context for news
1251     *
1252     * @param int $a_obj_id
1253     * @param int $a_obj_type
1254     * @param int $a_sub_obj_id
1255     * @param string $a_sub_obj_type
1256     */
1257    public function setContext(int $a_obj_id, string $a_obj_type, int $a_sub_obj_id = 0, string $a_sub_obj_type = "")
1258    {
1259        $this->setContextObjId($a_obj_id);
1260        $this->setContextObjType($a_obj_type);
1261        $this->setContextSubObjId($a_sub_obj_id);
1262        $this->setContextSubObjType($a_sub_obj_type);
1263    }
1264
1265    /**
1266     * Convert time period for DB-queries
1267     *
1268     * @param mixed $a_time_period
1269     * @return string
1270     */
1271    protected static function handleTimePeriod($a_time_period)
1272    {
1273        // time period is number of days
1274        if (is_numeric($a_time_period)) {
1275            if ($a_time_period > 0) {
1276                return date('Y-m-d H:i:s', time() - ($a_time_period * 24 * 60 * 60));
1277            }
1278        }
1279        // time period is datetime
1280        elseif (preg_match("/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/", $a_time_period)) {
1281            return $a_time_period;
1282        }
1283        // :TODO: what to return?
1284    }
1285
1286    /**
1287     * Query news for a context
1288     *
1289     * @deprecated will move to ilNewsData
1290     * @param    boolean        query for outgoing rss feed
1291     * @param    int            time period in seconds
1292     * @param    string        startind date
1293     * @param    boolean        do not include auto generated news items
1294     */
1295    public function queryNewsForContext(
1296        $a_for_rss_use = false,
1297        $a_time_period = 0,
1298        $a_starting_date = "",
1299        $a_no_auto_generated = false,
1300        $a_oldest_first = false,
1301        $a_limit = 0
1302    ) {
1303        $ilDB = $this->db;
1304        $ilUser = $this->user;
1305        $lng = $this->lng;
1306
1307        $and = "";
1308        if ($a_time_period > 0) {
1309            $limit_ts = self::handleTimePeriod($a_time_period);
1310            $and = " AND creation_date >= " . $ilDB->quote($limit_ts, "timestamp") . " ";
1311        }
1312
1313        if ($a_starting_date != "") {
1314            $and .= " AND creation_date > " . $ilDB->quote($a_starting_date, "timestamp") . " ";
1315        }
1316
1317        if ($a_no_auto_generated) {
1318            $and .= " AND priority = 1 AND content_type = " . $ilDB->quote("text", "text") . " ";
1319        }
1320
1321        // this is changed with 4.1 (news table for lm pages)
1322        if ($this->getContextSubObjId() > 0) {
1323            $and .= " AND context_sub_obj_id = " . $ilDB->quote($this->getContextSubObjId(), "integer") .
1324                " AND context_sub_obj_type = " . $ilDB->quote($this->getContextSubObjType(), "text");
1325        }
1326
1327        $ordering = ($a_oldest_first)
1328            ? " creation_date ASC, id ASC "
1329            : " creation_date DESC, id DESC ";
1330
1331        if ($a_for_rss_use && ilNewsItem::getPrivateFeedId() == false) {
1332            $query = "SELECT * " .
1333                "FROM il_news_item " .
1334                " WHERE " .
1335                    "context_obj_id = " . $ilDB->quote($this->getContextObjId(), "integer") .
1336                    " AND context_obj_type = " . $ilDB->quote($this->getContextObjType(), "text") .
1337                    $and .
1338                    " ORDER BY " . $ordering;
1339        } elseif (ilNewsItem::getPrivateFeedId() != false) {
1340            $query = "SELECT il_news_item.* " .
1341                ", il_news_read.user_id user_read " .
1342                "FROM il_news_item LEFT JOIN il_news_read " .
1343                "ON il_news_item.id = il_news_read.news_id AND " .
1344                " il_news_read.user_id = " . $ilDB->quote(ilNewsItem::getPrivateFeedId(), "integer") .
1345                " WHERE " .
1346                    "context_obj_id = " . $ilDB->quote($this->getContextObjId(), "integer") .
1347                    " AND context_obj_type = " . $ilDB->quote($this->getContextObjType(), "text") .
1348                    $and .
1349                    " ORDER BY " . $ordering;
1350        } else {
1351            $query = "SELECT il_news_item.* " .
1352                ", il_news_read.user_id as user_read " .
1353                "FROM il_news_item LEFT JOIN il_news_read " .
1354                "ON il_news_item.id = il_news_read.news_id AND " .
1355                " il_news_read.user_id = " . $ilDB->quote($ilUser->getId(), "integer") .
1356                " WHERE " .
1357                    "context_obj_id = " . $ilDB->quote($this->getContextObjId(), "integer") .
1358                    " AND context_obj_type = " . $ilDB->quote($this->getContextObjType(), "text") .
1359                    $and .
1360                    " ORDER BY " . $ordering;
1361        }
1362        //echo $query;
1363        $set = $ilDB->query($query);
1364        $result = array();
1365        while ($rec = $ilDB->fetchAssoc($set)) {
1366            if ($a_limit > 0 && count($result) >= $a_limit) {
1367                continue;
1368            }
1369            if (!$a_for_rss_use || (ilNewsItem::getPrivateFeedId() != false) || ($rec["visibility"] == NEWS_PUBLIC ||
1370                ($rec["priority"] == 0 &&
1371                ilBlockSetting::_lookup(
1372                    "news",
1373                    "public_notifications",
1374                    0,
1375                    $rec["context_obj_id"]
1376                )))) {
1377                $result[$rec["id"]] = $rec;
1378            }
1379        }
1380
1381        // do we get data for rss and may the time limit by an issue?
1382        // do a second query without time limit.
1383        // this is not very performant, but I do not have a better
1384        // idea. The keep_rss_min setting is currently (Jul 2012) only set
1385        // by mediacasts
1386        if ($a_time_period != "" && $a_for_rss_use) {
1387            include_once("./Services/Block/classes/class.ilBlockSetting.php");
1388            $keep_rss_min = ilBlockSetting::_lookup(
1389                "news",
1390                "keep_rss_min",
1391                0,
1392                $this->getContextObjId()
1393            );
1394            if ($keep_rss_min > 0) {
1395                return $this->queryNewsForContext(
1396                    true,
1397                    0,
1398                    $a_starting_date,
1399                    $a_no_auto_generated,
1400                    $a_oldest_first,
1401                    $keep_rss_min
1402                );
1403            }
1404        }
1405
1406        return $result;
1407    }
1408
1409    /**
1410     * Query news data by news ids
1411     *
1412     * @param int[] $a_news_ids
1413     * @return array[]
1414     */
1415    public static function queryNewsByIds(array $a_news_ids)
1416    {
1417        global $DIC;
1418        $ilDB = $DIC->database();
1419        $news = array();
1420        $set = $ilDB->query("SELECT * FROM il_news_item " .
1421            " WHERE " . $ilDB->in("id", $a_news_ids, false, "integer"));
1422        while ($rec = $ilDB->fetchAssoc($set)) {
1423            $news[$rec["id"]] = $rec;
1424        }
1425        return $news;
1426    }
1427
1428    /**
1429     *
1430     * @deprecated will move to ilNewsData
1431     * @param int $a_ref_id
1432     * @param int $a_time_period hours
1433     * @return array news item ids
1434     */
1435    public function checkNewsExistsForObjects($objects, $a_time_period = 1)
1436    {
1437        $ilDB = $this->db;
1438
1439        $all = array();
1440
1441        $limit_ts = self::handleTimePeriod($a_time_period);
1442
1443        // are there any news items for relevant objects and?
1444        $query = $ilDB->query("SELECT id,context_obj_id,context_obj_type" .
1445            " FROM il_news_item" .
1446            " WHERE " . $ilDB->in("context_obj_id", array_keys($objects), false, "integer") .
1447            " AND creation_date >= " . $ilDB->quote($limit_ts, "timestamp"));
1448        while ($rec = $ilDB->fetchAssoc($query)) {
1449            if ($objects[$rec["context_obj_id"]]["type"] == $rec["context_obj_type"]) {
1450                $all[] = $rec["id"];
1451            }
1452        }
1453
1454        return $all;
1455    }
1456
1457    /**
1458     * Query News for multiple Contexts
1459     * @deprecated will move to ilNewsData
1460     * @param array $a_contexts
1461     * @param bool $a_for_rss_use
1462     * @param int $a_time_period
1463     * @param string $a_starting_date
1464     * @param bool $a_no_auto_generated
1465     * @param null $a_user_id
1466     * @param int $a_limit
1467     * @param int[] $a_exclude
1468     * @return array
1469     */
1470    public function queryNewsForMultipleContexts(
1471        $a_contexts,
1472        $a_for_rss_use = false,
1473        $a_time_period = 0,
1474        $a_starting_date = "",
1475        $a_no_auto_generated = false,
1476        $a_user_id = null,
1477        $a_limit = 0,
1478        $a_exclude = array()
1479    ) {
1480        $ilDB = $this->db;
1481        $ilUser = $this->user;
1482        $lng = $this->lng;
1483        $ilCtrl = $this->ctrl;
1484
1485        $and = "";
1486        if ($a_time_period > 0) {
1487            $limit_ts = self::handleTimePeriod($a_time_period);
1488            $and = " AND creation_date >= " . $ilDB->quote($limit_ts, "timestamp") . " ";
1489        }
1490
1491        if ($a_starting_date != "") {
1492            $and .= " AND creation_date > " . $ilDB->quote($a_starting_date, "timestamp") . " ";
1493        }
1494
1495        if ($a_no_auto_generated) {
1496            $and .= " AND priority = 1 AND content_type = " . $ilDB->quote("text", "text") . " ";
1497        }
1498
1499        if ($a_limit > 0) {
1500            $ilDB->setLimit($a_limit, 0);
1501        }
1502
1503        if (is_array($a_exclude) && count($a_exclude) > 0) {
1504            $and .= " AND " . $ilDB->in("id", $a_exclude, true, "integer") . " ";
1505        }
1506
1507        $ids = array();
1508        $type = array();
1509
1510        foreach ($a_contexts as $cont) {
1511            $ids[] = $cont["obj_id"];
1512            $type[$cont["obj_id"]] = $cont["obj_type"];
1513        }
1514
1515        if ($a_for_rss_use && ilNewsItem::getPrivateFeedId() == false) {
1516            $query = "SELECT * " .
1517                "FROM il_news_item " .
1518                " WHERE " .
1519                    $ilDB->in("context_obj_id", $ids, false, "integer") . " " .
1520                    $and .
1521                    " ORDER BY creation_date DESC ";
1522        } elseif (ilNewsItem::getPrivateFeedId() != false) {
1523            $query = "SELECT il_news_item.* " .
1524                ", il_news_read.user_id as user_read " .
1525                "FROM il_news_item LEFT JOIN il_news_read " .
1526                "ON il_news_item.id = il_news_read.news_id AND " .
1527                " il_news_read.user_id = " . $ilDB->quote(ilNewsItem::getPrivateFeedId(), "integer") .
1528                " WHERE " .
1529                    $ilDB->in("context_obj_id", $ids, false, "integer") . " " .
1530                    $and .
1531                    " ORDER BY creation_date DESC ";
1532        } else {
1533            if ($a_user_id) {
1534                $user_id = $a_user_id;
1535            } else {
1536                $user_id = $ilUser->getId();
1537            }
1538            $query = "SELECT il_news_item.* " .
1539                ", il_news_read.user_id as user_read " .
1540                "FROM il_news_item LEFT JOIN il_news_read " .
1541                "ON il_news_item.id = il_news_read.news_id AND " .
1542                " il_news_read.user_id = " . $ilDB->quote($user_id, "integer") .
1543                " WHERE " .
1544                    $ilDB->in("context_obj_id", $ids, false, "integer") . " " .
1545                    $and .
1546                    " ORDER BY creation_date DESC ";
1547        }
1548
1549        $set = $ilDB->query($query);
1550        $result = array();
1551        while ($rec = $ilDB->fetchAssoc($set)) {
1552            if ($type[$rec["context_obj_id"]] == $rec["context_obj_type"]) {
1553                if (!$a_for_rss_use || ilNewsItem::getPrivateFeedId() != false || ($rec["visibility"] == NEWS_PUBLIC ||
1554                    ($rec["priority"] == 0 &&
1555                    ilBlockSetting::_lookup(
1556                        "news",
1557                        "public_notifications",
1558                        0,
1559                        $rec["context_obj_id"]
1560                    )))) {
1561                    $result[$rec["id"]] = $rec;
1562                }
1563            }
1564        }
1565
1566        return $result;
1567    }
1568
1569
1570    /**
1571     * Set item read.
1572     * @deprecated will move to ilNewsData
1573     */
1574    public static function _setRead($a_user_id, $a_news_id)
1575    {
1576        global $DIC;
1577
1578        $ilDB = $DIC->database();
1579        $ilAppEventHandler = $DIC["ilAppEventHandler"];
1580
1581        $ilDB->replace(
1582            "il_news_read",
1583            array(
1584                "user_id" => array("integer", $a_user_id),
1585                "news_id" => array("integer", $a_news_id)
1586                ),
1587            array()
1588            );
1589
1590        /*
1591        $ilDB->manipulate("DELETE FROM il_news_read WHERE ".
1592            "user_id = ".$ilDB->quote($a_user_id, "integer").
1593            " AND news_id = ".$ilDB->quote($a_news_id, "integer"));
1594        $ilDB->manipulate("INSERT INTO il_news_read (user_id, news_id) VALUES (".
1595            $ilDB->quote($a_user_id, "integer").",".
1596            $ilDB->quote($a_news_id, "integer").")");*/
1597
1598        $ilAppEventHandler->raise(
1599            "Services/News",
1600            "readNews",
1601            array("user_id" => $a_user_id, "news_ids" => array($a_news_id))
1602        );
1603    }
1604
1605    /**
1606     * Set item unread.
1607     * @deprecated will move to ilNewsData
1608     */
1609    public static function _setUnread($a_user_id, $a_news_id)
1610    {
1611        global $DIC;
1612
1613        $ilDB = $DIC->database();
1614        $ilAppEventHandler = $DIC["ilAppEventHandler"];
1615
1616        $ilDB->manipulate("DELETE FROM il_news_read (user_id, news_id) VALUES (" .
1617            " WHERE user_id = " . $ilDB->quote($a_user_id, "integer") .
1618            " AND news_id = " . $ilDB->quote($a_news_id, "integer"));
1619
1620        $ilAppEventHandler->raise(
1621            "Services/News",
1622            "unreadNews",
1623            array("user_id" => $a_user_id, "news_ids" => array($a_news_id))
1624        );
1625    }
1626
1627    /**
1628     * Merges two sets of news
1629     * @deprecated will move to ilNewsData
1630     * @param	array	$n1		Array of news
1631     * @param	array	$n2		Array of news
1632     *
1633     * @return	array			Array of news
1634     */
1635    public static function mergeNews($n1, $n2)
1636    {
1637        foreach ($n2 as $id => $news) {
1638            $n1[$id] = $news;
1639        }
1640
1641        return $n1;
1642    }
1643
1644    /**
1645     * Get default visibility for reference id
1646     * @deprecated will move to ilNewsData
1647     * @param	$a_ref_id		reference id
1648     */
1649    public static function _getDefaultVisibilityForRefId($a_ref_id)
1650    {
1651        global $DIC;
1652
1653        $tree = $DIC->repositoryTree();
1654        $ilSetting = $DIC->settings();
1655
1656        include_once("./Services/Block/classes/class.ilBlockSetting.php");
1657
1658        $news_set = new ilSetting("news");
1659        $default_visibility = ($news_set->get("default_visibility") != "")
1660                ? $news_set->get("default_visibility")
1661                : "users";
1662
1663        if ($tree->isInTree($a_ref_id)) {
1664            $path = $tree->getPathFull($a_ref_id);
1665
1666            foreach ($path as $key => $row) {
1667                if (!in_array($row["type"], array("root", "cat","crs", "fold", "grp"))) {
1668                    continue;
1669                }
1670
1671                $visibility = ilBlockSetting::_lookup(
1672                    "news",
1673                    "default_visibility",
1674                    0,
1675                    $row["obj_id"]
1676                );
1677
1678                if ($visibility != "") {
1679                    $default_visibility = $visibility;
1680                }
1681            }
1682        }
1683
1684        return $default_visibility;
1685    }
1686
1687
1688    /**
1689     * Delete news item
1690     * @deprecated will move to ilNewsData
1691     */
1692    public function delete()
1693    {
1694        $ilDB = $this->db;
1695
1696        // delete il_news_read entries
1697        $ilDB->manipulate("DELETE FROM il_news_read " .
1698            " WHERE news_id = " . $ilDB->quote($this->getId(), "integer"));
1699
1700        // delete multimedia object
1701        $mob = $this->getMobId();
1702
1703        // delete
1704        $query = "DELETE FROM il_news_item" .
1705            " WHERE id = " . $ilDB->quote($this->getId(), "integer");
1706        $ilDB->manipulate($query);
1707
1708        // delete mob after news, to have a "mob usage" of 0
1709        if ($mob > 0 and ilObject::_exists($mob)) {
1710            include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
1711            $mob = new ilObjMediaObject($mob);
1712            $mob->delete();
1713        }
1714    }
1715
1716    /**
1717     * Get all news of a context
1718     * @deprecated will move to ilNewsData
1719     */
1720    public static function getNewsOfContext(
1721        $a_context_obj_id,
1722        $a_context_obj_type,
1723        $a_context_sub_obj_id = 0,
1724        $a_context_sub_obj_type = ""
1725    ) {
1726        global $DIC;
1727
1728        $ilDB = $DIC->database();
1729
1730        if ($a_context_obj_id == 0 || $a_context_obj_type == "") {
1731            return [];
1732        }
1733
1734        if ($a_context_sub_obj_id > 0) {
1735            $and = " AND context_sub_obj_id = " . $ilDB->quote($a_context_sub_obj_id, "integer") .
1736                " AND context_sub_obj_type = " . $ilDB->quote($a_context_sub_obj_type, "text");
1737        }
1738
1739        // get news records
1740        $query = "SELECT * FROM il_news_item" .
1741            " WHERE context_obj_id = " . $ilDB->quote($a_context_obj_id, "integer") .
1742            " AND context_obj_type = " . $ilDB->quote($a_context_obj_type, "text") .
1743            $and;
1744
1745        $news_set = $ilDB->query($query);
1746
1747        $news_arr = [];
1748        while ($news = $ilDB->fetchAssoc($news_set)) {
1749            $news_arr[] = new ilNewsItem($news["id"]);
1750        }
1751        return $news_arr;
1752    }
1753
1754    /**
1755     * Delete all news of a context
1756     * @deprecated will move to ilNewsData
1757     */
1758    public static function deleteNewsOfContext(
1759        $a_context_obj_id,
1760        $a_context_obj_type,
1761        $a_context_sub_obj_id = 0,
1762        $a_context_sub_obj_type = ""
1763    ) {
1764        foreach (self::getNewsOfContext(
1765            $a_context_obj_id,
1766            $a_context_obj_type,
1767            $a_context_sub_obj_id,
1768            $a_context_sub_obj_type
1769        ) as $n) {
1770            $n->delete();
1771        }
1772    }
1773
1774    /**
1775     * Lookup News Title
1776     * @deprecated will move to ilNewsData
1777     */
1778    public static function _lookupTitle($a_news_id)
1779    {
1780        global $DIC;
1781
1782        $ilDB = $DIC->database();
1783
1784        $query = "SELECT title FROM il_news_item WHERE id = " .
1785            $ilDB->quote($a_news_id, "integer");
1786        $set = $ilDB->query($query);
1787        $rec = $ilDB->fetchAssoc($set);
1788        return $rec["title"];
1789    }
1790
1791    /**
1792     * Lookup News Visibility
1793     * @deprecated will move to ilNewsData
1794     */
1795    public static function _lookupVisibility($a_news_id)
1796    {
1797        global $DIC;
1798
1799        $ilDB = $DIC->database();
1800
1801        $query = "SELECT visibility FROM il_news_item WHERE id = " .
1802            $ilDB->quote($a_news_id, "integer");
1803        $set = $ilDB->query($query);
1804        $rec = $ilDB->fetchAssoc($set);
1805
1806        return $rec["visibility"];
1807    }
1808
1809    /**
1810     * Lookup mob id
1811     * @deprecated will move to ilNewsData
1812     */
1813    public static function _lookupMobId($a_news_id)
1814    {
1815        global $DIC;
1816
1817        $ilDB = $DIC->database();
1818
1819        $query = "SELECT mob_id FROM il_news_item WHERE id = " .
1820            $ilDB->quote($a_news_id, "integer");
1821        $set = $ilDB->query($query);
1822        $rec = $ilDB->fetchAssoc($set);
1823        return $rec["mob_id"];
1824    }
1825
1826    /**
1827     * Checks whether news are available for
1828     * @deprecated will move to ilNewsData
1829     */
1830    public static function filterObjIdsPerNews($a_obj_ids, $a_time_period = 0, $a_starting_date = "", $a_ending_date = '', $ignore_period = false)
1831    {
1832        global $DIC;
1833
1834        $ilDB = $DIC->database();
1835
1836        $and = "";
1837        if ($a_time_period > 0) {
1838            $limit_ts = self::handleTimePeriod($a_time_period);
1839            $and = " AND creation_date >= " . $ilDB->quote($limit_ts, "timestamp") . " ";
1840        }
1841
1842        if ($a_starting_date != "") {
1843            $and .= " AND creation_date >= " . $ilDB->quote($a_starting_date, "timestamp");
1844        }
1845
1846        $query = "SELECT DISTINCT(context_obj_id) AS obj_id FROM il_news_item" .
1847            " WHERE " . $ilDB->in("context_obj_id", $a_obj_ids, false, "integer") . " " . $and;
1848        //" WHERE context_obj_id IN (".implode(ilUtil::quoteArray($a_obj_ids),",").")".$and;
1849
1850        $set = $ilDB->query($query);
1851        $objs = array();
1852        while ($rec = $ilDB->fetchAssoc($set)) {
1853            $objs[] = $rec["obj_id"];
1854        }
1855
1856        return $objs;
1857    }
1858
1859    /**
1860     * Determine title for news item entry
1861     */
1862    public static function determineNewsTitleByNewsId($a_news_id, $a_agg_ref_id = 0, $a_aggregation = "")
1863    {
1864        global $DIC;
1865
1866        $ilDB = $DIC->database();
1867
1868        $query = "SELECT context_obj_type, content_is_lang_var, title FROM il_news_item WHERE id = " .
1869            $ilDB->quote($a_news_id, "integer");
1870        $set = $ilDB->query($query);
1871        $rec = $ilDB->fetchAssoc($set);
1872
1873        return self::determineNewsTitle(
1874            $rec["context_obj_type"],
1875            $rec["title"],
1876            $rec["content_is_lang_var"],
1877            $a_agg_ref_id,
1878            $a_aggregation
1879        );
1880    }
1881
1882    /**
1883     * Determine title for news item entry
1884     * @deprecated will move to util?
1885     */
1886    public static function determineNewsTitle(
1887        $a_context_obj_type,
1888        $a_title,
1889        $a_content_is_lang_var,
1890        $a_agg_ref_id = 0,
1891        $a_aggregation = ""
1892    ) {
1893        global $DIC;
1894
1895        $lng = $DIC->language();
1896        $obj_definition = $DIC["objDefinition"];
1897
1898        if ($a_agg_ref_id > 0) {
1899            $cnt = count($a_aggregation);
1900
1901            // forums
1902            if ($a_context_obj_type == "frm") {
1903                if ($cnt > 1) {
1904                    return sprintf($lng->txt("news_x_postings"), $cnt);
1905                } else {
1906                    return $lng->txt("news_1_postings");
1907                }
1908            } else {	// files
1909                $up_cnt = $cr_cnt = 0;
1910                foreach ($a_aggregation as $item) {
1911                    if ($item["title"] == "file_updated") {
1912                        $up_cnt++;
1913                    } else {
1914                        $cr_cnt++;
1915                    }
1916                }
1917                $sep = "";
1918                if ($cr_cnt == 1) {
1919                    $tit = $lng->txt("news_1_file_created");
1920                    $sep = "<br />";
1921                } elseif ($cr_cnt > 1) {
1922                    $tit = sprintf($lng->txt("news_x_files_created"), $cr_cnt);
1923                    $sep = "<br />";
1924                }
1925                if ($up_cnt == 1) {
1926                    $tit .= $sep . $lng->txt("news_1_file_updated");
1927                } elseif ($up_cnt > 1) {
1928                    $tit .= $sep . sprintf($lng->txt("news_x_files_updated"), $up_cnt);
1929                }
1930                return $tit;
1931            }
1932        } else {
1933            if ($a_content_is_lang_var) {
1934                if ($obj_definition->isPlugin($a_context_obj_type)) {
1935                    return ilObjectPlugin::lookupTxtById($a_context_obj_type, $a_title);
1936                }
1937                return $lng->txt($a_title);
1938            } else {
1939                return $a_title;
1940            }
1941        }
1942
1943        return "";
1944    }
1945
1946    /**
1947     * Determine new content
1948     * @deprecated will move to util?
1949     */
1950    public static function determineNewsContent($a_context_obj_type, $a_content, $a_is_lang_var)
1951    {
1952        global $DIC;
1953
1954        $lng = $DIC->language();
1955        $obj_definition = $DIC["objDefinition"];
1956
1957        if ($a_is_lang_var) {
1958            if ($obj_definition->isPlugin($a_context_obj_type)) {
1959                return ilObjectPlugin::lookupTxtById($a_context_obj_type, $a_content);
1960            }
1961            $lng->loadLanguageModule($a_context_obj_type);
1962            return $lng->txt($a_content);
1963        } else {
1964            return $a_content;
1965        }
1966    }
1967
1968
1969
1970    /**
1971     * Get first new id of news set related to a certain context
1972     * @deprecated will move to ilNewsData
1973     */
1974    public static function getFirstNewsIdForContext(
1975        $a_context_obj_id,
1976        $a_context_obj_type,
1977        $a_context_sub_obj_id = "",
1978        $a_context_sub_obj_type = ""
1979    ) {
1980        global $DIC;
1981
1982        $ilDB = $DIC->database();
1983
1984        // Determine how many rows should be deleted
1985        $query = "SELECT * " .
1986            "FROM il_news_item " .
1987            "WHERE " .
1988                "context_obj_id = " . $ilDB->quote($a_context_obj_id, "integer") .
1989                " AND context_obj_type = " . $ilDB->quote($a_context_obj_type, "text") .
1990                " AND context_sub_obj_id = " . $ilDB->quote($a_context_sub_obj_id, "integer") .
1991                " AND " . $ilDB->equals("context_sub_obj_type", $a_context_sub_obj_type, "text", true);
1992
1993        $set = $ilDB->query($query);
1994        $rec = $ilDB->fetchAssoc($set);
1995
1996        return $rec["id"];
1997    }
1998
1999    /**
2000     * Get last news id of news set related to a certain context
2001     * @deprecated will move to ilNewsData
2002     */
2003    public static function getLastNewsIdForContext(
2004        $a_context_obj_id,
2005        $a_context_obj_type,
2006        $a_context_sub_obj_id = "",
2007        $a_context_sub_obj_type = "",
2008        $a_only_today = false
2009    ) {
2010        global $DIC;
2011
2012        $ilDB = $DIC->database();
2013
2014        // Determine how many rows should be deleted
2015        $query = "SELECT id, update_date " .
2016            "FROM il_news_item " .
2017            "WHERE " .
2018                "context_obj_id = " . $ilDB->quote($a_context_obj_id, "integer") .
2019                " AND context_obj_type = " . $ilDB->quote($a_context_obj_type, "text") .
2020                " AND context_sub_obj_id = " . $ilDB->quote($a_context_sub_obj_id, "integer") .
2021                " AND " . $ilDB->equals("context_sub_obj_type", $a_context_sub_obj_type, "text", true) .
2022            " ORDER BY update_date DESC";
2023
2024        $ilDB->setLimit(1);
2025        $set = $ilDB->query($query);
2026        $rec = $ilDB->fetchAssoc($set);
2027
2028        $id = (int) $rec["id"];
2029        if ($a_only_today) {
2030            $now = ilUtil::now();
2031            if (substr($now, 0, 10) != substr($rec["update_date"], 0, 10)) {
2032                $id = 0;
2033            }
2034        }
2035
2036        return $id;
2037    }
2038
2039
2040    /**
2041     * Lookup media object usage(s)
2042     * @deprecated will move to ilNewsData
2043     */
2044    public static function _lookupMediaObjectUsages($a_mob_id)
2045    {
2046        global $DIC;
2047
2048        $ilDB = $DIC->database();
2049
2050        $query = "SELECT * " .
2051            "FROM il_news_item " .
2052            "WHERE " .
2053                " mob_id = " . $ilDB->quote($a_mob_id, "integer");
2054
2055        $usages = array();
2056        $set = $ilDB->query($query);
2057        while ($rec = $ilDB->fetchAssoc($set)) {
2058            $usages[$rec["id"]] = array("type" => "news", "id" => $rec["id"]);
2059        }
2060
2061        return $usages;
2062    }
2063
2064    /**
2065     * Context Object ID
2066     * @deprecated will move to ilNewsData
2067     */
2068    public static function _lookupContextObjId($a_news_id)
2069    {
2070        global $DIC;
2071
2072        $ilDB = $DIC->database();
2073
2074        $query = "SELECT * " .
2075            "FROM il_news_item " .
2076            "WHERE " .
2077                " id = " . $ilDB->quote($a_news_id, "integer");
2078        $set = $ilDB->query($query);
2079        $rec = $ilDB->fetchAssoc($set);
2080
2081        return $rec["context_obj_id"];
2082    }
2083
2084    /**
2085     * @deprecated will move to settings
2086     */
2087    public static function _lookupDefaultPDPeriod()
2088    {
2089        $news_set = new ilSetting("news");
2090        $per = $news_set->get("pd_period");
2091        if ($per == 0) {
2092            $per = 30;
2093        }
2094
2095        return $per;
2096    }
2097
2098    /**
2099     * @deprecated will move to settings->user
2100     */
2101    public static function _lookupUserPDPeriod($a_user_id)
2102    {
2103        $news_set = new ilSetting("news");
2104        $allow_shorter_periods = $news_set->get("allow_shorter_periods");
2105        $allow_longer_periods = $news_set->get("allow_longer_periods");
2106        $default_per = ilNewsItem::_lookupDefaultPDPeriod();
2107
2108        include_once("./Services/Block/classes/class.ilBlockSetting.php");
2109        $per = ilBlockSetting::_lookup(
2110            "pdnews",
2111            "news_pd_period",
2112            $a_user_id,
2113            0
2114        );
2115
2116        // news period information
2117        if ($per <= 0 ||
2118            (!$allow_shorter_periods && ($per < $default_per)) ||
2119            (!$allow_longer_periods && ($per > $default_per))
2120            ) {
2121            $per = $default_per;
2122        }
2123
2124        return $per;
2125    }
2126
2127    /**
2128     * @deprecated will move to settings
2129     */
2130    public static function _lookupRSSPeriod()
2131    {
2132        $news_set = new ilSetting("news");
2133        $rss_period = $news_set->get("rss_period");
2134        if ($rss_period == 0) {		// default to two weeks
2135            $rss_period = 14;
2136        }
2137        return $rss_period;
2138    }
2139
2140    /**
2141     * @deprecated will move to settings->user
2142     */
2143    public static function setPrivateFeedId($a_userId)
2144    {
2145        ilNewsItem::$privFeedId = $a_userId;
2146    }
2147
2148    /**
2149     * @deprecated will move to settings->user
2150     */
2151    public static function getPrivateFeedId()
2152    {
2153        return ilNewsItem::$privFeedId;
2154    }
2155
2156    /**
2157     * Deliver mob file
2158     *
2159     * @deprecated will move to ?
2160     * @param string $a_purpose
2161     * @param bool $a_increase_download_cnt
2162     * @return bool
2163     */
2164    public function deliverMobFile($a_purpose = "Standard", $a_increase_download_cnt = false)
2165    {
2166        $mob = $this->getMobId();
2167        include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
2168        $mob = new ilObjMediaObject($mob);
2169        $mob_dir = ilObjMediaObject::_getDirectory($mob->getId());
2170
2171        // check purpose
2172        if (!$mob->hasPurposeItem($a_purpose)) {
2173            return false;
2174        }
2175
2176        $m_item = $mob->getMediaItem($a_purpose);
2177        if ($m_item->getLocationType() != "Reference") {
2178            $file = $mob_dir . "/" . $m_item->getLocation();
2179            if (file_exists($file) && is_file($file)) {
2180                if ($a_increase_download_cnt) {
2181                    $this->increaseDownloadCounter();
2182                }
2183                ilUtil::deliverFile($file, $m_item->getLocation(), "", false, false, false);
2184                return true;
2185            } else {
2186                ilUtil::sendFailure("File not found!", true);
2187                return false;
2188            }
2189        } else {
2190            if ($a_increase_download_cnt) {
2191                $this->increaseDownloadCounter();
2192            }
2193            ilUtil::redirect($m_item->getLocation());
2194        }
2195    }
2196
2197    /**
2198     * Increase download counter
2199     * @deprecated will move to data
2200     */
2201    public function increaseDownloadCounter()
2202    {
2203        $ilDB = $this->db;
2204
2205        $cnt = $this->getMobDownloadCounter();
2206        $cnt++;
2207        $this->setMobDownloadCounter($cnt);
2208        $ilDB->manipulate(
2209            "UPDATE il_news_item SET " .
2210            " mob_cnt_download = " . $ilDB->quote($cnt, "integer") .
2211            " WHERE id = " . $ilDB->quote($this->getId(), "integer")
2212            );
2213    }
2214
2215    /**
2216     * Increase play counter
2217     *
2218     * @deprecated will move to data
2219     */
2220    public function increasePlayCounter()
2221    {
2222        $ilDB = $this->db;
2223
2224        $cnt = $this->getMobPlayCounter();
2225        $cnt++;
2226        $this->setMobPlayCounter($cnt);
2227        $ilDB->manipulate(
2228            "UPDATE il_news_item SET " .
2229            " mob_cnt_play = " . $ilDB->quote($cnt, "integer") .
2230            " WHERE id = " . $ilDB->quote($this->getId(), "integer")
2231            );
2232    }
2233
2234    /**
2235     * Prepare news data from cache
2236     * @deprecated will move to data
2237     * @param string $a_cres cache string
2238     * @return array news array
2239     */
2240    public static function prepareNewsDataFromCache($a_cres)
2241    {
2242        global $DIC;
2243
2244        $ilDB = $DIC->database();
2245
2246        $data = $a_cres;
2247        $news_ids = array_keys($data);
2248        $set = $ilDB->query("SELECT id FROM il_news_item " .
2249            " WHERE " . $ilDB->in("id", $news_ids, false, "integer"));
2250        $existing_ids = array();
2251        while ($rec = $ilDB->fetchAssoc($set)) {
2252            $existing_ids[] = $rec["id"];
2253        }
2254        //var_dump($existing_ids);
2255        $existing_news = array();
2256        foreach ($data as $k => $v) {
2257            if (in_array($k, $existing_ids)) {
2258                $existing_news[$k] = $v;
2259            }
2260        }
2261
2262        //var_dump($data);
2263        //var_dump($existing_news);
2264
2265        return $existing_news;
2266    }
2267}
2268