Просмотр исходного кода

Offer to add a prefix when resharing user labels (#9236)

* Offer to add a prefix when resharing user labels
Improve https://github.com/FreshRSS/FreshRSS/pull/8112

* make fix-all
Alexandre Alapetite 1 неделя назад
Родитель
Сommit
f902047204

+ 7 - 7
README.fr.md

@@ -234,20 +234,20 @@ Voir le [dépôt dédié à ces extensions](https://github.com/FreshRSS/Extensio
 | Ελληνικά (el) | ■■■■■■■・・・ 75% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fel+%2F%28TODO%7CDIRTY%29%24%2F) |
 | English (en) | ■■■■■■■■■■ 100% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fen+%2F%28TODO%7CDIRTY%29%24%2F) |
 | English (United States) (en-US) | ■■■■■■■■■■ 100% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fen-US+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Español (es) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fes+%2F%28TODO%7CDIRTY%29%24%2F) |
-| فارسی (fa) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffa+%2F%28TODO%7CDIRTY%29%24%2F) |
+| Español (es) | ■■■■■■■■■・ 98% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fes+%2F%28TODO%7CDIRTY%29%24%2F) |
+| فارسی (fa) | ■■■■■■■■■・ 98% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffa+%2F%28TODO%7CDIRTY%29%24%2F) |
 | Suomi (fi) | ■■■■■■■■■・ 91% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffi+%2F%28TODO%7CDIRTY%29%24%2F) |
 | Français (fr) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffr+%2F%28TODO%7CDIRTY%29%24%2F) |
 | עברית (he) | ■■■■・・・・・・ 41% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fhe+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Magyar (hu) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fhu+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Bahasa Indonesia (id) | ■■■■■■■■■■ 100% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fid+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Italiano (it) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fit+%2F%28TODO%7CDIRTY%29%24%2F) |
+| Magyar (hu) | ■■■■■■■■■・ 98% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fhu+%2F%28TODO%7CDIRTY%29%24%2F) |
+| Bahasa Indonesia (id) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fid+%2F%28TODO%7CDIRTY%29%24%2F) |
+| Italiano (it) | ■■■■■■■■■・ 98% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fit+%2F%28TODO%7CDIRTY%29%24%2F) |
 | 日本語 (ja) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fja+%2F%28TODO%7CDIRTY%29%24%2F) |
 | 한국어 (ko) | ■■■■■■■■・・ 80% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fko+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Lietuvių (lt) | ■■■■■■■■■■ 100% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Flt+%2F%28TODO%7CDIRTY%29%24%2F) |
+| Lietuvių (lt) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Flt+%2F%28TODO%7CDIRTY%29%24%2F) |
 | Latviešu (lv) | ■■■■■■■■・・ 81% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Flv+%2F%28TODO%7CDIRTY%29%24%2F) |
 | Nederlands (nl) | ■■■■■■■■■・ 98% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fnl+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Occitan (oc) | ■■■■■■■・・・ 74% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Foc+%2F%28TODO%7CDIRTY%29%24%2F) |
+| Occitan (oc) | ■■■■■■■・・・ 73% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Foc+%2F%28TODO%7CDIRTY%29%24%2F) |
 | Polski (pl) | ■■■■■■■■■・ 98% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fpl+%2F%28TODO%7CDIRTY%29%24%2F) |
 | Português (Brasil) (pt-BR) | ■■■■■■■■■・ 98% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fpt-BR+%2F%28TODO%7CDIRTY%29%24%2F) |
 | Português (Portugal) (pt-PT) | ■■■■■■■■・・ 80% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fpt-PT+%2F%28TODO%7CDIRTY%29%24%2F) |

+ 7 - 7
README.md

@@ -130,20 +130,20 @@ See the [repository dedicated to those extensions](https://github.com/FreshRSS/E
 | Ελληνικά (el) | ■■■■■■■・・・ 75% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fel+%2F%28TODO%7CDIRTY%29%24%2F) |
 | English (en) | ■■■■■■■■■■ 100% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fen+%2F%28TODO%7CDIRTY%29%24%2F) |
 | English (United States) (en-US) | ■■■■■■■■■■ 100% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fen-US+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Español (es) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fes+%2F%28TODO%7CDIRTY%29%24%2F) |
-| فارسی (fa) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffa+%2F%28TODO%7CDIRTY%29%24%2F) |
+| Español (es) | ■■■■■■■■■・ 98% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fes+%2F%28TODO%7CDIRTY%29%24%2F) |
+| فارسی (fa) | ■■■■■■■■■・ 98% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffa+%2F%28TODO%7CDIRTY%29%24%2F) |
 | Suomi (fi) | ■■■■■■■■■・ 91% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffi+%2F%28TODO%7CDIRTY%29%24%2F) |
 | Français (fr) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffr+%2F%28TODO%7CDIRTY%29%24%2F) |
 | עברית (he) | ■■■■・・・・・・ 41% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fhe+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Magyar (hu) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fhu+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Bahasa Indonesia (id) | ■■■■■■■■■■ 100% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fid+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Italiano (it) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fit+%2F%28TODO%7CDIRTY%29%24%2F) |
+| Magyar (hu) | ■■■■■■■■■・ 98% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fhu+%2F%28TODO%7CDIRTY%29%24%2F) |
+| Bahasa Indonesia (id) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fid+%2F%28TODO%7CDIRTY%29%24%2F) |
+| Italiano (it) | ■■■■■■■■■・ 98% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fit+%2F%28TODO%7CDIRTY%29%24%2F) |
 | 日本語 (ja) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fja+%2F%28TODO%7CDIRTY%29%24%2F) |
 | 한국어 (ko) | ■■■■■■■■・・ 80% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fko+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Lietuvių (lt) | ■■■■■■■■■■ 100% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Flt+%2F%28TODO%7CDIRTY%29%24%2F) |
+| Lietuvių (lt) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Flt+%2F%28TODO%7CDIRTY%29%24%2F) |
 | Latviešu (lv) | ■■■■■■■■・・ 81% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Flv+%2F%28TODO%7CDIRTY%29%24%2F) |
 | Nederlands (nl) | ■■■■■■■■■・ 98% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fnl+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Occitan (oc) | ■■■■■■■・・・ 74% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Foc+%2F%28TODO%7CDIRTY%29%24%2F) |
+| Occitan (oc) | ■■■■■■■・・・ 73% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Foc+%2F%28TODO%7CDIRTY%29%24%2F) |
 | Polski (pl) | ■■■■■■■■■・ 98% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fpl+%2F%28TODO%7CDIRTY%29%24%2F) |
 | Português (Brasil) (pt-BR) | ■■■■■■■■■・ 98% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fpt-BR+%2F%28TODO%7CDIRTY%29%24%2F) |
 | Português (Portugal) (pt-PT) | ■■■■■■■■・・ 80% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fpt-PT+%2F%28TODO%7CDIRTY%29%24%2F) |

+ 10 - 2
app/Controllers/configureController.php

@@ -541,8 +541,16 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
 			if (!empty($params['shareRss']) && ctype_digit($params['shareRss'])) {
 				$queryParams['shareRss'] = (bool)$params['shareRss'];
 			}
-			if (!empty($params['publishLabelsInsteadOfTags']) && ctype_digit($params['publishLabelsInsteadOfTags'])) {
-				$queryParams['publishLabelsInsteadOfTags'] = (bool)$params['publishLabelsInsteadOfTags'];
+
+			if (isset($params['includeUserLabels'])) {
+				$queryParams['includeUserLabels'] = !empty($params['includeUserLabels']);
+			}
+			if (isset($params['includeArticleTags'])) {
+				// Reversed logic to ease default value
+				$queryParams['excludeArticleTags'] = empty($params['includeArticleTags']);
+			}
+			if (is_string($params['userLabelPrefix'] ?? null)) {
+				$queryParams['userLabelPrefix'] = trim($params['userLabelPrefix']);
 			}
 
 			$queries = FreshRSS_Context::userConf()->queries;

+ 49 - 10
app/Models/UserQuery.php

@@ -21,7 +21,9 @@ class FreshRSS_UserQuery {
 	private string $token = '';
 	private bool $shareRss = false;
 	private bool $shareOpml = false;
-	private bool $publishLabelsInsteadOfTags = false;
+	private bool $includeUserLabels = false;
+	private bool $excludeArticleTags = false;
+	private string $userLabelPrefix = '';
 	/** @var array<int,FreshRSS_Category> $categories where the key is the category ID */
 	private array $categories;
 	/** @var array<int,FreshRSS_Tag> $labels where the key is the label ID */
@@ -44,7 +46,8 @@ class FreshRSS_UserQuery {
 
 	/**
 	 * @param array{get?:string,name?:string,order?:string,search?:string,state?:int,url?:string,token?:string,
-	 * 	shareRss?:bool,shareOpml?:bool,publishLabelsInsteadOfTags?:bool,description?:string,imageUrl?:string} $query
+	 * 	shareRss?:bool,shareOpml?:bool,includeUserLabels?:bool,excludeArticleTags?:bool,userLabelPrefix?:string,
+	 * 	publishLabelsInsteadOfTags?:bool,description?:string,imageUrl?:string} $query
 	 * @param array<FreshRSS_Category> $categories
 	 * @param array<FreshRSS_Tag> $labels
 	 */
@@ -76,6 +79,9 @@ class FreshRSS_UserQuery {
 				unset($link['name']);
 				unset($link['shareOpml']);
 				unset($link['shareRss']);
+				unset($link['includeUserLabels']);
+				unset($link['excludeArticleTags']);
+				unset($link['userLabelPrefix']);
 				unset($link['publishLabelsInsteadOfTags']);
 				$this->url = Minz_Url::display(['params' => $link]);
 			}
@@ -94,9 +100,24 @@ class FreshRSS_UserQuery {
 		if (isset($query['shareOpml'])) {
 			$this->shareOpml = $query['shareOpml'];
 		}
-		if (isset($query['publishLabelsInsteadOfTags'])) {
-			$this->publishLabelsInsteadOfTags = (bool)$query['publishLabelsInsteadOfTags'];
+
+		if (isset($query['publishLabelsInsteadOfTags'])) {	// Legacy
+			$this->includeUserLabels = (bool)$query['publishLabelsInsteadOfTags'];
+			$this->excludeArticleTags = (bool)$query['publishLabelsInsteadOfTags'];
+			if ($this->includeUserLabels) {
+				$this->userLabelPrefix = '';
+			}
+		}
+		if (isset($query['includeUserLabels'])) {
+			$this->includeUserLabels = (bool)$query['includeUserLabels'];
+		}
+		if (isset($query['excludeArticleTags'])) {
+			$this->excludeArticleTags = (bool)$query['excludeArticleTags'];
 		}
+		if (isset($query['userLabelPrefix'])) {
+			$this->userLabelPrefix = (string)$query['userLabelPrefix'];
+		}
+
 		if (isset($query['description'])) {
 			$this->description = $query['description'];
 		}
@@ -116,7 +137,7 @@ class FreshRSS_UserQuery {
 	 *
 	 * @return array{get?:string,name?:string,order?:string,search?:string,
 	 * 	state?:int,url?:string,token?:string,shareRss?:bool,shareOpml?:bool,
-	 * 	publishLabelsInsteadOfTags?:bool,description?:string,imageUrl?:string}
+	 * 	includeUserLabels?:bool,excludeArticleTags?:bool,userLabelPrefix?:string,description?:string,imageUrl?:string}
 	 */
 	public function toArray(): array {
 		return array_filter([
@@ -129,7 +150,9 @@ class FreshRSS_UserQuery {
 			'token' => $this->token,
 			'shareRss' => $this->shareRss,
 			'shareOpml' => $this->shareOpml,
-			'publishLabelsInsteadOfTags' => $this->publishLabelsInsteadOfTags,
+			'includeUserLabels' => $this->includeUserLabels,
+			'excludeArticleTags' => $this->excludeArticleTags,
+			'userLabelPrefix' => $this->userLabelPrefix,
 			'description' => $this->description,
 			'imageUrl' => $this->imageUrl,
 		], fn($v): bool => $v !== '' && $v !== 0 && $v !== false);
@@ -287,12 +310,28 @@ class FreshRSS_UserQuery {
 		return $this->shareOpml;
 	}
 
-	public function setPublishLabelsInsteadOfTags(bool $publishLabelsInsteadOfTags): void {
-		$this->publishLabelsInsteadOfTags = $publishLabelsInsteadOfTags;
+	public function setIncludeUserLabels(bool $includeUserLabels): void {
+		$this->includeUserLabels = $includeUserLabels;
+	}
+
+	public function includeUserLabels(): bool {
+		return $this->includeUserLabels;
+	}
+
+	public function setExcludeArticleTags(bool $excludeArticleTags): void {
+		$this->excludeArticleTags = $excludeArticleTags;
+	}
+
+	public function excludeArticleTags(): bool {
+		return $this->excludeArticleTags;
+	}
+
+	public function setUserLabelPrefix(string $userLabelPrefix): void {
+		$this->userLabelPrefix = $userLabelPrefix;
 	}
 
-	public function publishLabelsInsteadOfTags(): bool {
-		return $this->publishLabelsInsteadOfTags;
+	public function userLabelPrefix(): string {
+		return $this->userLabelPrefix;
 	}
 
 	protected function sharedUrl(bool $xmlEscaped = true): string {

+ 3 - 1
app/Models/View.php

@@ -128,7 +128,9 @@ class FreshRSS_View extends Minz_View {
 	public bool $internal_rendering = false;
 	public string $description = '';
 	public string $image_url = '';
-	public bool $publishLabelsInsteadOfTags = false;
+	public bool $includeUserLabels = false;
+	public bool $excludeArticleTags = false;
+	public string $userLabelPrefix = '';
 
 	// Content preview
 	public string $fatalError;

+ 2 - 1
app/i18n/cs/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'Použitý filtr:',
 			'categories' => 'Zobrazit podle kategorie',
 			'feeds' => 'Zobrazit podle kanálu',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => 'Seřadit podle data',
-			'publish_labels_instead_of_tags' => 'Replace <i>feed tags</i> by <i>user labels</i> in the shared RSS',	// TODO
 			'search' => 'Výraz',
 			'shareOpml' => 'Povolit sdílení příslušných kategorií a kanálů pomocí OPML',
 			'shareRss' => 'Povolit sdílení pomocí HTML &amp; RSS',

+ 2 - 1
app/i18n/de/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'Angewendeter Filter:',
 			'categories' => 'Nach Kategorie filtern',
 			'feeds' => 'Nach Feed filtern',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => 'Nach Datum sortieren',
-			'publish_labels_instead_of_tags' => '<i>Feed-Tags</i> im geteilten RSS durch <i>Benutzerlabel</i> ersetzen',
 			'search' => 'Suchbegriff',
 			'shareOpml' => 'Teilen via OPML mit zugehörigen Kategorien und Feeds aktivieren',
 			'shareRss' => 'Teilen via HTML &amp; RSS aktivieren',

+ 2 - 1
app/i18n/el/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'Εφαρμοσμένο φίλτρο:',
 			'categories' => 'Προβολή ανά κατηγορία',
 			'feeds' => 'Προβολή ανά ροή',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => 'Ταξινόμηση κατά ημερομηνία',
-			'publish_labels_instead_of_tags' => 'Αντικατάσταση των <i>ετικετών ροής</i> από <i>ετικέτες χρήστη</i> στο κοινοποιημένο RSS',
 			'search' => 'Έκφραση',
 			'shareOpml' => 'Ενεργοποίηση κοινοποίησης μέσω OPML των αντίστοιχων κατηγοριών και ροών',
 			'shareRss' => 'Ενεργοποίηση κοινοποίησης μέσω HTML &amp; RSS',

+ 2 - 1
app/i18n/en-US/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'Filter applied:',	// IGNORE
 			'categories' => 'Display by category',	// IGNORE
 			'feeds' => 'Display by feed',	// IGNORE
+			'include_article_tags_label' => 'Include article tags from feeds',	// IGNORE
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// IGNORE
 			'order' => 'Sort by date',	// IGNORE
-			'publish_labels_instead_of_tags' => 'Replace <i>feed tags</i> by <i>user labels</i> in the shared RSS',	// IGNORE
 			'search' => 'Expression',	// IGNORE
 			'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds',	// IGNORE
 			'shareRss' => 'Enable sharing by HTML &amp; RSS',	// IGNORE

+ 2 - 1
app/i18n/en/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'Filter applied:',
 			'categories' => 'Display by category',
 			'feeds' => 'Display by feed',
+			'include_article_tags_label' => 'Include article tags from feeds',
+			'include_user_labels_label' => 'Include user labels, with prefix:',
 			'order' => 'Sort by date',
-			'publish_labels_instead_of_tags' => 'Replace <i>feed tags</i> by <i>user labels</i> in the shared RSS',
 			'search' => 'Expression',
 			'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds',
 			'shareRss' => 'Enable sharing by HTML &amp; RSS',

+ 2 - 1
app/i18n/es/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'Filtro aplicado:',
 			'categories' => 'Mostrar por categoría',
 			'feeds' => 'Mostrar por fuente',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => 'Ordenar por fecha',
-			'publish_labels_instead_of_tags' => 'Reemplazar <i>etiquetas de fuente</i> por <i>etiquetas de usuario</i> en el RSS compartido',
 			'search' => 'Expresión',
 			'shareOpml' => 'Permitir que OPML comparta las categorías y fuentes correspondientes',
 			'shareRss' => 'Permite compartir por HTML &amp; RSS',

+ 2 - 1
app/i18n/fa/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => ' فیلتر اعمال شده:',
 			'categories' => 'نمایش بر اساس دسته‌بندی',
 			'feeds' => ' نمایش با فید',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => ' مرتب سازی بر اساس تاریخ',
-			'publish_labels_instead_of_tags' => 'در RSS اشتراکی، <i>برچسب‌های فید</i> را با <i>برچسب‌های کاربر</i> جایگزین کنید',
 			'search' => ' بیان',
 			'shareOpml' => 'فعال‌سازی اشتراک‌گذاری دسته‌ها و فیدهای مربوطه با OPML',
 			'shareRss' => 'اشتراک‌گذاری RSS',

+ 2 - 1
app/i18n/fi/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'Suodatin käytössä:',
 			'categories' => 'Näytä luokan mukaan',
 			'feeds' => 'Näytä syötteen mukaan',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => 'Lajittele päivämäärän mukaan',
-			'publish_labels_instead_of_tags' => 'Replace <i>feed tags</i> by <i>user labels</i> in the shared RSS',	// TODO
 			'search' => 'Lauseke',
 			'shareOpml' => 'Jaa kyselyä vastaavat luokat ja syötteet OPML-muodossa',
 			'shareRss' => 'Jaa HTML &amp; RSS -muodossa',

+ 3 - 2
app/i18n/fr/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'Filtres appliqués :',
 			'categories' => 'Afficher par catégorie',
 			'feeds' => 'Afficher par flux',
+			'include_article_tags_label' => 'Inclure les tags des articles provenant des flux',
+			'include_user_labels_label' => 'Inclure les étiquettes utilisateur, avec le préfixe :',
 			'order' => 'Tri par date',
-			'publish_labels_instead_of_tags' => 'Remplacer les <i>tags des flux</i> par les <i>étiquettes d’utilisateur</i> dans le RSS partagé',
 			'search' => 'Expression',	// IGNORE
 			'shareOpml' => 'Active le partage par OPML des catégories et flux correspondants',
 			'shareRss' => 'Active le partage par HTML &amp; RSS',
@@ -214,7 +215,7 @@ return array(
 		'get_Z' => 'Tous les flux, y compris les archivés',
 		'get_all' => 'Afficher tous les articles',
 		'get_all_labels' => 'Afficher les articles avec une étiquette',
-		'get_category' => 'Afficher la catégorie <em>%s<em>',
+		'get_category' => 'Afficher la catégorie <em>%s</em>',
 		'get_favorite' => 'Afficher les articles favoris',
 		'get_feed' => 'Afficher le flux <em>%s</em>',
 		'get_important' => 'Afficher les articles des flux importants',

+ 2 - 1
app/i18n/he/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'מסננים בשימוש:',
 			'categories' => 'Display by category',	// TODO
 			'feeds' => 'Display by feed',	// TODO
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => 'Sort by date',	// TODO
-			'publish_labels_instead_of_tags' => 'Replace <i>feed tags</i> by <i>user labels</i> in the shared RSS',	// TODO
 			'search' => 'Expression',	// TODO
 			'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds',	// TODO
 			'shareRss' => 'Enable sharing by HTML &amp; RSS',	// TODO

+ 2 - 1
app/i18n/hu/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'Alkalmazott szűrő:',
 			'categories' => 'Rendezés kategória szerint',
 			'feeds' => 'Rendezés hírforrás szerint',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => 'Rendezés dátum szerint',
-			'publish_labels_instead_of_tags' => 'A megosztott RSS-ben a <i>hírforrás-címkéket</i> <i>felhasználói címkékre</i> cserélje',
 			'search' => 'Kifejezés',
 			'shareOpml' => 'Engedélyezze a megfelelő kategóriák és hírcsatornák OPML-alapú megosztását',
 			'shareRss' => 'Engedélyezze a HTML &amp; RSS megosztást',

+ 2 - 1
app/i18n/id/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'Filter yang diterapkan:',
 			'categories' => 'Tampilkan berdasarkan kategori',
 			'feeds' => 'Tampilkan berdasarkan umpan',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => 'Urutkan berdasarkan tanggal',
-			'publish_labels_instead_of_tags' => 'Ganti <i>tag umpan</i> dengan <i>label pengguna</i> pada RSS yang dibagikan',
 			'search' => 'Ekspresi Pencarian',
 			'shareOpml' => 'Aktifkan berbagi melalui OPML dari kategori dan umpan terkait',
 			'shareRss' => 'Aktifkan berbagi melalui HTML &amp; RSS',

+ 2 - 1
app/i18n/it/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'Filtro applicato:',
 			'categories' => 'Mostra per categoria',
 			'feeds' => 'Mostra per feed',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => 'Ordina per data',
-			'publish_labels_instead_of_tags' => 'Sostituisce i <i>tag del feed</i> con le <i>etichette dell’utente</i> nel feed RSS condiviso',
 			'search' => 'Espressione',
 			'shareOpml' => 'Abilita la condivisione di OPML di categorie e feed corrispondenti',
 			'shareRss' => 'Abilita la condivisione di HTML &amp; RSS',

+ 2 - 1
app/i18n/ja/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'フィルターを適用:',
 			'categories' => 'カテゴリごとに表示する',
 			'feeds' => 'フィードごとに表示する',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => '日付ごとにソートする',
-			'publish_labels_instead_of_tags' => '共有RSSでは<i>フィードタグ</i>の代わりに<i>ユーザーラベル</i>を使用する',
 			'search' => '検索式',
 			'shareOpml' => 'カテゴリとフィードのOPMLによる共有を有効にする',
 			'shareRss' => 'HTMLとRSSによる共有を有効にする',

+ 2 - 1
app/i18n/ko/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => '필터 적용:',
 			'categories' => '카테고리별로 표시',
 			'feeds' => '피드별로 표시',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => '날짜순으로 정렬',
-			'publish_labels_instead_of_tags' => 'Replace <i>feed tags</i> by <i>user labels</i> in the shared RSS',	// TODO
 			'search' => '정규 표현식',
 			'shareOpml' => '해당 카테고리와 피드에 대한 OPML 공유 활성화',
 			'shareRss' => 'HTML 및 RSS 공유 활성화',

+ 2 - 1
app/i18n/lt/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'Pritaikytas filtras:',
 			'categories' => 'Rodyti pagal kategoriją',
 			'feeds' => 'Rodyti pagal kanalą',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => 'Rikiuoti pagal datą',
-			'publish_labels_instead_of_tags' => 'Bendrinamame RSS pakeisti <i>kanalo žymas</i> <i>naudotojo etiketėmis</i>',
 			'search' => 'Išraiška',
 			'shareOpml' => 'Įjungti atitinkamų kategorijų ir kanalų bendrinimą per OPML',
 			'shareRss' => 'Įjungti bendrinimą per HTML ir RSS',

+ 2 - 1
app/i18n/lv/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'Piemērotais filtrs:',
 			'categories' => 'Rādīt pēc kategorijas',
 			'feeds' => 'Rādīt pēc barotnes',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => 'Kārtot pēc datuma',
-			'publish_labels_instead_of_tags' => 'Replace <i>feed tags</i> by <i>user labels</i> in the shared RSS',	// TODO
 			'search' => 'Izteiksme',
 			'shareOpml' => 'Iespējot dalīšanos, izmantojot OPML atbilstošajām kategorijām un barotnēm',
 			'shareRss' => 'Iespējojiet koplietošanu ar HTML &amp; RSS',

+ 2 - 1
app/i18n/nl/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'Filter toegepast:',
 			'categories' => 'Weergeven op categorie',
 			'feeds' => 'Weergeven op feed',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => 'Sorteren op datum',
-			'publish_labels_instead_of_tags' => 'Vervang <i>feedtags</i> door <i>gebruikerslabels</i> in de gedeelde RSS',
 			'search' => 'Expressie',
 			'shareOpml' => 'Via OPML delen van bijbehorende categorieën en feeds aanzetten',
 			'shareRss' => 'Via HTML &amp; RSS delen aanzetten',

+ 2 - 1
app/i18n/oc/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'Filtres aplicats :',
 			'categories' => 'Afichatge per categoria',
 			'feeds' => 'Afichatge per flux',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => 'Triar per data',
-			'publish_labels_instead_of_tags' => 'Replace <i>feed tags</i> by <i>user labels</i> in the shared RSS',	// TODO
 			'search' => 'Expression',	// IGNORE
 			'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds',	// TODO
 			'shareRss' => 'Enable sharing by HTML &amp; RSS',	// TODO

+ 2 - 1
app/i18n/pl/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'Zastosowane filtry:',
 			'categories' => 'Według kategorii',
 			'feeds' => 'Według kanału',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => 'Sortowanie wg daty',
-			'publish_labels_instead_of_tags' => 'Zamień <i>tagi kanałów</i> na <i>własne etykiety</i> w udostępnionym RSS',
 			'search' => 'Wyrażenie',
 			'shareOpml' => 'Włącz udostępnianie OPML-ów zawierających kategorie i kanały',
 			'shareRss' => 'Włącz udostępnianie przez HTML i RSS',

+ 2 - 1
app/i18n/pt-BR/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'Filtro aplicado:',
 			'categories' => 'Exibir por categoria',
 			'feeds' => 'Exibir por feed',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => 'Ordenar por data',
-			'publish_labels_instead_of_tags' => 'Substitua <i>tags de feed</i> por <i>rótulos de usuário</i> no RSS compartilhado.',
 			'search' => 'Expressão',
 			'shareOpml' => 'Habilita o compartilhamento por OPML de categorias e feeds correspondentes',
 			'shareRss' => 'Habilita o compartilhamento por HTML &amp; RSS',

+ 2 - 1
app/i18n/pt-PT/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'Filtro aplicado:',
 			'categories' => 'Mostrar por categoria',
 			'feeds' => 'Mostrar por feed',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => 'Ordenar por data',
-			'publish_labels_instead_of_tags' => 'Replace <i>feed tags</i> by <i>user labels</i> in the shared RSS',	// TODO
 			'search' => 'Expressão',
 			'shareOpml' => 'Activa a partilha por OPML de categorias e feeds correspondentes',
 			'shareRss' => 'Activa o partilha por HTML &amp; RSS',

+ 2 - 1
app/i18n/ru/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'Применённые фильтры:',
 			'categories' => 'Отображение по категории',
 			'feeds' => 'Отображение по ленте',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => 'Сортировать по дате',
-			'publish_labels_instead_of_tags' => 'Заменить <i>теги ленты</i> на <i>пользовательские метки</i> в общей RSS-ленте',
 			'search' => 'Выражение',
 			'shareOpml' => 'Включить общий доступ с помощью OPML к соответствующим категориям и лентам',
 			'shareRss' => 'Включить общий доступ с помощью HTML &amp; RSS',

+ 2 - 1
app/i18n/sk/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'Použitý filter:',
 			'categories' => 'Zobraziť podľa kategórie',
 			'feeds' => 'Zobraziť podľa kanála',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => 'Zobraziť podľa dátumu',
-			'publish_labels_instead_of_tags' => 'Replace <i>feed tags</i> by <i>user labels</i> in the shared RSS',	// TODO
 			'search' => 'Výraz',
 			'shareOpml' => 'Povoliť zdieľanie prostredníctvom OPML zodpovedajúdich kategórií a kanálov',
 			'shareRss' => 'Povoliť zdieľanie prostredníctvom HTML &amp; RSS',

+ 2 - 1
app/i18n/tr/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'Uygulanan filtre:',
 			'categories' => 'Kategoriye göre göster',
 			'feeds' => 'Beslemeye göre göster',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => 'Tarihe göre sırala',
-			'publish_labels_instead_of_tags' => 'Paylaşılan RSS içindeki <i>feed tags</i>, <i>user labels</i> tarafından değiştirilmeli',
 			'search' => 'İfade',
 			'shareOpml' => 'İlgili kategori ve beslemelerin OPML ile paylaşımını etkinleştir',
 			'shareRss' => 'HTML ve RSS ile paylaşımı etkinleştir',

+ 2 - 1
app/i18n/uk/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => 'Застосовано фільтр:',
 			'categories' => 'Показати категорію',
 			'feeds' => 'Показати стрічку',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => 'Впорядкувати за датою',
-			'publish_labels_instead_of_tags' => 'Замінити <i>теги стрічок</i> на <i>користувацькі мітки</i> в RSS-поширенні',
 			'search' => 'Вираз',
 			'shareOpml' => 'Увімкнути OPML-поширення відповідних категорій і стрічок',
 			'shareRss' => 'Увімкнути поширення в форматі HTML і RSS',

+ 2 - 1
app/i18n/zh-CN/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => '生效的过滤器:',
 			'categories' => '按分类显示',
 			'feeds' => '按订阅源显示',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => '按日期排序',
-			'publish_labels_instead_of_tags' => '在共享的 RSS 中用 <i>用户标签</i> 替换 <i>订阅源标签</i>',	// DIRTY
 			'search' => '表达式',
 			'shareOpml' => '启用相应类别和 feed 的 OPML 分享',
 			'shareRss' => '启用 HTML 和 RSS 分享',

+ 2 - 1
app/i18n/zh-TW/conf.php

@@ -201,8 +201,9 @@ return array(
 			'_' => '套用的過濾器:',
 			'categories' => '按類別顯示',
 			'feeds' => '按訂閱源顯示',
+			'include_article_tags_label' => 'Include article tags from feeds',	// TODO
+			'include_user_labels_label' => 'Include user labels, with prefix:',	// TODO
 			'order' => '按日期排序',
-			'publish_labels_instead_of_tags' => 'Replace <i>feed tags</i> by <i>user labels</i> in the shared RSS',	// TODO
 			'search' => '表達式',
 			'shareOpml' => '啟用透過對應類別和訂閱源的 OPML 分享',
 			'shareRss' => '啟用透過 HTML 分享 &amp; RSS',

+ 12 - 3
app/views/helpers/configure/query.phtml

@@ -51,9 +51,18 @@
 							<li><a href="<?= $this->query->sharedUrlGreader() ?>"><?= _i('link') ?> <?= _t('conf.query.share.greader') ?></a></li>
 							<li><a href="<?= $this->query->sharedUrlRss() ?>"><?= _i('link') ?> <?= _t('conf.query.share.rss') ?></a></li>
 						</ul>
-						<label class="checkbox" for="publishLabelsInsteadOfTags">
-							<input type="checkbox" name="query[publishLabelsInsteadOfTags]" id="publishLabelsInsteadOfTags" value="1" <?= $this->query->publishLabelsInsteadOfTags() ? 'checked="checked"' : ''?> />
-							<?= _t('conf.query.filter.publish_labels_instead_of_tags') ?>
+					</div>
+					<div class="group-controls">
+						<label class="checkbox" for="includeArticleTags">
+							<input type="checkbox" name="query[includeArticleTags]" id="includeArticleTags" value="1" <?= !$this->query->excludeArticleTags() ? 'checked="checked"' : ''?> />
+							<?= _t('conf.query.filter.include_article_tags_label') ?>
+						</label>
+						<label class="checkbox" for="includeUserLabels">
+							<input type="checkbox" name="query[includeUserLabels]" id="includeUserLabels" value="1" <?= $this->query->includeUserLabels() ? 'checked="checked"' : ''?> />
+							<?= _t('conf.query.filter.include_user_labels_label') ?>
+							<input type="text" name="query[userLabelPrefix]" id="userLabelPrefix" class="w20"
+								value="<?= htmlspecialchars($this->query->userLabelPrefix() ?: (Minz_User::name() ?? '') . '/', ENT_COMPAT, 'UTF-8') ?>"
+								placeholder="<?= htmlspecialchars((Minz_User::name() ?? '') . '/', ENT_COMPAT, 'UTF-8') ?>" />
 						</label>
 						<?php endif; ?>
 					</div>

+ 5 - 3
app/views/index/rss.phtml

@@ -40,14 +40,16 @@ foreach ($this->entries as $item) {
 					}
 				}
 
-				if ($this->publishLabelsInsteadOfTags) {
+				if ($this->includeUserLabels) {
 					$categories = $this->entryIdsTagNames['e_' . $item->id()] ?? [];
 					if (is_array($categories)) {
 						foreach ($categories as $category) {
-							echo "\t\t\t", '<category>', $category, '</category>', "\n";
+							echo "\t\t\t", '<category>', htmlspecialchars($this->userLabelPrefix, ENT_NOQUOTES, 'UTF-8'), $category, '</category>', "\n";
 						}
 					}
-				} else {
+				}
+
+				if (!$this->excludeArticleTags) {
 					$categories = $item->tags();
 					if (is_array($categories)) {
 						foreach ($categories as $category) {

+ 4 - 2
p/api/query.php

@@ -170,9 +170,11 @@ $view->rss_url = $query->sharedUrlRss();
 $view->rss_title = $query->getName();
 $view->image_url = $query->getImageUrl();
 $view->description = $query->getDescription() ?: _t('index.feed.rss_of', $view->rss_title);
-$view->publishLabelsInsteadOfTags = $query->publishLabelsInsteadOfTags();
+$view->includeUserLabels = $query->includeUserLabels();
+$view->excludeArticleTags = $query->excludeArticleTags();
+$view->userLabelPrefix = $query->userLabelPrefix();
 $view->entryIdsTagNames = [];
-if ($view->publishLabelsInsteadOfTags && in_array($format, ['rss', 'atom'], true)) {
+if ($view->includeUserLabels && in_array($format, ['rss', 'atom'], true)) {
 	$entries = iterator_to_array($view->entries, preserve_keys: false);	// TODO: Optimise: avoid iterator_to_array if possible
 	$view->entries = $entries;
 	if (!empty($entries)) {

+ 8 - 1
p/themes/base-theme/frss.css

@@ -298,7 +298,7 @@ label > span[title] {
 	text-decoration: underline dotted;
 }
 
-input:not(.w50,.w100) {
+input:not(.w20,.w50,.w100) {
 	max-width: 90%;
 	width: 300px;
 }
@@ -335,6 +335,13 @@ textarea.h50 {
 	min-height: 7rem;
 }
 
+input.w20,
+select.w20,
+textarea.w20 {
+	min-width: 20%;
+	box-sizing: border-box;
+}
+
 input.w50,
 select.w50,
 textarea.w50 {

+ 8 - 1
p/themes/base-theme/frss.rtl.css

@@ -298,7 +298,7 @@ label > span[title] {
 	text-decoration: underline dotted;
 }
 
-input:not(.w50,.w100) {
+input:not(.w20,.w50,.w100) {
 	max-width: 90%;
 	width: 300px;
 }
@@ -335,6 +335,13 @@ textarea.h50 {
 	min-height: 7rem;
 }
 
+input.w20,
+select.w20,
+textarea.w20 {
+	min-width: 20%;
+	box-sizing: border-box;
+}
+
 input.w50,
 select.w50,
 textarea.w50 {

+ 23 - 0
tests/app/Models/UserQueryTest.php

@@ -97,6 +97,7 @@ class UserQueryTest extends TestCase {
 
 	public static function testToArray_whenNoData_returnsEmptyArray(): void {
 		$user_query = new FreshRSS_UserQuery([], [], []);
+		// All values are defaults, so array should be empty
 		self::assertCount(0, $user_query->toArray());
 	}
 
@@ -216,4 +217,26 @@ class UserQueryTest extends TestCase {
 		self::assertFalse($user_query->isDeprecated());
 	}
 
+	public static function test__construct_whenUserLabelPrefix_storesValue(): void {
+		$query = ['userLabelPrefix' => 'test/'];
+		$user_query = new FreshRSS_UserQuery($query, [], []);
+		self::assertSame('test/', $user_query->userLabelPrefix());
+	}
+
+	public static function test__construct_migration_fromPublishLabelsInsteadOfTags_whenEnabled(): void {
+		// Test of legacy migration
+		$query = ['publishLabelsInsteadOfTags' => true];
+		$user_query = new FreshRSS_UserQuery($query, [], []);
+		self::assertTrue($user_query->includeUserLabels());
+		self::assertTrue($user_query->excludeArticleTags());
+	}
+
+	public static function test__construct_migration_fromPublishLabelsInsteadOfTags_whenDisabled(): void {
+		// Test of legacy migration
+		$query = ['publishLabelsInsteadOfTags' => false];
+		$user_query = new FreshRSS_UserQuery($query, [], []);
+		self::assertFalse($user_query->includeUserLabels());
+		self::assertFalse($user_query->excludeArticleTags());
+	}
+
 }