aboutsummaryrefslogtreecommitdiff
path: root/lib/models/StudipNews.php
diff options
context:
space:
mode:
authorPhilipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de>2024-09-24 10:53:31 +0200
committerPhilipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de>2024-09-24 10:53:31 +0200
commit4459dd7917f4d1c34f40bb68f0e991e9c3d53e4c (patch)
tree5c07151ae61276d334e88f6309c30d439a85c12e /lib/models/StudipNews.php
parentda0022e5c1abbf9825ae76debaabdff7e8623bb4 (diff)
parent97a188592c679890a25c37ab78463add76a52ff7 (diff)
Merge branch 'main' into issue-3911issue-3911
Diffstat (limited to 'lib/models/StudipNews.php')
-rw-r--r--lib/models/StudipNews.php696
1 files changed, 696 insertions, 0 deletions
diff --git a/lib/models/StudipNews.php b/lib/models/StudipNews.php
new file mode 100644
index 0000000..ccb1828
--- /dev/null
+++ b/lib/models/StudipNews.php
@@ -0,0 +1,696 @@
+<?php
+// +---------------------------------------------------------------------------+
+// This file is part of Stud.IP
+//
+// Copyright (C) 2005 André Noack <noack@data-quest>,
+// Suchi & Berg GmbH <info@data-quest.de>
+// +---------------------------------------------------------------------------+
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or any later version.
+// +---------------------------------------------------------------------------+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// +---------------------------------------------------------------------------+
+require_once 'lib/object.inc.php';
+
+/**
+ * StudipNews.php
+ *
+ * @author André Noack <noack@data-quest>, Suchi & Berg GmbH <info@data-quest.de>
+ * @author Arne Schröder <schroeder@data-quest>
+ * @access public
+ *
+ * @property string $id alias column for news_id
+ * @property string $news_id database column
+ * @property I18NString $topic database column
+ * @property I18NString $body database column
+ * @property string $author database column
+ * @property int $date database column
+ * @property string $user_id database column
+ * @property int $expire database column
+ * @property int $allow_comments database column
+ * @property int $prio database column
+ * @property int $chdate database column
+ * @property string $chdate_uid database column
+ * @property int $mkdate database column
+ * @property SimpleORMapCollection|NewsRange[] $news_ranges has_many NewsRange
+ * @property SimpleORMapCollection|StudipComment[] $comments has_many StudipComment
+ * @property SimpleORMapCollection|NewsRoles[] $news_roles has_many NewsRoles
+ * @property User $owner belongs_to User
+ * @property int $views additional field
+ */
+class StudipNews extends SimpleORMap implements PrivacyObject
+{
+ protected static function configure($config = [])
+ {
+ $config['db_table'] = 'news';
+ $config['has_many']['news_ranges'] = [
+ 'class_name' => NewsRange::class,
+ 'assoc_foreign_key' => 'news_id',
+ 'on_delete' => 'delete',
+ 'on_store' => 'store',
+ ];
+ $config['has_many']['comments'] = [
+ 'class_name' => StudipComment::class,
+ 'assoc_foreign_key' => 'object_id',
+ 'on_delete' => 'delete',
+ 'on_store' => 'store',
+ ];
+ $config['belongs_to']['owner'] = [
+ 'class_name' => User::class,
+ 'foreign_key' => 'user_id',
+ ];
+ $config['has_many']['news_roles'] = [
+ 'class_name' => NewsRoles::class,
+ 'assoc_foreign_key' => 'news_id',
+ 'on_delete' => 'delete'
+ ];
+
+ $config['additional_fields'] = [
+ 'views' => [
+ 'get' => function (StudipNews $news): int {
+ return object_return_views($news->id);
+ },
+ ],
+ ];
+
+ $config['i18n_fields'] = ['topic', 'body'];
+
+ // Strip <admin_msg> from news body
+ $config['registered_callbacks']['after_initialize'][] = function ($news) {
+ if ($news->isI18nField('body')) {
+ $news->body->setOriginal(explode('<admin_msg>', $news->body->original())[0]);
+ foreach ($news->body->toArray() as $lang => $value) {
+ if ($value) {
+ $news->body->setLocalized(
+ explode('<admin_msg>', $value)[0],
+ $lang
+ );
+ }
+ }
+ }
+ };
+
+ parent::configure($config);
+ }
+
+ public static function GetNewsByRange($range_id, $only_visible = false, $as_objects = false)
+ {
+ $clause = '';
+ if ($only_visible) {
+ $clause = " AND date < UNIX_TIMESTAMP() AND (date + expire) > UNIX_TIMESTAMP() ";
+ }
+ $query = "SELECT news_id AS idx, news.*
+ FROM news_range
+ INNER JOIN news USING (news_id)
+ WHERE range_id = ? {$clause} ";
+ if (Config::get()->SORT_NEWS_BY_CHDATE) {
+ $query .= "ORDER BY IF(prio = 0, 6, prio) DESC, chdate DESC, date DESC, topic ASC";
+ } else {
+ $query .= "ORDER BY IF(prio = 0, 6, prio) DESC, date DESC, chdate DESC, topic ASC";
+ }
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$range_id]);
+ $ret = $statement->fetchGrouped(PDO::FETCH_ASSOC);
+ if (!(isset($GLOBALS['perm']) && $GLOBALS['perm']->have_perm('root'))) {
+ if (!(User::find($range_id) && $GLOBALS['user']->id == $range_id)) {
+ foreach ($ret as $news_id => $news) {
+ if (!NewsRoles::checkUserAccess($news_id)) {
+ unset($ret[$news_id]);
+ }
+ }
+ }
+ }
+
+ return $as_objects ? static::GetNewsObjects($ret) : $ret;
+ }
+
+ public static function CountUnread($range_id = 'studip', $user_id = false)
+ {
+ $query = "SELECT nw.news_id as idx, nw.chdate > IFNULL(b.visitdate, :threshold) AS active
+ FROM news_range a
+ LEFT JOIN news nw ON (a.news_id = nw.news_id AND UNIX_TIMESTAMP() BETWEEN date AND date + expire)
+ LEFT JOIN object_user_visits b ON (b.object_id = nw.news_id AND b.user_id = :user_id AND b.plugin_id = :plugin_id)
+ WHERE a.range_id = :range_id AND nw.user_id != :user_id";
+ $statement = DBManager::get()->prepare($query);
+ $statement->bindValue(':threshold', object_get_visit_threshold());
+ $statement->bindValue(':user_id', $user_id ?: $GLOBALS['user']->id);
+ $statement->bindValue(':range_id', $range_id);
+ $plugin_id = object_type_to_id('news');
+ $statement->bindValue(':plugin_id', $plugin_id);
+ $statement->execute();
+ $ret = $statement->fetchGrouped(PDO::FETCH_ASSOC);
+ foreach ($ret as $news_id => $news) {
+ if (!NewsRoles::checkUserAccess($news_id, $user_id) && !$GLOBALS['perm']->have_perm('root') || !$news['active']) {
+ unset($ret[$news_id]);
+ }
+ }
+ return count($ret);
+ }
+
+ public static function GetNewsByAuthor($user_id, $as_objects = false)
+ {
+ $query = "SELECT news_id AS idx, news.*
+ FROM news
+ WHERE user_id = ? ";
+ if (Config::get()->SORT_NEWS_BY_CHDATE) {
+ $query .= "ORDER BY IF(prio = 0, 6, prio) DESC, chdate DESC, date DESC";
+ } else {
+ $query .= "ORDER BY IF(prio = 0, 6, prio) DESC, date DESC, chdate DESC";
+ }
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$user_id]);
+ $ret = $statement->fetchGrouped(PDO::FETCH_ASSOC);
+ if (!(isset($GLOBALS['perm']) && $GLOBALS['perm']->have_perm('root'))) {
+ if ($GLOBALS['user']->id != $user_id) {
+ foreach ($ret as $news_id => $news) {
+ if (!NewsRoles::checkUserAccess($news_id)) {
+ unset($ret[$news_id]);
+ }
+ }
+ }
+ }
+
+ return $as_objects ? static::GetNewsObjects($ret) : $ret;
+ }
+
+ public static function GetNewsByRSSId($rss_id, $as_objects = false)
+ {
+ if ($user_id = static::GetUserIDFromRssID($rss_id)) {
+ return static::GetNewsByRange($user_id, true, $as_objects);
+ }
+
+ return [];
+ }
+
+ public static function GetNewsObjects($news_result)
+ {
+ return array_map(
+ [static::class, 'buildExisting'],
+ (array) $news_result
+ );
+ }
+
+ /**
+ * fetches set of news items from database
+ *
+ * @param string $user_id author id for news set
+ * @param string $area area group for news set (global, inst, sem or user)
+ * @param string $term search term for news topic
+ * @param int $startdate return only news (still) visible after this date
+ * @param int $enddate return only news (still) visible before this date
+ * @param boolean $as_objects include StudipNews objects in result array
+ * @param int $limit max size of returned news set
+ * @return array set of news items
+ */
+ public static function GetNewsRangesByFilter($user_id, $area = '', $term = '', $startdate = 0, $enddate = 0, $as_objects = false, $limit = 100)
+ {
+ $news_result = [];
+ $query_vars = [];
+
+ if ($limit <= 0) {
+ return $news_result;
+ }
+ $where_querypart = [];
+ if (isset($startdate)) {
+ $where_querypart[] = '(date+expire) > ?';
+ $query_vars[] = $startdate;
+ }
+ if (isset($enddate)) {
+ $where_querypart[] = 'date < ?';
+ $query_vars[] = $enddate;
+ }
+
+ if(!$GLOBALS['perm']->have_perm('root') || $area !== 'global') {
+ $where_querypart[] = 'news.user_id = ?';
+ $query_vars[] = $user_id;
+ }
+
+ if (isset($term)) {
+ $where_querypart[] = "topic LIKE CONCAT('%', ?, '%')";
+ $query_vars[] = $term;
+ }
+ switch ($area) {
+ case 'global':
+ $select_querypart = 'CONCAT(news_id, "_studip") AS idx, range_id, news.* ';
+ $from_querypart = 'news_range INNER JOIN news USING(news_id)';
+ $where_querypart[] = 'range_id = ?';
+ if (Config::get()->SORT_NEWS_BY_CHDATE) {
+ $order_querypart = 'news.chdate DESC, news.date DESC';
+ } else {
+ $order_querypart = 'news.date DESC, news.chdate DESC';
+ }
+ $query_vars[] = 'studip';
+ break;
+ case 'sem':
+ $select_querypart = 'CONCAT(news_id, "_", range_id) AS idx, range_id, seminare.Name AS title, '
+ .'seminare.start_time AS start, news.*, seminare.start_time, sd1.name AS startsem, '
+ .'IFNULL((SELECT semester_data.name FROM semester_data INNER JOIN semester_courses USING (semester_id) WHERE semester_courses.course_id = seminare.Seminar_id ORDER BY semester_data.beginn DESC LIMIT 1), "'._("unbegrenzt").'") AS endsem ';
+ $from_querypart = 'news INNER JOIN news_range USING(news_id) INNER JOIN seminare ON Seminar_id = range_id '
+ .'LEFT JOIN semester_data sd1 ON (start_time BETWEEN sd1.beginn AND sd1.ende) ';
+ if (Config::get()->SORT_NEWS_BY_CHDATE) {
+ $order_querypart = 'seminare.Name, news.chdate DESC, news.date DESC';
+ } else {
+ $order_querypart = 'seminare.Name, news.date DESC, news.chdate DESC';
+ }
+ break;
+ case 'inst':
+ $select_querypart = 'CONCAT(news_id, "_", range_id) AS idx, range_id, Institute.Name AS title, news.* ';
+ $from_querypart = 'Institute INNER JOIN news_range ON Institut_id = range_id INNER JOIN news USING(news_id)';
+ if (Config::get()->SORT_NEWS_BY_CHDATE) {
+ $order_querypart = 'Institute.Name, news.chdate DESC, news.date DESC';
+ } else {
+ $order_querypart = 'Institute.Name, news.date DESC, news.chdate DESC';
+ }
+ break;
+ case 'user':
+ $select_querypart = 'CONCAT(news_id, "_", auth_user_md5.user_id) AS idx, range_id, auth_user_md5.user_id AS userid, news.* ';
+ $from_querypart = 'auth_user_md5 INNER JOIN news_range ON auth_user_md5.user_id = range_id INNER JOIN news USING(news_id)';
+ if (Config::get()->SORT_NEWS_BY_CHDATE) {
+ $order_querypart = 'auth_user_md5.Nachname, news.chdate DESC, news.date DESC';
+ } else {
+ $order_querypart = 'auth_user_md5.Nachname, news.date DESC, news.chdate DESC';
+ }
+ break;
+ default:
+ foreach (['global', 'inst', 'sem', 'user'] as $type) {
+ $add_news = static::GetNewsRangesByFilter($user_id, $type, $term, $startdate, $enddate, $as_objects, $limit);
+ if (is_array($add_news) && isset($add_news[$type])) {
+ $limit = $limit - count($add_news[$type]);
+ $news_result = array_merge($news_result, $add_news);
+ }
+ }
+ return $news_result;
+ }
+ $query = "SELECT " . $select_querypart . "
+ FROM " . $from_querypart . "
+ WHERE " . implode(' AND ', $where_querypart) . "
+ ORDER BY " . $order_querypart . " LIMIT 0, ?";
+
+ $query_vars[] = $limit;
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute($query_vars);
+ $news_result = $statement->fetchGrouped();
+
+ $objects = [$area => []];
+ foreach ($news_result as $id => $result) {
+ $objects[$area][$id] = [
+ 'range_id' => $result['range_id'],
+ 'title' => $result['title'] ?? '',
+ ];
+ if ($area === 'sem') {
+ if (!isset($objects[$area][$id]['semester'])) {
+ $objects[$area][$id]['semester'] = '';
+ }
+ $objects[$area][$id]['semester'] .= sprintf('(%s%s)',
+ $result['startsem'],
+ $result['startsem'] != $result['endsem'] ? ' - ' . $result['endsem'] : '');
+ } elseif ($area === 'user') {
+ if ($GLOBALS['user']->id === $result['userid']) {
+ $objects[$area][$id]['title'] = _('Ankündigungen auf Ihrer Profilseite');
+ } else {
+ $objects[$area][$id]['title'] = sprintf(_('Ankündigungen auf der Profilseite von %s'), get_fullname($result['userid']));
+ }
+ } elseif ($area === 'global') {
+ $objects[$area][$id]['title'] = _('Ankündigungen auf der Stud.IP Startseite');
+ }
+ if ($as_objects) {
+ $objects[$area][$id]['object'] = self::build($result, false);
+ }
+ }
+
+ return $objects;
+ }
+
+ public static function GetUserIdFromRssID($rss_id)
+ {
+ $ret = static::GetRangeIdFromRssID($rss_id);
+ return $ret['range_id'];
+ }
+
+ public static function GetRssIdFromUserId($user_id)
+ {
+ return static::GetRssIdFromRangeId($user_id);
+ }
+
+ public static function GetRangeFromRssID($rss_id)
+ {
+ if ($rss_id){
+ $query = "SELECT range_id ,range_type
+ FROM news_rss_range
+ WHERE rss_id = ?";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$rss_id]);
+ $ret = $statement->fetch(PDO::FETCH_ASSOC);
+
+ if (count($ret)) {
+ return $ret;
+ }
+ }
+ return false;
+ }
+
+ public static function GetRangeIdFromRssID($rss_id)
+ {
+ $ret = static::GetRangeFromRssID($rss_id);
+ return $ret['range_id'];
+ }
+
+ public static function GetRssIdFromRangeId($range_id)
+ {
+ $query = "SELECT rss_id FROM news_rss_range WHERE range_id = ?";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$range_id]);
+ return $statement->fetchColumn();
+ }
+
+ public static function SetRssId($range_id, $type = false)
+ {
+ if (!$type){
+ $type = get_object_type($range_id);
+ if ($type === 'fak') {
+ $type = 'inst';
+ }
+ }
+ $rss_id = md5('StudipRss' . $range_id);
+
+ $query = "REPLACE INTO news_rss_range (range_id,rss_id,range_type)
+ VALUES (?, ?, ?)";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([
+ $range_id,
+ $rss_id,
+ $type
+ ]);
+ return $statement->rowCount();
+ }
+
+ public static function UnsetRssId($range_id)
+ {
+ $query = "DELETE FROM news_rss_range WHERE range_id = ?";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$range_id]);
+ return $statement->rowCount();
+ }
+
+ public static function GetAdminMsg($user_id, $date)
+ {
+ return sprintf(
+ _('Zuletzt aktualisiert von %s (%s) am %s'),
+ get_fullname($user_id),
+ get_username($user_id),
+ date('d.m.y', $date)
+ );
+ }
+
+ public static function DoGarbageCollect($news_deletion_days = false)
+ {
+ $db = DBManager::get();
+ $queries = [];
+ $parameters = [];
+
+ if ($news_deletion_days !== false) {
+ $queries[] = "SELECT news.news_id
+ FROM news
+ WHERE date + expire + ? < UNIX_TIMESTAMP()";
+ $parameters[] = (int) $news_deletion_days;
+ }
+
+ $queries[] = "SELECT news_range.news_id
+ FROM news_range
+ LEFT JOIN news USING (news_id)
+ WHERE news.news_id IS NULL";
+ $queries[] = "SELECT news.news_id
+ FROM news
+ LEFT JOIN news_range USING (news_id)
+ WHERE range_id IS NULL";
+
+ $query = implode(' UNION DISTINCT ', $queries);
+ $stm = $db->prepare($query);
+ $stm->execute($parameters);
+ $result = $stm->fetchAll(PDO::FETCH_COLUMN);
+ $killed = 0;
+ if (count($result) > 0) {
+ $query = "DELETE FROM news WHERE news_id IN (?)";
+ $statement = $db->prepare($query);
+ $statement->execute([$result]);
+ $killed = $statement->rowCount();
+
+ $query = "DELETE FROM news_range WHERE news_id IN (?)";
+ $statement = $db->prepare($query);
+ $statement->execute([$result]);
+
+ object_kill_visits(null, $result);
+ object_kill_views($result);
+ StudipComment::DeleteCommentsByObject($result);
+ }
+ return $killed;
+ }
+
+ public static function DeleteNewsRanges($range_id)
+ {
+ $query = "DELETE FROM news_range WHERE range_id = :id";
+ $statement = DBManager::get()->prepare($query);
+ $statement->bindValue(':id', $range_id);
+ $result = $statement->execute();
+
+ static::DoGarbageCollect();
+
+ return $result;
+ }
+
+ public static function DeleteNewsByAuthor($user_id)
+ {
+ $deleted = 0;
+ foreach (static::GetNewsByAuthor($user_id, true) as $news) {
+ $deleted += $news->delete();
+ }
+ return $deleted;
+ }
+
+ public static function haveRangePermission($operation, $range_id, $user_id = '')
+ {
+ static $news_range_perm_cache;
+ if (isset($news_range_perm_cache[$user_id.$range_id.$operation])) {
+ return $news_range_perm_cache[$user_id.$range_id.$operation];
+ }
+ if (!$user_id) {
+ $user_id = $GLOBALS['user']->id;
+ }
+ if ($GLOBALS['perm']->have_perm('root', $user_id)) {
+ return $news_range_perm_cache[$user_id.$range_id.$operation] = true;
+ }
+
+ $type = get_object_type($range_id, ['global', 'sem', 'inst', 'fak', 'user']);
+ switch($type) {
+ case 'global':
+ if ($operation === 'view')
+ return $news_range_perm_cache[$user_id.$range_id.$operation] = true;
+ break;
+ case 'fak':
+ case 'inst':
+ case 'sem':
+ if ($operation === 'view'
+ && ($type !== 'sem'
+ || $GLOBALS['perm']->have_studip_perm('user', $range_id)
+ || (Config::get()->ENABLE_FREE_ACCESS && Seminar::getInstance($range_id)->read_level == 0)
+ )) {
+ return $news_range_perm_cache[$user_id.$range_id.$operation] = true;
+ }
+ if ($operation === 'edit' || $operation === 'copy') {
+ if ($GLOBALS['perm']->have_studip_perm('tutor', $range_id))
+ return $news_range_perm_cache[$user_id.$range_id.$operation] = true;
+ }
+ break;
+ case 'user':
+ if ($operation === 'view') {
+ if ($range_id === $user_id || get_visibility_by_id($range_id)) {
+ return $news_range_perm_cache[$user_id.$range_id.$operation] = true;
+ }
+ } elseif ($operation === 'edit' || $operation == 'copy') {
+ if ($GLOBALS['perm']->have_profile_perm('user', $range_id))
+ return $news_range_perm_cache[$user_id.$range_id.$operation] = true;
+ }
+ break;
+ }
+ return $news_range_perm_cache[$user_id.$range_id.$operation] = false;
+ }
+
+ public function restoreRanges()
+ {
+ $this->resetRelation('news_ranges');
+ return count($this->news_ranges);
+ }
+
+ public function getRanges()
+ {
+ return $this->news_ranges->pluck('range_id');
+ }
+
+ public function issetRange($range_id)
+ {
+ return array_search($range_id, $this->getRanges()) !== false;
+ }
+
+ public function addRange($range_id)
+ {
+ if (!$this->issetRange($range_id)) {
+ $range = new NewsRange([$this->getId(), $range_id]);
+ if ($range->isNew()) {
+ $range->range_id = $range_id;
+ $range->news_id = $this->getId();
+ }
+ $this->news_ranges[] = $range;
+ return true;
+ }
+
+ return false;
+ }
+
+ public function deleteRange($range_id)
+ {
+ if ($this->issetRange($range_id)) {
+ return $this->news_ranges->unsetBy('range_id', $range_id);
+ }
+
+ return false;
+ }
+
+ public function storeRanges()
+ {
+ $this->storeRelations();
+ }
+
+ public function delete()
+ {
+ object_kill_visits(null, $this->getId());
+ object_kill_views($this->getId());
+ return parent::delete();
+ }
+
+ /**
+ * checks, if user has permission to perform given operation on news object
+ *
+ * @param string $operation delete, unassign, edit, copy, or view
+ * @param string $check_range_id specified range-id, used only for unassign-operation
+ * @param string $user_id optional; check permission for
+ * given user ID; otherwise for the
+ * global $user's ID
+ * @return boolean true or false
+ */
+ public function havePermission($operation, $check_range_id = '', $user_id = null)
+ {
+ if (!$user_id) {
+ $user_id = $GLOBALS['user']->id;
+ }
+ if (!in_array($operation, ['delete', 'unassign', 'edit', 'copy', 'view'])) {
+ return false;
+ }
+
+ // in order to unassign, there must be more than one range assigned; $check_range_id must be specified.
+ if ($operation === 'unassign' && count($this->getRanges()) < 2) {
+ return false;
+ }
+ // root, owner, and owner's deputy have full permission
+ if ($GLOBALS['perm']->have_perm('root', $user_id)
+ || ($user_id === $this->user_id && $GLOBALS['perm']->have_perm('autor'))
+ || (Deputy::isEditActivated() && Deputy::isDeputy($user_id, $this->user_id, true)))
+ {
+ return true;
+ }
+
+ // check news' ranges for edit, copy or view permission
+ if ($operation === 'unassign' || $operation === 'delete') {
+ $range_operation = 'edit';
+ } else {
+ $range_operation = $operation;
+ }
+ $permission_ranges = 0;
+ foreach ($this->getRanges() as $range_id) {
+ if (static::haveRangePermission($range_operation, $range_id, $user_id)) {
+ if ($operation === 'view' || $operation === 'edit' || $operation === 'copy') {
+ // in order to view, edit, copy, or unassign, access to one of the ranges is sufficient
+ return true;
+ } elseif ($operation === 'unassign' && $range_id === $check_range_id) {
+ // in order to unassign, access to the specified range is needed
+ return true;
+ }
+ // in order to delete, access to all ranges is necessary
+ $permission_ranges += 1;
+ } elseif ($operation === 'delete') {
+ return false;
+ }
+ }
+ if ($operation === 'delete' && count($this->getRanges()) == $permission_ranges) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * checks, if basic news data is complete
+ *
+ * @return boolean true or false
+ */
+ public function validate()
+ {
+ if (!$this->user_id && $this->isNew()) {
+ $this->user_id = $GLOBALS['user']->id;
+ $this->author = get_fullname(false, 'full', false);
+ }
+ if (!$this->user_id OR !$this->author) {
+ PageLayout::postError(_('Fehler: Personenangabe unvollständig.'));
+ return false;
+ }
+ if (!$this->topic) {
+ PageLayout::postError(_('Bitte geben Sie einen Titel für die Ankündigung ein.'));
+ return false;
+ }
+ if (!$this->body) {
+ PageLayout::postError(_('Bitte geben Sie einen Inhalt für die Ankündigung ein.'));
+ return false;
+ }
+ if (!count($this->getRanges())) {
+ PageLayout::postError(_('Die Ankündigung muss mindestens einem Bereich zugeordnet sein.'));
+ return false;
+ }
+ if ((int)$this->date < 1) {
+ PageLayout::postError(_('Ungültiges Einstelldatum.'));
+ return false;
+ }
+ if ((int)$this->expire < 1) {
+ PageLayout::postError(_('Ungültiges Ablaufdatum.'));
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Export available data of a given user into a storage object
+ * (an instance of the StoredUserData class) for that user.
+ *
+ * @param StoredUserData $storage object to store data into
+ */
+ public static function exportUserData(StoredUserData $storage)
+ {
+ $sorm = self::findBySQL("user_id = ?", [$storage->user_id]);
+ if ($sorm) {
+ $field_data = [];
+ foreach ($sorm as $row) {
+ $field_data[] = $row->toRawArray();
+ }
+ if ($field_data) {
+ $storage->addTabularData(_('Ankündigungen'), 'news', $field_data);
+ }
+ }
+ }
+}