diff --git a/app/controllers/news.php b/app/controllers/news.php
index 5377b1c7941fe2a5ce4ddca3127e0e232278b167..e60e174f634fa99c5b76e61c01bee80bdf730e55 100644
--- a/app/controllers/news.php
+++ b/app/controllers/news.php
@@ -53,6 +53,23 @@ class NewsController extends StudipController
                 'icon'  => 'person',
             ],
         ];
+
+        $this->priorities = [
+            0 => '0 (' . _('normal') . ')',
+            1 => '1 (' . _('sehr niedrig') . ')',
+            2 => '2',
+            3 => '3',
+            4 => '4',
+            5 => '5',
+            6 => '6',
+            7 => '7',
+            8 => '8',
+            9 => '9',
+           10 => '10 (' . _('sehr hoch') . ')',
+       ];
+
+       $this->roles = NewsRoles::getAvailableRoles();
+       $this->rolesStats = RolePersistence::getStatistics();
     }
 
     /**
@@ -166,6 +183,7 @@ class NewsController extends StudipController
             'news_basic' => true,
             'news_comments' => false,
             'news_areas' => false,
+            'news_visibility' => false,
         ];
 
         if ($context_range) {
@@ -197,6 +215,15 @@ class NewsController extends StudipController
         if (!$news->havePermission('edit') && !$news->isNew()) {
             throw new AccessDeniedException();
         }
+
+        if(!$news->isNew()){
+            $this->assigned = NewsRoles::getRoles($id);
+            if ($this->assigned){
+                $this->news_isvisible['news_visibility'] = true;
+            }
+            $this->roles = NewsRoles::getAvailableRoles($id);
+        }
+
         // if form sent, get news data by post vars
         if (Request::get('news_isvisible')) {
             // visible categories, selected areas, topic, and body are utf8 encoded when sent via ajax
@@ -216,6 +243,13 @@ class NewsController extends StudipController
                                   ? $this->getTimeStamp(Request::get('news_enddate'), 'end') - $news->date
                                   : '';
             $news->allow_comments = Request::bool('news_allow_comments', false);
+            $news->prio = Request::int('news_prio', 0);
+            $assignedroles = Request::intArray('assignedroles',false);
+
+            $this->assigned = NewsRoles::load($assignedroles);
+            if ($this->assigned){
+                $this->news_isvisible['news_visibility'] = true;
+            }
         } elseif ($id) {
             // if news id given check for valid id and load ranges
             if ($news->isNew()) {
@@ -439,6 +473,10 @@ class NewsController extends StudipController
 
                 $news->store();
 
+                if ($GLOBALS['perm']->have_perm('admin')) {
+                    NewsRoles::update($news->id, $assignedroles);
+                }
+
                 PageLayout::postSuccess(_('Die Ankündigung wurde gespeichert.'));
                 if (!Request::isXhr() && !$id) {
                     // in fallback mode redirect to edit page with proper news id
diff --git a/app/views/news/edit_news.php b/app/views/news/edit_news.php
index f5641c157389699f04a127d22746c082f317b191..2559d065842e0111b591dc09988218424ad0ac0a 100644
--- a/app/views/news/edit_news.php
+++ b/app/views/news/edit_news.php
@@ -23,6 +23,7 @@
     <input type="hidden" name="news_basic_js" value="">
     <input type="hidden" name="news_comments_js" value="">
     <input type="hidden" name="news_areas_js" value="">
+    <input type="hidden" name="news_visibility_js" value="">
     <input type="hidden" name="news_isvisible" value="<?=htmlReady(json_encode($news_isvisible))?>">
     <input type="hidden" name="news_selectable_areas" value="<?=htmlReady(json_encode($area_options_selectable))?>">
     <input type="hidden" name="news_selected_areas" value="<?=htmlReady(json_encode($area_options_selected))?>">
@@ -256,6 +257,50 @@
         </div>
     </fieldset>
 
+    <fieldset <?= $news_isvisible['news_visibility'] ? '' : 'class="collapsed"' ?>>
+        <legend class="news_visibility_header" id="news_visibility">
+            <?= _('Sichtbarkeitseinstellungen') ?>
+        </legend>
+
+        <? if ($anker == 'news_visibility') : ?>
+            <a name='anker'></a>
+        <? endif ?>
+
+        <label>
+            <?= _('Priorität') ?>
+
+            <select name="news_prio">
+                <? foreach ($priorities as $key => $label) : ?>
+                    <option value ="<?= $key ?>"<?= $news->prio == $key ? ' selected' : '' ?>><?= $label ?></option>
+                <? endforeach ?>
+            </select>
+        </label>
+        <? if ($GLOBALS['perm']->have_perm('admin')) : ?>
+            <label>
+                <?= _('Sichtbarkeit') ?>
+
+                <select id="assignedroles" name="assignedroles[]" multiple>
+                    <? if ($assigned) : ?>
+                        <? foreach ($assigned as $assignedrole) : ?>
+                            <option value="<?= $assignedrole->getRoleid() ?>" selected>
+                                <?= htmlReady($assignedrole->getRolename()) ?>
+                                <? if ($assignedrole->getSystemtype()) : ?>[<?= _('Systemrolle') ?>]<? endif ?>
+                                (<?= $rolesStats[$assignedrole->getRoleid()]['explicit'] + $rolesStats[$assignedrole->getRoleid()]['implicit'] ?>)
+                            </option>
+                        <? endforeach ?>
+                    <? endif ?>
+                    <? foreach ($roles as $role) : ?>
+                        <option value="<?= $role->getRoleid() ?>">
+                            <?= htmlReady($role->getRolename()) ?>
+                            <? if ($role->getSystemtype()) : ?>[<?= _('Systemrolle') ?>]<? endif ?>
+                            (<?= $rolesStats[$role->getRoleid()]['explicit'] + $rolesStats[$role->getRoleid()]['implicit'] ?>)
+                        </option>
+                    <? endforeach ?>
+                </select>
+            </label>
+        <? endif ?>
+    </fieldset>
+
     <footer data-dialog-button>
         <? if ($news->isNew()) : ?>
             <?= Button::createAccept(_('Ankündigung erstellen'), 'save_news') ?>
@@ -280,4 +325,9 @@
             event.preventDefault();
         }
     });
+    <? if ($GLOBALS['perm']->have_perm('admin')) : ?>
+        $("#assignedroles").select2({
+            width: '100%'
+        });
+    <? endif ?>
 </script>
diff --git a/db/migrations/5.1.16_tic_410.php b/db/migrations/5.1.16_tic_410.php
new file mode 100644
index 0000000000000000000000000000000000000000..af9774e8d6a8d96f81c74361b63353e7ef909cef
--- /dev/null
+++ b/db/migrations/5.1.16_tic_410.php
@@ -0,0 +1,43 @@
+<?php
+class tic410 extends Migration
+{
+    public function description()
+    {
+        return "create NewsRoles table";
+    }
+
+    public function up()
+    {
+        $db =  DBManager::get();
+
+        $query = 'CREATE TABLE IF NOT EXISTS `news_roles` (
+          `news_id` CHAR(32) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
+          `roleid` int(10) NOT NULL,
+           PRIMARY KEY (`news_id`, `roleid`)
+          ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;';
+        $db->exec($query);
+
+        $query = 'ALTER TABLE `news` ADD COLUMN `prio` tinyint(2) NOT NULL DEFAULT 0 AFTER `allow_comments`';
+        $db->exec($query);
+
+        $query = "INSERT IGNORE INTO `config` (`field`, `value`, `type`, `range`, `mkdate`, `chdate`, `description`)
+                  VALUES (:name, :value, :type, :range, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), :description)";
+
+        $statement = DBManager::get()->prepare($query);
+        $statement->execute([
+            ':name'        => 'NEWS_ONLY_SYSTEM_ROLES',
+            ':description' => 'Über diese Option wird die Auswahl der rollenspezifischen Ankündigungen auf Systemrollen begrenzt',
+            ':range'       => 'global',
+            ':type'        => 'boolean',
+            ':value'       => '1'
+        ]);
+    }
+
+    public function down()
+    {
+        $db =  DBManager::get();
+
+        $db->exec('DROP TABLE IF EXISTS `news_roles`');
+        $db->exec('ALTER TABLE `news` DROP COLUMN `prio`');
+    }
+}
diff --git a/lib/models/NewsRoles.class.php b/lib/models/NewsRoles.class.php
new file mode 100644
index 0000000000000000000000000000000000000000..e2de5f850016db69e73fe74875bc13db9b582be4
--- /dev/null
+++ b/lib/models/NewsRoles.class.php
@@ -0,0 +1,140 @@
+<?php
+
+/**
+ * NewsRoles.class.php - model class for the news roles
+ *
+ * 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 (at your option) any later version.
+ *
+ * @author      Sebastian Biller <s.biller@tu-braunschweig.de>
+ * @license     http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category    Stud.IP
+ * @package     admin
+ * @since       5.1
+ *
+ * @property string news_id database column
+ * @property int roleid database column
+ */
+
+class NewsRoles extends SimpleORMap
+{
+    protected static function configure($config = [])
+    {
+        $config['db_table'] = 'news_roles';
+
+        $config['belongs_to']['news_ranges'] = [
+            'class_name'  => StudipNews::class,
+            'foreign_key' => 'news_id',
+        ];
+
+        parent::configure($config);
+    }
+
+    public static function checkUserAccess($news_id, $user_id = null)
+    {
+        $user_id = $user_id ?: (isset($GLOBALS['user']) ? $GLOBALS['user']->id : null);
+        $news_roles = self::getRoles($news_id);
+
+        if (!$news_roles) {
+            return true;
+        }
+
+        if (!$user_id) {
+            return false;
+        }
+
+        $user_roles = RolePersistence::getAssignedRoles($user_id, true);
+
+        foreach ($news_roles as $news_role) {
+            foreach ($user_roles as $user_role) {
+                if ($news_role->getRoleid() === $user_role->getRoleid()) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    public static function getRoles($news_id)
+    {
+        $news_roles = self::findBynews_id($news_id);
+        $news_role_ids = [];
+        foreach ($news_roles as $news_role) {
+            $news_role_ids[] = $news_role['roleid'];
+        }
+
+        $only_system_roles = Config::get()->NEWS_ONLY_SYSTEM_ROLES;
+        $roles = RolePersistence::getAllRoles();
+        $re = [];
+        foreach ($news_role_ids as $role_id) {
+            if (isset($roles[$role_id])) {
+                if ($only_system_roles && !$roles[$role_id]->getSystemtype()) {
+                    continue;
+                }
+                $re[$role_id] = $roles[$role_id];
+            }
+        }
+        return $re;
+    }
+
+    public static function getAvailableRoles($news_id = null)
+    {
+        $news_role_ids = [];
+        if ($news_id) {
+            $news_roles = self::findBynews_id($news_id);
+            foreach ($news_roles as $news_role) {
+                $news_role_ids[] = $news_role['roleid'];
+            }
+        }
+
+        $only_system_roles = Config::get()->NEWS_ONLY_SYSTEM_ROLES;
+        $roles = RolePersistence::getAllRoles();
+        $rolesStats = RolePersistence::getStatistics();
+        $re = [];
+        foreach ($roles as $key => $role) {
+            if (!in_array($key, $news_role_ids)) {
+                if ($only_system_roles && !$role->getSystemtype()) {
+                    continue;
+                }
+                if ($rolesStats[$role->getRoleid()]['explicit'] + $rolesStats[$role->getRoleid()]['implicit'] == 0) {
+                    continue;
+                }
+                $re[$key] = $role;
+            }
+        }
+
+        return $re;
+    }
+
+    public static function update($news_id, $new_roles)
+    {
+        self::deleteBynews_id($news_id);
+
+        if ($new_roles) {
+            foreach ($new_roles as $new_role) {
+                $NewsRoles = new self();
+                $NewsRoles->news_id = $news_id;
+                $NewsRoles->roleid = $new_role;
+                $NewsRoles->store();
+            }
+        }
+    }
+
+    public static function load($new_roles)
+    {
+        $roles = RolePersistence::getAllRoles();
+
+        return array_filter(array_map(
+            function ($role_id) use ($roles) {
+                if (!isset($roles[$role_id])) {
+                    return false;
+                }
+                return [$role_id, $roles[$role_id]];
+            },
+            $new_roles
+        ));
+    }
+}
diff --git a/lib/models/StudipNews.class.php b/lib/models/StudipNews.class.php
index b528604bfbf8b27ca374ef0aaca1686e54cb5498..227561a125db37aaaf4ef83ef1ebda9d1244a7ca 100644
--- a/lib/models/StudipNews.class.php
+++ b/lib/models/StudipNews.class.php
@@ -36,6 +36,7 @@ require_once 'lib/object.inc.php';
  * @property string user_id database column
  * @property string expire database column
  * @property string allow_comments database column
+ * @property int prio database column
  * @property string chdate database column
  * @property string chdate_uid database column
  * @property string mkdate database column
@@ -64,6 +65,11 @@ class StudipNews extends SimpleORMap implements PrivacyObject
             'class_name'  => 'User',
             'foreign_key' => 'user_id',
         ];
+        $config['has_many']['news_roles'] = [
+            'class_name'        => NewsRoles::class,
+            'assoc_foreign_key' => 'news_id',
+            'on_delete'         => 'delete'
+        ];
 
         $config['i18n_fields']['topic'] = true;
         $config['i18n_fields']['body'] = true;
@@ -96,25 +102,33 @@ class StudipNews extends SimpleORMap implements PrivacyObject
                   INNER JOIN news USING (news_id)
                   WHERE range_id = ? {$clause} ";
         if (Config::get()->SORT_NEWS_BY_CHDATE) {
-            $query .= "ORDER BY chdate DESC, date DESC, topic ASC";
+            $query .= "ORDER BY prio DESC, chdate DESC, date DESC, topic ASC";
         } else {
-            $query .= "ORDER BY date DESC, chdate DESC, topic ASC";
+            $query .= "ORDER BY 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 SUM(nw.chdate > IFNULL(b.visitdate, :threshold) AND nw.user_id != :user_id)
+        $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
-                  GROUP BY a.range_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);
@@ -122,7 +136,13 @@ class StudipNews extends SimpleORMap implements PrivacyObject
         $plugin_id = object_type_to_id('news');
         $statement->bindValue(':plugin_id', $plugin_id);
         $statement->execute();
-        return (int) $statement->fetchColumn();
+        $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 (int) count($ret);
     }
 
     public static function GetNewsByAuthor($user_id, $as_objects = false)
@@ -131,13 +151,22 @@ class StudipNews extends SimpleORMap implements PrivacyObject
                   FROM news
                   WHERE user_id = ? ";
         if (Config::get()->SORT_NEWS_BY_CHDATE) {
-            $query .= "ORDER BY chdate DESC, date DESC";
+            $query .= "ORDER BY prio DESC, chdate DESC, date DESC";
         } else {
-            $query .= "ORDER BY date DESC, chdate DESC";
+            $query .= "ORDER BY 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;
     }