diff --git a/classes/labels.php b/classes/labels.php
index f72d7d84bf227b13e5d1df93e00b8826c60c667c..570f24f4ff74ee0d1e937c385864c1d4e5ad2c41 100644
--- a/classes/labels.php
+++ b/classes/labels.php
@@ -37,7 +37,18 @@ class Labels
 		}
 	}
 
-	static function get_all_labels($owner_uid)	{
+	static function get_as_hash($owner_uid) {
+		$rv = [];
+		$labels = Labels::get_all($owner_uid);
+
+		foreach ($labels as $i => $label) {
+			$rv[$label["id"]] = $labels[$i];
+		}
+
+		return $rv;
+	}
+
+	static function get_all($owner_uid)	{
 		$rv = array();
 
 		$pdo = Db::pdo();
@@ -46,7 +57,7 @@ class Labels
 			WHERE owner_uid = ? ORDER BY caption");
 		$sth->execute([$owner_uid]);
 
-		while ($line = $sth->fetch()) {
+		while ($line = $sth->fetch(PDO::FETCH_ASSOC)) {
 			array_push($rv, $line);
 		}
 
diff --git a/classes/pref/filters.php b/classes/pref/filters.php
index 9c250be19a2039ab1200413a2aa8958246131574..4e52260c9a606380d70a5795e5d6f5f10cd872ad 100755
--- a/classes/pref/filters.php
+++ b/classes/pref/filters.php
@@ -318,6 +318,84 @@ class Pref_Filters extends Handler_Protected {
 			WHERE id = ? AND owner_uid = ?");
 		$sth->execute([$filter_id, $_SESSION['uid']]);
 
+		if (empty($filter_id) || $row = $sth->fetch()) {
+			$rv = [
+				"id" => $filter_id,
+				"enabled" => $row["enabled"] ?? true,
+				"match_any_rule" => $row["match_any_rule"] ?? false,
+				"inverse" => $row["inverse"] ?? false,
+				"title" => $row["title"] ?? "",
+				"rules" => [],
+				"actions" => [],
+				"filter_types" => [],
+				"filter_actions" => [],
+				"labels" => Labels::get_all($_SESSION["uid"])
+			];
+
+			$res = $this->pdo->query("SELECT id,description
+				FROM ttrss_filter_types WHERE id != 5 ORDER BY description");
+
+			while ($line = $res->fetch()) {
+				$rv["filter_types"][$line["id"]] = __($line["description"]);
+			}
+
+			$res = $this->pdo->query("SELECT id,description FROM ttrss_filter_actions
+				ORDER BY name");
+
+			while ($line = $res->fetch()) {
+				$rv["filter_actions"][$line["id"]] = __($line["description"]);
+			}
+
+			if ($filter_id) {
+				$rules_sth = $this->pdo->prepare("SELECT * FROM ttrss_filters2_rules
+					WHERE filter_id = ? ORDER BY reg_exp, id");
+				$rules_sth->execute([$filter_id]);
+
+				while ($rrow = $rules_sth->fetch(PDO::FETCH_ASSOC)) {
+					if ($rrow["match_on"]) {
+						$rrow["feed_id"] = json_decode($rrow["match_on"], true);
+					} else {
+						if ($rrow["cat_filter"]) {
+							$feed_id = "CAT:" . (int)$rrow["cat_id"];
+						} else {
+							$feed_id = (int)$rrow["feed_id"];
+						}
+
+						$rrow["feed_id"] = ["" . $feed_id]; // set item type to string for in_array()
+					}
+
+					unset($rrow["cat_filter"]);
+					unset($rrow["cat_id"]);
+					unset($rrow["filter_id"]);
+					unset($rrow["id"]);
+					if (!$rrow["inverse"]) unset($rrow["inverse"]);
+					unset($rrow["match_on"]);
+
+					$rrow["name"] = $this->_get_rule_name($rrow);
+
+					array_push($rv["rules"], $rrow);
+				}
+
+				$actions_sth = $this->pdo->prepare("SELECT * FROM ttrss_filters2_actions
+					WHERE filter_id = ? ORDER BY id");
+				$actions_sth->execute([$filter_id]);
+
+				while ($arow = $actions_sth->fetch(PDO::FETCH_ASSOC)) {
+					$arow["action_param_label"] = $arow["action_param"];
+
+					unset($arow["filter_id"]);
+					unset($arow["id"]);
+
+					$arow["name"] = $this->_get_action_name($arow);
+
+					array_push($rv["actions"], $arow);
+				}
+			}
+			print json_encode($rv);
+		}
+
+		/*return;
+
 		if (empty($filter_id) || $row = $sth->fetch()) {
 
 			$enabled = $row["enabled"] ?? true;
@@ -475,7 +553,7 @@ class Pref_Filters extends Handler_Protected {
 			}
 
 			print "</footer></form>";
-		}
+		} */
 	}
 
 	private function _get_rule_name($rule) {
@@ -592,8 +670,7 @@ class Pref_Filters extends Handler_Protected {
 		$sth->execute(array_merge($ids, [$_SESSION['uid']]));
 	}
 
-	private function _save_rules_and_actions($filter_id)
-	{
+	private function _save_rules_and_actions($filter_id) {
 
 		$sth = $this->pdo->prepare("DELETE FROM ttrss_filters2_rules WHERE filter_id = ?");
 		$sth->execute([$filter_id]);
@@ -670,7 +747,7 @@ class Pref_Filters extends Handler_Protected {
 		}
 	}
 
-	function add() {
+	function add () {
 		$enabled = checkbox_to_sql_bool(clean($_REQUEST["enabled"] ?? false));
 		$match_any_rule = checkbox_to_sql_bool(clean($_REQUEST["match_any_rule"] ?? false));
 		$title = clean($_REQUEST["title"]);
@@ -764,7 +841,15 @@ class Pref_Filters extends Handler_Protected {
 		<?php
 	}
 
-	function newrule() {
+	function editrule() {
+		$feed_ids = explode(",", clean($_REQUEST["ids"]));
+
+		print json_encode([
+			"multiselect" => $this->feed_multi_select("feed_id", $feed_ids, 'style="width : 500px; height : 300px" dojoType="dijit.form.MultiSelect"')
+		]);
+
+		/*return;
+
 		$rule = json_decode(clean($_REQUEST["rule"]), true);
 
 		if ($rule) {
@@ -818,7 +903,7 @@ class Pref_Filters extends Handler_Protected {
 
 		print "<fieldset>";
 		print "<span id='filterDlg_feeds'>";
-		$this->feed_multi_select("feed_id",
+		print $this->feed_multi_select("feed_id",
 			$feed_id,
 			'style="width : 500px; height : 300px" dojoType="dijit.form.MultiSelect"');
 		print "</span>";
@@ -840,7 +925,7 @@ class Pref_Filters extends Handler_Protected {
 
 		print "</footer>";
 
-		print "</form>";
+		print "</form>";*/
 	}
 
 	function newaction() {
@@ -1071,102 +1156,106 @@ class Pref_Filters extends Handler_Protected {
 						   $attributes = "", $include_all_feeds = true,
 						   $root_id = null, $nest_level = 0) {
 
-	$pdo = Db::pdo();
+		$pdo = Db::pdo();
 
-	//	print_r(in_array("CAT:6",$default_ids));
+		$rv = "";
 
-	if (!$root_id) {
-		print "<select multiple=\true\" id=\"$id\" name=\"$id\" $attributes>";
-		if ($include_all_feeds) {
-			$is_selected = (in_array("0", $default_ids)) ? "selected=\"1\"" : "";
-			print "<option $is_selected value=\"0\">".__('All feeds')."</option>";
-		}
-	}
+		//	print_r(in_array("CAT:6",$default_ids));
 
-	if (get_pref('ENABLE_FEED_CATS')) {
+		if (!$root_id) {
+			$rv .= "<select multiple=\true\" id=\"$id\" name=\"$id\" $attributes>";
+			if ($include_all_feeds) {
+				$is_selected = (in_array("0", $default_ids)) ? "selected=\"1\"" : "";
+				$rv .= "<option $is_selected value=\"0\">".__('All feeds')."</option>";
+			}
+		}
 
-		if (!$root_id) $root_id = null;
+		if (get_pref('ENABLE_FEED_CATS')) {
 
-		$sth = $pdo->prepare("SELECT id,title,
-				(SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
-					c2.parent_cat = ttrss_feed_categories.id) AS num_children
-				FROM ttrss_feed_categories
-				WHERE owner_uid = :uid AND
-				(parent_cat = :root_id OR (:root_id IS NULL AND parent_cat IS NULL)) ORDER BY title");
+			if (!$root_id) $root_id = null;
 
-		$sth->execute([":uid" => $_SESSION['uid'], ":root_id" => $root_id]);
+			$sth = $pdo->prepare("SELECT id,title,
+					(SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
+						c2.parent_cat = ttrss_feed_categories.id) AS num_children
+					FROM ttrss_feed_categories
+					WHERE owner_uid = :uid AND
+					(parent_cat = :root_id OR (:root_id IS NULL AND parent_cat IS NULL)) ORDER BY title");
 
-		while ($line = $sth->fetch()) {
+			$sth->execute([":uid" => $_SESSION['uid'], ":root_id" => $root_id]);
 
-			for ($i = 0; $i < $nest_level; $i++)
-				$line["title"] = " " . $line["title"];
+			while ($line = $sth->fetch()) {
 
-			$is_selected = in_array("CAT:".$line["id"], $default_ids) ? "selected=\"1\"" : "";
+				for ($i = 0; $i < $nest_level; $i++)
+					$line["title"] = " " . $line["title"];
 
-			printf("<option $is_selected value='CAT:%d'>%s</option>",
-				$line["id"], htmlspecialchars($line["title"]));
+				$is_selected = in_array("CAT:".$line["id"], $default_ids) ? "selected=\"1\"" : "";
 
-			if ($line["num_children"] > 0)
-				$this->feed_multi_select($id, $default_ids, $attributes,
-					$include_all_feeds, $line["id"], $nest_level+1);
+				$rv .= sprintf("<option $is_selected value='CAT:%d'>%s</option>",
+					$line["id"], htmlspecialchars($line["title"]));
 
-			$f_sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds
-					WHERE cat_id = ? AND owner_uid = ? ORDER BY title");
+				if ($line["num_children"] > 0)
+					$rv .= $this->feed_multi_select($id, $default_ids, $attributes,
+						$include_all_feeds, $line["id"], $nest_level+1);
 
-			$f_sth->execute([$line['id'], $_SESSION['uid']]);
+				$f_sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds
+						WHERE cat_id = ? AND owner_uid = ? ORDER BY title");
 
-			while ($fline = $f_sth->fetch()) {
-				$is_selected = (in_array($fline["id"], $default_ids)) ? "selected=\"1\"" : "";
+				$f_sth->execute([$line['id'], $_SESSION['uid']]);
 
-				$fline["title"] = " " . $fline["title"];
+				while ($fline = $f_sth->fetch()) {
+					$is_selected = (in_array($fline["id"], $default_ids)) ? "selected=\"1\"" : "";
 
-				for ($i = 0; $i < $nest_level; $i++)
 					$fline["title"] = " " . $fline["title"];
 
-				printf("<option $is_selected value='%d'>%s</option>",
-					$fline["id"], htmlspecialchars($fline["title"]));
-			}
-		}
+					for ($i = 0; $i < $nest_level; $i++)
+						$fline["title"] = " " . $fline["title"];
 
-		if (!$root_id) {
-			$is_selected = in_array("CAT:0", $default_ids) ? "selected=\"1\"" : "";
+					$rv .= sprintf("<option $is_selected value='%d'>%s</option>",
+						$fline["id"], htmlspecialchars($fline["title"]));
+				}
+			}
 
-			printf("<option $is_selected value='CAT:0'>%s</option>",
-				__("Uncategorized"));
+			if (!$root_id) {
+				$is_selected = in_array("CAT:0", $default_ids) ? "selected=\"1\"" : "";
 
-			$f_sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds
-					WHERE cat_id IS NULL AND owner_uid = ? ORDER BY title");
-			$f_sth->execute([$_SESSION['uid']]);
+				$rv .= sprintf("<option $is_selected value='CAT:0'>%s</option>",
+					__("Uncategorized"));
 
-			while ($fline = $f_sth->fetch()) {
-				$is_selected = in_array($fline["id"], $default_ids) ? "selected=\"1\"" : "";
+				$f_sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds
+						WHERE cat_id IS NULL AND owner_uid = ? ORDER BY title");
+				$f_sth->execute([$_SESSION['uid']]);
 
-				$fline["title"] = " " . $fline["title"];
+				while ($fline = $f_sth->fetch()) {
+					$is_selected = in_array($fline["id"], $default_ids) ? "selected=\"1\"" : "";
 
-				for ($i = 0; $i < $nest_level; $i++)
 					$fline["title"] = " " . $fline["title"];
 
-				printf("<option $is_selected value='%d'>%s</option>",
-					$fline["id"], htmlspecialchars($fline["title"]));
+					for ($i = 0; $i < $nest_level; $i++)
+						$fline["title"] = " " . $fline["title"];
+
+					$rv .= sprintf("<option $is_selected value='%d'>%s</option>",
+						$fline["id"], htmlspecialchars($fline["title"]));
+				}
 			}
-		}
 
-	} else {
-		$sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds
-				WHERE owner_uid = ? ORDER BY title");
-		$sth->execute([$_SESSION['uid']]);
+		} else {
+			$sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds
+					WHERE owner_uid = ? ORDER BY title");
+			$sth->execute([$_SESSION['uid']]);
 
-		while ($line = $sth->fetch()) {
+			while ($line = $sth->fetch()) {
 
-			$is_selected = (in_array($line["id"], $default_ids)) ? "selected=\"1\"" : "";
+				$is_selected = (in_array($line["id"], $default_ids)) ? "selected=\"1\"" : "";
 
-			printf("<option $is_selected value='%d'>%s</option>",
-				$line["id"], htmlspecialchars($line["title"]));
+				$rv .= sprintf("<option $is_selected value='%d'>%s</option>",
+					$line["id"], htmlspecialchars($line["title"]));
+			}
 		}
-	}
 
-	if (!$root_id) {
-		print "</select>";
+		if (!$root_id) {
+			$rv .= "</select>";
+		}
+
+		return $rv;
 	}
 }
-}
diff --git a/classes/rpc.php b/classes/rpc.php
index 643ad29d8275a84cdd51ed9c7cee1a63f703d453..20a11b994e0cfafd23e7bc6ea4dcb4790cf47b35 100755
--- a/classes/rpc.php
+++ b/classes/rpc.php
@@ -399,7 +399,7 @@ class RPC extends Handler_Protected {
 
 		$params["icon_indicator_white"] = $this->image_to_base64("images/indicator_white.gif");
 
-		$params["labels"] = Labels::get_all_labels($_SESSION["uid"]);
+		$params["labels"] = Labels::get_all($_SESSION["uid"]);
 
 		return $params;
 	}
@@ -430,7 +430,7 @@ class RPC extends Handler_Protected {
 		$data["max_feed_id"] = (int) $max_feed_id;
 		$data["num_feeds"] = (int) $num_feeds;
 		$data['cdm_expanded'] = get_pref('CDM_EXPANDED');
-		$data["labels"] = Labels::get_all_labels($_SESSION["uid"]);
+		$data["labels"] = Labels::get_all($_SESSION["uid"]);
 
 		if (LOG_DESTINATION == 'sql' && $_SESSION['access_level'] >= 10) {
 			if (DB_TYPE == 'pgsql') {
diff --git a/include/controls.php b/include/controls.php
index ae5fba739b8c52ed7ea806721512a67b13605a6b..e3349d31b73eda4bfe06cdb78daf476c7844b481 100755
--- a/include/controls.php
+++ b/include/controls.php
@@ -60,21 +60,11 @@
       return $rv;
    }
 
-   function select_labels(string $name, string $value, array $attributes = [], string $id = "") {
-      $pdo = \Db::pdo();
-
-      $sth = $pdo->prepare("SELECT caption FROM ttrss_labels2
-            WHERE owner_uid = ? ORDER BY caption");
-      $sth->execute([$_SESSION['uid']]);
-
-      $values = [];
-
-      while ($row = $sth->fetch()) {
-         array_push($values, $row["caption"]);
-      }
+   /*function select_labels(string $name, string $value, array $attributes = [], string $id = "") {
+      $values = \Labels::get_as_hash($_SESSION["uid"]);
 
       return select_tag($name, $value, $values, $attributes, $id);
-   }
+   }*/
 
    function select_hash(string $name, $value, array $values, array $attributes = [], string $id = "") {
       $attributes_str = attributes_to_string($attributes);
diff --git a/js/CommonFilters.js b/js/CommonFilters.js
index bd6808f5940063ed5b55dd83d6bf70ffc5634cf8..75e1fa8af43a968313718e8548f81d8c760c19f6 100644
--- a/js/CommonFilters.js
+++ b/js/CommonFilters.js
@@ -7,26 +7,17 @@
 
 /* exported Filters */
 const	Filters = {
-	edit: function(id) { // if no id, new filter dialog
-		let query;
-
-		if (!App.isPrefs()) {
-			query = {
-				op: "pref-filters", method: "edit",
-				feed: Feeds.getActive(), is_cat: Feeds.activeIsCat()
-			};
-		} else {
-			query = {op: "pref-filters", method: "edit", id: id};
-		}
+	edit: function(filter_id = null) { // if no id, new filter dialog
 
 		const dialog = new fox.SingleUseDialog({
 			id: "filterEditDlg",
-			title: id ? __("Edit Filter") : __("Create Filter"),
+			title: filter_id ? __("Edit Filter") : __("Create Filter"),
 			ACTION_TAG: 4,
 			ACTION_SCORE: 6,
 			ACTION_LABEL: 7,
 			ACTION_PLUGIN: 9,
 			PARAM_ACTIONS: [4, 6, 7, 9],
+			filter_info: {},
 			test: function() {
 				const test_dialog = new fox.SingleUseDialog({
 					title: "Test Filter",
@@ -116,16 +107,17 @@ const	Filters = {
 
 				test_dialog.show();
 			},
-			createNewRuleElement: function(parentNode, replaceNode) {
+			insertRule: function(parentNode, replaceNode) {
 				const rule = dojo.formToJson("filter_new_rule_form");
 
 				xhr.post("backend.php", {op: "pref-filters", method: "printrulename", rule: rule}, (reply) => {
 					try {
 						const li = document.createElement('li');
+						li.addClassName("rule");
 
-						li.innerHTML = `<input dojoType='dijit.form.CheckBox' type='checkbox' onclick='Lists.onRowChecked(this)'>
-								<span onclick='App.dialogOf(this).onRuleClicked(this)'>${reply}</span>
-							${App.FormFields.hidden_tag("rule[]", rule)}`;
+						li.innerHTML = `${App.FormFields.checkbox_tag("", false, {onclick: 'Lists.onRowChecked(this)'})}
+								<span class="name" onclick='App.dialogOf(this).onRuleClicked(this)'>${reply}</span>
+								<span class="payload" >${App.FormFields.hidden_tag("rule[]", rule)}</span>`;
 
 						dojo.parser.parse(li);
 
@@ -139,7 +131,7 @@ const	Filters = {
 					}
 				});
 			},
-			createNewActionElement: function(parentNode, replaceNode) {
+			insertAction: function(parentNode, replaceNode) {
 				const form = document.forms["filter_new_action_form"];
 
 				if (form.action_id.value == 7) {
@@ -153,10 +145,11 @@ const	Filters = {
 				xhr.post("backend.php", { op: "pref-filters", method: "printactionname", action: action }, (reply) => {
 					try {
 						const li = document.createElement('li');
+						li.addClassName("action");
 
-						li.innerHTML = `<input dojoType='dijit.form.CheckBox' type='checkbox' onclick='Lists.onRowChecked(this)'>
-								<span onclick='App.dialogOf(this).onActionClicked(this)'>${reply}</span>
-							${App.FormFields.hidden_tag("action[]", action)}`;
+						li.innerHTML = `${App.FormFields.checkbox_tag("", false, {onclick: 'Lists.onRowChecked(this)'})}
+								<span class="name" onclick='App.dialogOf(this).onActionClicked(this)'>${reply}</span>
+								<span class="payload">${App.FormFields.hidden_tag("action[]", action)}</span>`;
 
 						dojo.parser.parse(li);
 
@@ -171,30 +164,84 @@ const	Filters = {
 					}
 				});
 			},
-			editRule: function(replaceNode, ruleStr) {
+			editRule: function(replaceNode, ruleStr = null) {
 				const edit_rule_dialog = new fox.SingleUseDialog({
 					id: "filterNewRuleDlg",
 					title: ruleStr ? __("Edit rule") : __("Add rule"),
 					execute: function () {
 						if (this.validate()) {
-							dialog.createNewRuleElement(App.byId("filterDlg_Matches"), replaceNode);
+							dialog.insertRule(App.byId("filterDlg_Matches"), replaceNode);
 							this.hide();
 						}
 					},
 					content: __('Loading, please wait...'),
 				});
 
-				const tmph = dojo.connect(edit_rule_dialog, "onShow", null, function (/* e */) {
+				const tmph = dojo.connect(edit_rule_dialog, "onShow", null, function () {
 					dojo.disconnect(tmph);
 
-					xhr.post("backend.php", {op: 'pref-filters', method: 'newrule', rule: ruleStr}, (reply) => {
-						edit_rule_dialog.attr('content', reply);
+					let rule;
+
+					if (ruleStr) {
+						rule = JSON.parse(ruleStr);
+					} else {
+						rule = {
+							reg_exp: "",
+							filter_type: 1,
+							feed_id: ["0"],
+							inverse: false,
+						};
+					}
+
+					console.log(rule, dialog.filter_info);
+
+					xhr.json("backend.php", {op: "pref-filters", method: "editrule", ids: rule.feed_id.join(",")}, function (editrule) {
+						edit_rule_dialog.attr('content',
+							`
+							<form name="filter_new_rule_form" id="filter_new_rule_form" onsubmit="return false">
+
+								<section>
+									<textarea dojoType="fox.form.ValidationTextArea"
+										required="true" id="filterDlg_regExp" ValidRegExp="true"
+										rows="4" style="font-size : 14px; width : 530px; word-break: break-all"
+										name="reg_exp">${rule.reg_exp}</textarea>
+
+									<div dojoType="dijit.Tooltip" id="filterDlg_regExp_tip" connectId="filterDlg_regExp" position="below"></div>
+
+									<fieldset>
+										<label class="checkbox">".
+											${App.FormFields.checkbox_tag("inverse", rule.inverse)}
+											${__("Inverse regular expression matching")}
+										</label>
+									</fieldset>
+									<fieldset>
+										<label style="display : inline">${__("on field")}</label>
+										${App.FormFields.select_hash("filter_type", rule.filter_type, dialog.filter_info.filter_types)}
+										<label style="padding-left : 10px; display : inline">${__("in")}</label>
+									</fieldset>
+									<fieldset>
+										<span id="filterDlg_feeds">
+											${editrule.multiselect}
+										</span>
+									</fieldset>
+								</section>
+
+								<footer>
+									${App.FormFields.button_tag(App.FormFields.icon("help") + " " + __("More info"), "", {class: 'pull-left alt-info',
+										onclick: "window.open('https://tt-rss.org/wiki/ContentFilters')"})}
+									${App.FormFields.submit_tag(__("Save rule"), {onclick: "App.dialogOf(this).execute()"})}
+									${App.FormFields.cancel_dialog_tag(__("Cancel"))}
+								</footer>
+
+							</form>
+						`);
 					});
+
 				});
 
 				edit_rule_dialog.show();
 			},
-			editAction: function(replaceNode, actionStr) {
+			/*editAction: function(replaceNode, actionStr) {
 				const edit_action_dialog = new fox.SingleUseDialog({
 					title: actionStr ? __("Edit action") : __("Add action"),
 					hideOrShowActionParam: function(sender) {
@@ -221,7 +268,7 @@ const	Filters = {
 					}
 				});
 
-				const tmph = dojo.connect(edit_action_dialog, "onShow", null, function (/* e */) {
+				const tmph = dojo.connect(edit_action_dialog, "onShow", null, function () {
 					dojo.disconnect(tmph);
 
 					xhr.post("backend.php", {op: 'pref-filters', method: 'newaction', action: actionStr}, (reply) => {
@@ -234,22 +281,65 @@ const	Filters = {
 				});
 
 				edit_action_dialog.show();
-			},
+			}, */
+			/*editAction: function(replaceNode, actionStr) {
+				const edit_action_dialog = new fox.SingleUseDialog({
+					title: actionStr ? __("Edit action") : __("Add action"),
+					hideOrShowActionParam: function(sender) {
+						const action = parseInt(sender.value);
+
+						dijit.byId("filterDlg_actionParam").domNode.hide();
+						dijit.byId("filterDlg_actionParamLabel").domNode.hide();
+						dijit.byId("filterDlg_actionParamPlugin").domNode.hide();
+
+						// if selected action supports parameters, enable params field
+						if (action == dialog.ACTION_LABEL) {
+							dijit.byId("filterDlg_actionParamLabel").domNode.show();
+						} else if (action == dialog.ACTION_PLUGIN) {
+							dijit.byId("filterDlg_actionParamPlugin").domNode.show();
+						} else if (dialog.PARAM_ACTIONS.indexOf(action) != -1) {
+							dijit.byId("filterDlg_actionParam").domNode.show();
+						}
+					},
+					execute: function () {
+						if (this.validate()) {
+							dialog.createNewActionElement(App.byId("filterDlg_Actions"), replaceNode);
+							this.hide();
+						}
+					}
+				});
+
+				const tmph = dojo.connect(edit_action_dialog, "onShow", null, function () {
+					dojo.disconnect(tmph);
+
+					xhr.post("backend.php", {op: 'pref-filters', method: 'newaction', action: actionStr}, (reply) => {
+						edit_action_dialog.attr('content', reply);
+
+						setTimeout(() => {
+							edit_action_dialog.hideOrShowActionParam(dijit.byId("filterDlg_actionSelect").attr('value'));
+						}, 250);
+					});
+				});
+
+				edit_action_dialog.show();
+			},*/
 			selectRules: function (select) {
 				Lists.select("filterDlg_Matches", select);
 			},
 			selectActions: function (select) {
 				Lists.select("filterDlg_Actions", select);
 			},
-			onRuleClicked: function (e) {
-				const li = e.closest('li');
-				const rule = li.querySelector('input[name="rule[]"]').value
+			onRuleClicked: function (elem) {
+
+				const li = elem.closest('li');
+				const rule = li.querySelector('input[name="rule[]"]').value;
 
 				this.editRule(li, rule);
 			},
-			onActionClicked: function (e) {
-				const li = e.closest('li');
-				const action = li.querySelector('input[name="action[]"]').value
+			onActionClicked: function (elem) {
+
+				const li = elem.closest('li');
+				const action = li.querySelector('input[name="action[]"]').value;
 
 				this.editAction(li, action);
 			},
@@ -302,13 +392,141 @@ const	Filters = {
 			content: __("Loading, please wait...")
 		});
 
-
-
 		const tmph = dojo.connect(dialog, 'onShow', function () {
 			dojo.disconnect(tmph);
 
-			xhr.post("backend.php", query, function (reply) {
-				dialog.attr('content', reply);
+			const query = {op: "pref-filters", method: "edit", id: filter_id};
+
+			/*if (!App.isPrefs()) {
+				query = {
+					op: "pref-filters", method: "edit",
+					feed: Feeds.getActive(), is_cat: Feeds.activeIsCat()
+				};
+			} else {
+				query = {op: "pref-filters", method: "edit", id: id};
+			}*/
+
+			xhr.json("backend.php", query, function (filter) {
+
+				dialog.filter_info = filter;
+
+				const options = {
+					enabled: [ filter.enabled, __('Enabled') ],
+					match_any_rule: [ filter.match_any_rule, __('Match any rule') ],
+					inverse: [ filter.inverse, __('Inverse matching') ],
+				};
+
+				dialog.attr('content',
+				`
+					<form onsubmit='return false'>
+
+						${App.FormFields.hidden_tag("op", "pref-filters")}
+						${App.FormFields.hidden_tag("id", filter_id)}
+						${App.FormFields.hidden_tag("method", filter_id ? "editSave" : "add")}
+						${App.FormFields.hidden_tag("csrf_token", App.getInitParam('csrf_token'))}
+
+						<section>
+							<input required="true" dojoType="dijit.form.ValidationTextBox" style="width : 530px"
+								placeholder="${__("Title")}" name="title" value="${App.escapeHtml(filter.title)}">
+						</section>
+
+						<div dojoType="dijit.layout.TabContainer" style="height : 300px">
+							<div dojoType="dijit.layout.ContentPane" title="${__('Match')}">
+								<div style="padding : 0" dojoType="dijit.layout.BorderContainer" gutters="false">
+									<div dojoType="fox.Toolbar" region="top">
+										<div dojoType="fox.form.DropDownButton">
+											<span>${__("Select")}</span>
+											<div dojoType="dijit.Menu" style="display: none;">
+												<!-- can"t use App.dialogOf() here because DropDownButton is not a child of the Dialog -->
+												<div onclick="dijit.byId('filterEditDlg').selectRules(true)"
+													dojoType="dijit.MenuItem">${__("All")}</div>
+												<div onclick="dijit.byId('filterEditDlg').selectRules(false)"
+													dojoType="dijit.MenuItem">${__("None")}</div>
+											</div>
+										</div>
+										<button dojoType="dijit.form.Button" onclick="App.dialogOf(this).addRule()">
+											${__("Add")}
+										</button>
+										<button dojoType="dijit.form.Button" onclick="App.dialogOf(this).deleteRule()">
+											${__("Delete")}
+										</button>
+									</div>
+									<div dojoType="dijit.layout.ContentPane" region="center">
+										<ul id="filterDlg_Matches">
+											${filter.rules.map((rule) => `
+												<li class='rule'>
+													${App.FormFields.checkbox_tag("", false, "", {onclick: 'Lists.onRowChecked(this)'})}
+													<span class='name' onclick='App.dialogOf(this).onRuleClicked(this)'>${rule.name}</span>
+													<span class='payload'>${App.FormFields.hidden_tag("rule[]", JSON.stringify(rule))}</span>
+												</li>
+											`).join("")}
+										</ul>
+									</div>
+								</div>
+							</div>
+							<div dojoType="dijit.layout.ContentPane" title="${__('Apply actions')}">
+								<div style="padding : 0" dojoType="dijit.layout.BorderContainer" gutters="false">
+									<div dojoType="fox.Toolbar" region="top">
+										<div dojoType="fox.form.DropDownButton">
+											<span>${__("Select")}</span>
+											<div dojoType="dijit.Menu" style="display: none">
+												<div onclick="dijit.byId('filterEditDlg').selectActions(true)"
+													dojoType="dijit.MenuItem">${__("All")}</div>
+												<div onclick="dijit.byId('filterEditDlg').selectActions(false)"
+													dojoType="dijit.MenuItem">${__("None")}</div>
+												</div>
+											</div>
+										<button dojoType="dijit.form.Button" onclick="App.dialogOf(this).addAction()">".
+											${__("Add")}
+										</button>
+										<button dojoType="dijit.form.Button" onclick="App.dialogOf(this).deleteAction()">".
+											${__("Delete")}
+										</button>
+									</div>
+									<div dojoType="dijit.layout.ContentPane" region="center">
+										<ul id="filterDlg_Actions">
+											${filter.actions.map((action) => `
+											<li class='rule'>
+												${App.FormFields.checkbox_tag("", false, "", {onclick: 'Lists.onRowChecked(this)'})}
+												<span class='name' onclick='App.dialogOf(this).onRuleClicked(this)'>${App.escapeHtml(action.name)}</span>
+												<span class='payload'>${App.FormFields.hidden_tag("action[]", JSON.stringify(action))}</span>
+											</li>
+											`).join("")}
+										</ul>
+									</div>
+								</div>
+							</div>
+						</div>
+
+						<br/>
+
+						<section class="narrow">
+							${Object.keys(options).map((name) =>
+								`
+								<fieldset class='narrow'>
+									<label class="checkbox">
+										${App.FormFields.checkbox_tag(name, options[name][0])}
+										${options[name][1]}
+									</label>
+								</fieldset>
+								`).join("")}
+						</section>
+
+						<footer>
+							${filter_id ?
+							`
+								${App.FormFields.button_tag(__("Remove"), "", {class: "pull-left alt-danger", onclick: "App.dialogOf(this).removeFilter()"})}
+								${App.FormFields.button_tag(__("Test"), "", {class: "alt-info", onclick: "App.dialogOf(this).test()"})}
+								${App.FormFields.submit_tag(__("Save"), {onclick: "App.dialogOf(this).execute()"})}
+								${App.FormFields.cancel_dialog_tag(__("Cancel"))}
+							` : `
+								${App.FormFields.button_tag(__("Test"), "", {class: "alt-info", onclick: "App.dialogOf(this).test()"})}
+								${App.FormFields.submit_tag(__("Create"), {onclick: "App.dialogOf(this).execute()"})}
+								${App.FormFields.cancel_dialog_tag(__("Cancel"))}
+							`}
+						</footer>
+					</form>
+				`);
 
 				if (!App.isPrefs()) {
 					const selectedText = App.getSelectedText();
diff --git a/plugins/auto_assign_labels/init.php b/plugins/auto_assign_labels/init.php
index 3fa4ad8c071fe634985ecb15881fc24152a968f3..341895cef5528cf9f03dd1216006ec428f3afd96 100755
--- a/plugins/auto_assign_labels/init.php
+++ b/plugins/auto_assign_labels/init.php
@@ -19,6 +19,7 @@ class Auto_Assign_Labels extends Plugin {
 	function get_all_labels_filter_format($owner_uid) {
 		$rv = array();
 
+		// TODO: use Labels::get_all()
 		$sth = $this->pdo->prepare("SELECT id, fg_color, bg_color, caption FROM ttrss_labels2 WHERE owner_uid = ?");
 		$sth->execute([$owner_uid]);
 
diff --git a/themes/compact.css b/themes/compact.css
index 92e9928c81268c4635b48ec59628bf19925e1c7c..16bdcf1f05997dd9cf3870804edbd921ada2310b 100644
--- a/themes/compact.css
+++ b/themes/compact.css
@@ -822,16 +822,18 @@ body.ttrss_main #headlines-spacer a:hover {
 }
 body.ttrss_main ul#filterDlg_Matches,
 body.ttrss_main ul#filterDlg_Actions {
-  max-height: 100px;
-  overflow: auto;
   list-style-type: none;
-  border-style: solid;
-  border-color: #ddd;
-  border-width: 1px 1px 1px 1px;
-  background-color: white;
-  margin: 0px 0px 5px 0px;
-  padding: 4px;
-  min-height: 16px;
+  margin: 0;
+  padding: 0;
+  /*max-height : 100px;
+		overflow : auto;
+		border-style : solid;
+		border-color : @border-default;
+		border-width : 1px 1px 1px 1px;
+		background-color : @default-bg;
+		margin : 0px 0px 5px 0px;
+		padding : 4px;
+		min-height : 16px;*/
 }
 body.ttrss_main ul#filterDlg_Matches li,
 body.ttrss_main ul#filterDlg_Actions li {
diff --git a/themes/compact_night.css b/themes/compact_night.css
index 29eff9cb998b7edf317ef0eca1ebeede6b4e5667..37adf3fda385faaa7432f5236cc8ff56abce611e 100644
--- a/themes/compact_night.css
+++ b/themes/compact_night.css
@@ -822,16 +822,18 @@ body.ttrss_main #headlines-spacer a:hover {
 }
 body.ttrss_main ul#filterDlg_Matches,
 body.ttrss_main ul#filterDlg_Actions {
-  max-height: 100px;
-  overflow: auto;
   list-style-type: none;
-  border-style: solid;
-  border-color: #222;
-  border-width: 1px 1px 1px 1px;
-  background-color: #333;
-  margin: 0px 0px 5px 0px;
-  padding: 4px;
-  min-height: 16px;
+  margin: 0;
+  padding: 0;
+  /*max-height : 100px;
+		overflow : auto;
+		border-style : solid;
+		border-color : @border-default;
+		border-width : 1px 1px 1px 1px;
+		background-color : @default-bg;
+		margin : 0px 0px 5px 0px;
+		padding : 4px;
+		min-height : 16px;*/
 }
 body.ttrss_main ul#filterDlg_Matches li,
 body.ttrss_main ul#filterDlg_Actions li {
diff --git a/themes/light.css b/themes/light.css
index 583f9cabb9207e7df328c7805aba1b827f4d9c03..0f2ffc1b622fad847b9c55788f4e6d58e2b87662 100644
--- a/themes/light.css
+++ b/themes/light.css
@@ -822,16 +822,18 @@ body.ttrss_main #headlines-spacer a:hover {
 }
 body.ttrss_main ul#filterDlg_Matches,
 body.ttrss_main ul#filterDlg_Actions {
-  max-height: 100px;
-  overflow: auto;
   list-style-type: none;
-  border-style: solid;
-  border-color: #ddd;
-  border-width: 1px 1px 1px 1px;
-  background-color: white;
-  margin: 0px 0px 5px 0px;
-  padding: 4px;
-  min-height: 16px;
+  margin: 0;
+  padding: 0;
+  /*max-height : 100px;
+		overflow : auto;
+		border-style : solid;
+		border-color : @border-default;
+		border-width : 1px 1px 1px 1px;
+		background-color : @default-bg;
+		margin : 0px 0px 5px 0px;
+		padding : 4px;
+		min-height : 16px;*/
 }
 body.ttrss_main ul#filterDlg_Matches li,
 body.ttrss_main ul#filterDlg_Actions li {
diff --git a/themes/light/tt-rss.less b/themes/light/tt-rss.less
index b1895f318585d80fbb4f352cbe6a193cf1e1de39..2794d81775aa76d6bd94e7453a5c7ff374496973 100644
--- a/themes/light/tt-rss.less
+++ b/themes/light/tt-rss.less
@@ -955,16 +955,18 @@ body.ttrss_main {
 	}
 
 	ul#filterDlg_Matches, ul#filterDlg_Actions {
-		max-height : 100px;
-		overflow : auto;
 		list-style-type : none;
+		margin : 0;
+		padding: 0;
+		/*max-height : 100px;
+		overflow : auto;
 		border-style : solid;
 		border-color : @border-default;
 		border-width : 1px 1px 1px 1px;
 		background-color : @default-bg;
 		margin : 0px 0px 5px 0px;
 		padding : 4px;
-		min-height : 16px;
+		min-height : 16px;*/
 	}
 
 	ul#filterDlg_Matches li, ul#filterDlg_Actions li {
diff --git a/themes/night.css b/themes/night.css
index e428d8aa76979ae5d3ab50fb2a31b16a70a5fa4a..e012c92b2a0f7949951f875a82a1df72efad61f5 100644
--- a/themes/night.css
+++ b/themes/night.css
@@ -823,16 +823,18 @@ body.ttrss_main #headlines-spacer a:hover {
 }
 body.ttrss_main ul#filterDlg_Matches,
 body.ttrss_main ul#filterDlg_Actions {
-  max-height: 100px;
-  overflow: auto;
   list-style-type: none;
-  border-style: solid;
-  border-color: #222;
-  border-width: 1px 1px 1px 1px;
-  background-color: #333;
-  margin: 0px 0px 5px 0px;
-  padding: 4px;
-  min-height: 16px;
+  margin: 0;
+  padding: 0;
+  /*max-height : 100px;
+		overflow : auto;
+		border-style : solid;
+		border-color : @border-default;
+		border-width : 1px 1px 1px 1px;
+		background-color : @default-bg;
+		margin : 0px 0px 5px 0px;
+		padding : 4px;
+		min-height : 16px;*/
 }
 body.ttrss_main ul#filterDlg_Matches li,
 body.ttrss_main ul#filterDlg_Actions li {
diff --git a/themes/night_blue.css b/themes/night_blue.css
index c9ccaf7374ef742f1b3046872ea9c23b1dca55f6..b49a496e3dcdc083a26ccfc85228e69820380542 100644
--- a/themes/night_blue.css
+++ b/themes/night_blue.css
@@ -823,16 +823,18 @@ body.ttrss_main #headlines-spacer a:hover {
 }
 body.ttrss_main ul#filterDlg_Matches,
 body.ttrss_main ul#filterDlg_Actions {
-  max-height: 100px;
-  overflow: auto;
   list-style-type: none;
-  border-style: solid;
-  border-color: #222;
-  border-width: 1px 1px 1px 1px;
-  background-color: #333;
-  margin: 0px 0px 5px 0px;
-  padding: 4px;
-  min-height: 16px;
+  margin: 0;
+  padding: 0;
+  /*max-height : 100px;
+		overflow : auto;
+		border-style : solid;
+		border-color : @border-default;
+		border-width : 1px 1px 1px 1px;
+		background-color : @default-bg;
+		margin : 0px 0px 5px 0px;
+		padding : 4px;
+		min-height : 16px;*/
 }
 body.ttrss_main ul#filterDlg_Matches li,
 body.ttrss_main ul#filterDlg_Actions li {