Browse Source

Exposed the reading modes for extensions through Minz (#7668)

* + Exposed the reading modes for extensions through Minz. Now extensions can add a custom view mode. Graceful fallback to normal view in case the extension was disabled without resetting the view_mode through the uninstall method. In that case the user will be informed via Minz_Request::setBadNotification that the view has been reset to normal.
+ Added translation strings for de, en and en-us for the notification

* + Added missing, generated translations

* Simplify indexAction, performance

* Minor settings htmlspecialchars

* i18n: fr

* Minor wording

* Doc

* Fix i18n

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
Stefan 9 months ago
parent
commit
51298cd6bc

+ 1 - 0
app/Controllers/configureController.php

@@ -160,6 +160,7 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
 			Minz_Request::good(_t('feedback.conf.updated'), [ 'c' => 'configure', 'a' => 'reading' ]);
 			Minz_Request::good(_t('feedback.conf.updated'), [ 'c' => 'configure', 'a' => 'reading' ]);
 		}
 		}
 
 
+		$this->view->viewModes = FreshRSS_ViewMode::getAllModes();
 		FreshRSS_View::prependTitle(_t('conf.reading.title') . ' · ');
 		FreshRSS_View::prependTitle(_t('conf.reading.title') . ' · ');
 	}
 	}
 
 

+ 10 - 2
app/Controllers/indexController.php

@@ -16,9 +16,17 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController {
 	 */
 	 */
 	public function indexAction(): void {
 	public function indexAction(): void {
 		$preferred_output = FreshRSS_Context::userConf()->view_mode;
 		$preferred_output = FreshRSS_Context::userConf()->view_mode;
+		$viewMode = FreshRSS_ViewMode::getAllModes()[$preferred_output] ?? null;
+
+		// Fallback to 'normal' if the preferred mode was not found
+		if ($viewMode === null) {
+			Minz_Request::setBadNotification(_t('feedback.extensions.invalid_view_mode', $preferred_output));
+			$viewMode = FreshRSS_ViewMode::getAllModes()['normal'];
+		}
+
 		Minz_Request::forward([
 		Minz_Request::forward([
-			'c' => 'index',
-			'a' => $preferred_output,
+			'c' => $viewMode->controller(),
+			'a' => $viewMode->action(),
 		]);
 		]);
 	}
 	}
 
 

+ 4 - 0
app/Models/View.php

@@ -132,4 +132,8 @@ class FreshRSS_View extends Minz_View {
 	public string $errorMessage;
 	public string $errorMessage;
 	/** @var array<string,string> */
 	/** @var array<string,string> */
 	public array $message;
 	public array $message;
+
+	// View modes
+	/** @var array<FreshRSS_ViewMode> */
+	public array $viewModes;
 }
 }

+ 65 - 0
app/Models/ViewMode.php

@@ -0,0 +1,65 @@
+<?php
+declare(strict_types=1);
+
+/**
+ * Represents a view mode option for the reading configuration
+ */
+final class FreshRSS_ViewMode {
+	private string $id;
+	private string $name;
+	private string $controller;
+	private string $action;
+
+	public function __construct(string $id, string $name, string $controller = 'index', string $action = '') {
+		$this->id = $id;
+		$this->name = $name;
+		$this->controller = $controller;
+		$this->action = $action ?: $id;
+	}
+
+	public function id(): string {
+		return $this->id;
+	}
+
+	public function name(): string {
+		return $this->name;
+	}
+
+	public function controller(): string {
+		return $this->controller;
+	}
+
+	public function action(): string {
+		return $this->action;
+	}
+
+	/**
+	 * @return array<string,FreshRSS_ViewMode> Mode ID => FreshRSS_ViewMode
+	 */
+	public static function getDefaultModes(): array {
+		return [
+			'normal' => new self(id: 'normal', name: _t('conf.reading.view.normal'), controller: 'index', action: 'normal'),
+			'reader' => new self(id: 'reader', name: _t('conf.reading.view.reader'), controller: 'index', action: 'reader'),
+			'global' => new self(id: 'global', name: _t('conf.reading.view.global'), controller: 'index', action: 'global'),
+		];
+	}
+
+	/**
+	 * @return array<string,FreshRSS_ViewMode> Mode ID => FreshRSS_ViewMode
+	 */
+	public static function getAllModes(): array {
+		$modes = self::getDefaultModes();
+
+		// Allow extensions to add their own view modes
+		$extensionModes = Minz_ExtensionManager::callHook('view_modes', []);
+		if (is_array($extensionModes)) {
+			foreach ($extensionModes as $mode) {
+				if ($mode instanceof FreshRSS_ViewMode) {
+					$modes[$mode->id()] = $mode;
+				}
+			}
+		}
+
+		return $modes;
+	}
+}

+ 1 - 0
app/i18n/cs/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => '%s nelze povolit. Pro podrobnosti <a href="%s">zkontrolujte protokoly FreshRSS</a>.',
 			'ko' => '%s nelze povolit. Pro podrobnosti <a href="%s">zkontrolujte protokoly FreshRSS</a>.',
 			'ok' => '%s je nyní povoleno',
 			'ok' => '%s je nyní povoleno',
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// TODO
 		'no_access' => 'Nemáte přístup k %s',
 		'no_access' => 'Nemáte přístup k %s',
 		'not_enabled' => '%s není povoleno',
 		'not_enabled' => '%s není povoleno',
 		'not_found' => '%s neexistuje',
 		'not_found' => '%s neexistuje',

+ 1 - 0
app/i18n/de/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => '%s kann nicht aktiviert werden. Für Details <a href="%s">prüfen Sie die FreshRSS-Protokolle</a>.',
 			'ko' => '%s kann nicht aktiviert werden. Für Details <a href="%s">prüfen Sie die FreshRSS-Protokolle</a>.',
 			'ok' => '%s ist jetzt aktiviert',
 			'ok' => '%s ist jetzt aktiviert',
 		),
 		),
+		'invalid_view_mode' => 'Ungültige Standard-Ansicht %s gefunden. Setze Standard-Ansicht auf Normale Ansicht zurück.',
 		'no_access' => 'Sie haben keinen Zugang zu %s',
 		'no_access' => 'Sie haben keinen Zugang zu %s',
 		'not_enabled' => '%s ist noch nicht aktiviert',
 		'not_enabled' => '%s ist noch nicht aktiviert',
 		'not_found' => '%s existiert nicht',
 		'not_found' => '%s existiert nicht',

+ 1 - 0
app/i18n/el/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => '%s cannot be enabled. <a href="%s">Check FreshRSS logs</a> for details.',	// TODO
 			'ko' => '%s cannot be enabled. <a href="%s">Check FreshRSS logs</a> for details.',	// TODO
 			'ok' => '%s is now enabled',	// TODO
 			'ok' => '%s is now enabled',	// TODO
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// TODO
 		'no_access' => 'You have no access on %s',	// TODO
 		'no_access' => 'You have no access on %s',	// TODO
 		'not_enabled' => '%s is not enabled',	// TODO
 		'not_enabled' => '%s is not enabled',	// TODO
 		'not_found' => '%s does not exist',	// TODO
 		'not_found' => '%s does not exist',	// TODO

+ 1 - 0
app/i18n/en-us/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => '%s cannot be enabled. <a href="%s">Check FreshRSS logs</a> for details.',	// IGNORE
 			'ko' => '%s cannot be enabled. <a href="%s">Check FreshRSS logs</a> for details.',	// IGNORE
 			'ok' => '%s is now enabled',	// IGNORE
 			'ok' => '%s is now enabled',	// IGNORE
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// IGNORE
 		'no_access' => 'You have no access on %s',	// IGNORE
 		'no_access' => 'You have no access on %s',	// IGNORE
 		'not_enabled' => '%s is not enabled',	// IGNORE
 		'not_enabled' => '%s is not enabled',	// IGNORE
 		'not_found' => '%s does not exist',	// IGNORE
 		'not_found' => '%s does not exist',	// IGNORE

+ 1 - 0
app/i18n/en/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => '%s cannot be enabled. <a href="%s">Check FreshRSS logs</a> for details.',
 			'ko' => '%s cannot be enabled. <a href="%s">Check FreshRSS logs</a> for details.',
 			'ok' => '%s is now enabled',
 			'ok' => '%s is now enabled',
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',
 		'no_access' => 'You have no access on %s',
 		'no_access' => 'You have no access on %s',
 		'not_enabled' => '%s is not enabled',
 		'not_enabled' => '%s is not enabled',
 		'not_found' => '%s does not exist',
 		'not_found' => '%s does not exist',

+ 1 - 0
app/i18n/es/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => '%s no se puede activar. <a href="%s">Revisa el registro de FreshRSS</a> para más información.',
 			'ko' => '%s no se puede activar. <a href="%s">Revisa el registro de FreshRSS</a> para más información.',
 			'ok' => '%s ha quedado activado',
 			'ok' => '%s ha quedado activado',
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// TODO
 		'no_access' => 'No tienes acceso a %s',
 		'no_access' => 'No tienes acceso a %s',
 		'not_enabled' => '%s no está activado',
 		'not_enabled' => '%s no está activado',
 		'not_found' => '%s no existe',
 		'not_found' => '%s no existe',

+ 1 - 0
app/i18n/fa/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => ' %s را نمی توان فعال کرد. برای جزئیات <a href="%s">گزارش‌های FreshRSS</a> را بررسی کنید.',
 			'ko' => ' %s را نمی توان فعال کرد. برای جزئیات <a href="%s">گزارش‌های FreshRSS</a> را بررسی کنید.',
 			'ok' => ' %s اکنون فعال است',
 			'ok' => ' %s اکنون فعال است',
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// TODO
 		'no_access' => ' شما به %s دسترسی ندارید',
 		'no_access' => ' شما به %s دسترسی ندارید',
 		'not_enabled' => '%s فعال نیست',
 		'not_enabled' => '%s فعال نیست',
 		'not_found' => '%s وجود ندارد',
 		'not_found' => '%s وجود ندارد',

+ 1 - 0
app/i18n/fi/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => 'Laajennusta %s ei voi ottaa käyttöön. Lisätietoja on <a href="%s">FreshRSS-lokeissa</a>.',
 			'ko' => 'Laajennusta %s ei voi ottaa käyttöön. Lisätietoja on <a href="%s">FreshRSS-lokeissa</a>.',
 			'ok' => '%s on nyt otettu käyttöön',
 			'ok' => '%s on nyt otettu käyttöön',
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// TODO
 		'no_access' => 'Sinulla ei ole laajennuksen %s käyttöoikeutta',
 		'no_access' => 'Sinulla ei ole laajennuksen %s käyttöoikeutta',
 		'not_enabled' => 'Laajennus %s ei ole käytössä',
 		'not_enabled' => 'Laajennus %s ei ole käytössä',
 		'not_found' => 'Laajennusta %s ei ole',
 		'not_found' => 'Laajennusta %s ei ole',

+ 1 - 0
app/i18n/fr/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => '%s ne peut pas être activée. <a href="%s">Consulter les logs de FreshRSS</a> pour plus de détails.',
 			'ko' => '%s ne peut pas être activée. <a href="%s">Consulter les logs de FreshRSS</a> pour plus de détails.',
 			'ok' => '%s est désormais activée',
 			'ok' => '%s est désormais activée',
 		),
 		),
+		'invalid_view_mode' => 'Mode de vue “%s” invalide ! Passage en “Vue normale”.',
 		'no_access' => 'Vous n’avez aucun accès sur %s',
 		'no_access' => 'Vous n’avez aucun accès sur %s',
 		'not_enabled' => '%s n’est pas encore activée',
 		'not_enabled' => '%s n’est pas encore activée',
 		'not_found' => '%s n’existe pas',
 		'not_found' => '%s n’existe pas',

+ 1 - 0
app/i18n/he/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => '%s cannot be enabled. <a href="%s">Check FreshRSS logs</a> for details.',	// TODO
 			'ko' => '%s cannot be enabled. <a href="%s">Check FreshRSS logs</a> for details.',	// TODO
 			'ok' => '%s is now enabled',	// TODO
 			'ok' => '%s is now enabled',	// TODO
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// TODO
 		'no_access' => 'You have no access on %s',	// TODO
 		'no_access' => 'You have no access on %s',	// TODO
 		'not_enabled' => '%s is not enabled yet',
 		'not_enabled' => '%s is not enabled yet',
 		'not_found' => '%s does not exist',	// TODO
 		'not_found' => '%s does not exist',	// TODO

+ 1 - 0
app/i18n/hu/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => 'A(z) %s nem kapcsolható be. <a href="%s">nézd meg a FreshRSS log-okat</a> a részletekért.',
 			'ko' => 'A(z) %s nem kapcsolható be. <a href="%s">nézd meg a FreshRSS log-okat</a> a részletekért.',
 			'ok' => 'A(z) %s bekapcsolása sikeres',
 			'ok' => 'A(z) %s bekapcsolása sikeres',
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// TODO
 		'no_access' => 'Nincs hozzáférésed ehhez: %s',
 		'no_access' => 'Nincs hozzáférésed ehhez: %s',
 		'not_enabled' => 'A(z) %s nincs bekapcsolva',
 		'not_enabled' => 'A(z) %s nincs bekapcsolva',
 		'not_found' => 'A(z) %s nem létezik',
 		'not_found' => 'A(z) %s nem létezik',

+ 1 - 0
app/i18n/id/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => '%s cannot be enabled. <a href="%s">Check FreshRSS logs</a> for details.',	// TODO
 			'ko' => '%s cannot be enabled. <a href="%s">Check FreshRSS logs</a> for details.',	// TODO
 			'ok' => '%s is now enabled',	// TODO
 			'ok' => '%s is now enabled',	// TODO
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// TODO
 		'no_access' => 'You have no access on %s',	// TODO
 		'no_access' => 'You have no access on %s',	// TODO
 		'not_enabled' => '%s is not enabled',	// TODO
 		'not_enabled' => '%s is not enabled',	// TODO
 		'not_found' => '%s does not exist',	// TODO
 		'not_found' => '%s does not exist',	// TODO

+ 1 - 0
app/i18n/it/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => '%s non può essere abilitata. <a href="%s">Verifica i logs</a> per dettagli.',
 			'ko' => '%s non può essere abilitata. <a href="%s">Verifica i logs</a> per dettagli.',
 			'ok' => '%s è ora abilitata',
 			'ok' => '%s è ora abilitata',
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// TODO
 		'no_access' => 'Accesso negato a %s',
 		'no_access' => 'Accesso negato a %s',
 		'not_enabled' => '%s non abilitato',
 		'not_enabled' => '%s non abilitato',
 		'not_found' => '%s non disponibile',
 		'not_found' => '%s non disponibile',

+ 1 - 0
app/i18n/ja/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => '%sは有効にできません。 <a href="%s">FreshRSS のログを確認してください</a> 詳細が表示されます。',
 			'ko' => '%sは有効にできません。 <a href="%s">FreshRSS のログを確認してください</a> 詳細が表示されます。',
 			'ok' => '%sは有効にされています。',
 			'ok' => '%sは有効にされています。',
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// TODO
 		'no_access' => 'あなたは%sにアクセスする権限がありません',
 		'no_access' => 'あなたは%sにアクセスする権限がありません',
 		'not_enabled' => '%sは有効にされていません',
 		'not_enabled' => '%sは有効にされていません',
 		'not_found' => '%sは存在しません',
 		'not_found' => '%sは存在しません',

+ 1 - 0
app/i18n/ko/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => '%s 확장 기능을 활성화 할 수 없습니다. 자세한 내용은 <a href="%s">FreshRSS 로그</a>를 참고하세요.',
 			'ko' => '%s 확장 기능을 활성화 할 수 없습니다. 자세한 내용은 <a href="%s">FreshRSS 로그</a>를 참고하세요.',
 			'ok' => '%s 확장 기능이 활성화되었습니다',
 			'ok' => '%s 확장 기능이 활성화되었습니다',
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// TODO
 		'no_access' => '%s 확장 기능에 접근 권한이 없습니다',
 		'no_access' => '%s 확장 기능에 접근 권한이 없습니다',
 		'not_enabled' => '%s 확장 기능이 활성화되지 않았습니다',
 		'not_enabled' => '%s 확장 기능이 활성화되지 않았습니다',
 		'not_found' => '%s 확장 기능이 존재하지 않습니다',
 		'not_found' => '%s 확장 기능이 존재하지 않습니다',

+ 1 - 0
app/i18n/lv/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => '%s nevar būt ieslēgts. <a href="%s">Pārbaudiet FreshRSS žurnālu</a> priekš papildu informācijas.',
 			'ko' => '%s nevar būt ieslēgts. <a href="%s">Pārbaudiet FreshRSS žurnālu</a> priekš papildu informācijas.',
 			'ok' => '%s ir tagad ieslēgts',
 			'ok' => '%s ir tagad ieslēgts',
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// TODO
 		'no_access' => 'Jums nav piekļuves %s',
 		'no_access' => 'Jums nav piekļuves %s',
 		'not_enabled' => '%s nav ieslēgts',
 		'not_enabled' => '%s nav ieslēgts',
 		'not_found' => '%s nēeksistē',
 		'not_found' => '%s nēeksistē',

+ 1 - 0
app/i18n/nl/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => '%s kan niet worden ingeschakeld. <a href="%s">Controleer FreshRSS log bestanden</a> voor details.',
 			'ko' => '%s kan niet worden ingeschakeld. <a href="%s">Controleer FreshRSS log bestanden</a> voor details.',
 			'ok' => '%s is nn ingeschakeld',
 			'ok' => '%s is nn ingeschakeld',
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// TODO
 		'no_access' => 'U hebt geen toegang voor %s',
 		'no_access' => 'U hebt geen toegang voor %s',
 		'not_enabled' => '%s is nog niet ingeschakeld',
 		'not_enabled' => '%s is nog niet ingeschakeld',
 		'not_found' => '%s bestaat niet',
 		'not_found' => '%s bestaat niet',

+ 1 - 0
app/i18n/oc/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => '%s pòt pas èsser activada. <a href="%s">Consultatz los jornals d’audit de FreshRSS logs</a> per mai de detalhs.',
 			'ko' => '%s pòt pas èsser activada. <a href="%s">Consultatz los jornals d’audit de FreshRSS logs</a> per mai de detalhs.',
 			'ok' => '%s es ara activada',
 			'ok' => '%s es ara activada',
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// TODO
 		'no_access' => 'Avètz pas accès sus %s',
 		'no_access' => 'Avètz pas accès sus %s',
 		'not_enabled' => '%s es pas encara activada',
 		'not_enabled' => '%s es pas encara activada',
 		'not_found' => '%s existís pas',
 		'not_found' => '%s existís pas',

+ 1 - 0
app/i18n/pl/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => 'Rozszerzenie %s nie może zostać włączone. <a href="%s">Sprawdź dziennik</a> w celu uzyskania szczegółowych informacji.',
 			'ko' => 'Rozszerzenie %s nie może zostać włączone. <a href="%s">Sprawdź dziennik</a> w celu uzyskania szczegółowych informacji.',
 			'ok' => 'Rozszerzenie %s zostało włączone',
 			'ok' => 'Rozszerzenie %s zostało włączone',
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// TODO
 		'no_access' => 'Brak dostępu do %s',
 		'no_access' => 'Brak dostępu do %s',
 		'not_enabled' => 'Rozszerzenie %s nie jest włączone',
 		'not_enabled' => 'Rozszerzenie %s nie jest włączone',
 		'not_found' => 'Rozszerzenie %s nie istnieje',
 		'not_found' => 'Rozszerzenie %s nie istnieje',

+ 1 - 0
app/i18n/pt-br/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => '%s não pode ser habilitado. <a href="%s">verifique os logs do FreshRSS</a> para detalhes.',
 			'ko' => '%s não pode ser habilitado. <a href="%s">verifique os logs do FreshRSS</a> para detalhes.',
 			'ok' => '%s agora está habilitado',
 			'ok' => '%s agora está habilitado',
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// TODO
 		'no_access' => 'Você não tem acesso ao %s',
 		'no_access' => 'Você não tem acesso ao %s',
 		'not_enabled' => '%s não está habilitado',
 		'not_enabled' => '%s não está habilitado',
 		'not_found' => '%s não existe',
 		'not_found' => '%s não existe',

+ 1 - 0
app/i18n/pt-pt/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => '%s não pode ser activado. <a href="%s">verifique os logs do FreshRSS</a> para detalhes.',
 			'ko' => '%s não pode ser activado. <a href="%s">verifique os logs do FreshRSS</a> para detalhes.',
 			'ok' => '%s agora está activado',
 			'ok' => '%s agora está activado',
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// TODO
 		'no_access' => ' não tem acesso ao %s',
 		'no_access' => ' não tem acesso ao %s',
 		'not_enabled' => '%s não está habilitado',
 		'not_enabled' => '%s não está habilitado',
 		'not_found' => '%s não existe',
 		'not_found' => '%s não existe',

+ 1 - 0
app/i18n/ru/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => '%s не может быть включено. <a href="%s">Проверьте логи FreshRSS</a> для подробностей.',
 			'ko' => '%s не может быть включено. <a href="%s">Проверьте логи FreshRSS</a> для подробностей.',
 			'ok' => '%s теперь включено',
 			'ok' => '%s теперь включено',
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// TODO
 		'no_access' => 'У вас нет доступа к %s',
 		'no_access' => 'У вас нет доступа к %s',
 		'not_enabled' => '%s не включено',
 		'not_enabled' => '%s не включено',
 		'not_found' => '%s не существует',
 		'not_found' => '%s не существует',

+ 1 - 0
app/i18n/sk/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => '%s sa nepodarilo povoliť. <a href="%s">Prečítajte si záznamy FreshRSS</a>, ak chcete poznať podrobnosti.',
 			'ko' => '%s sa nepodarilo povoliť. <a href="%s">Prečítajte si záznamy FreshRSS</a>, ak chcete poznať podrobnosti.',
 			'ok' => '%s je teraz povolený',
 			'ok' => '%s je teraz povolený',
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// TODO
 		'no_access' => 'Nemáte prístup k %s',
 		'no_access' => 'Nemáte prístup k %s',
 		'not_enabled' => '%s nie je povolený',
 		'not_enabled' => '%s nie je povolený',
 		'not_found' => '%s neexistuje',
 		'not_found' => '%s neexistuje',

+ 1 - 0
app/i18n/tr/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => '%s etkinleştirilemedi. Ayrıntılar için <a href="%s">FreshRSS günlüklerini kontrol edin</a>.',
 			'ko' => '%s etkinleştirilemedi. Ayrıntılar için <a href="%s">FreshRSS günlüklerini kontrol edin</a>.',
 			'ok' => '%s artık etkin',
 			'ok' => '%s artık etkin',
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// TODO
 		'no_access' => '%s üzerinde erişiminiz yok',
 		'no_access' => '%s üzerinde erişiminiz yok',
 		'not_enabled' => '%s etkin değil',
 		'not_enabled' => '%s etkin değil',
 		'not_found' => '%s mevcut değil',
 		'not_found' => '%s mevcut değil',

+ 1 - 0
app/i18n/zh-cn/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => '%s 启用失败。<a href="%s">检查 FreshRSS 日志</a> 查看详情。',
 			'ko' => '%s 启用失败。<a href="%s">检查 FreshRSS 日志</a> 查看详情。',
 			'ok' => '%s 现已启用',
 			'ok' => '%s 现已启用',
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// TODO
 		'no_access' => '你无权访问 %s',
 		'no_access' => '你无权访问 %s',
 		'not_enabled' => '%s 未启用',
 		'not_enabled' => '%s 未启用',
 		'not_found' => '%s 不存在',
 		'not_found' => '%s 不存在',

+ 1 - 0
app/i18n/zh-tw/feedback.php

@@ -50,6 +50,7 @@ return array(
 			'ko' => '%s 啟用失敗。<a href="%s">檢查 FreshRSS 日誌</a> 查看詳情。',
 			'ko' => '%s 啟用失敗。<a href="%s">檢查 FreshRSS 日誌</a> 查看詳情。',
 			'ok' => '%s 現已啟用',
 			'ok' => '%s 現已啟用',
 		),
 		),
+		'invalid_view_mode' => 'Invalid view mode “%s”! Fall back to “Normal view”.',	// TODO
 		'no_access' => '你無權訪問 %s',
 		'no_access' => '你無權訪問 %s',
 		'not_enabled' => '%s 未啟用',
 		'not_enabled' => '%s 未啟用',
 		'not_found' => '%s 不存在',
 		'not_found' => '%s 不存在',

+ 13 - 3
app/views/configure/reading.phtml

@@ -14,9 +14,19 @@
 				<label class="group-name" for="view_mode"><?= _t('conf.reading.view.default') ?></label>
 				<label class="group-name" for="view_mode"><?= _t('conf.reading.view.default') ?></label>
 				<div class="group-controls">
 				<div class="group-controls">
 					<select name="view_mode" id="view_mode" data-leave-validation="<?= FreshRSS_Context::userConf()->view_mode ?>">
 					<select name="view_mode" id="view_mode" data-leave-validation="<?= FreshRSS_Context::userConf()->view_mode ?>">
-						<option value="normal"<?= FreshRSS_Context::userConf()->view_mode === 'normal' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.view.normal') ?></option>
-						<option value="reader"<?= FreshRSS_Context::userConf()->view_mode === 'reader' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.view.reader') ?></option>
-						<option value="global"<?= FreshRSS_Context::userConf()->view_mode === 'global' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.view.global') ?></option>
+						<?php
+						/** @var FreshRSS_View $this */
+						/** @var list<FreshRSS_ViewMode>|null $viewModes */
+						$viewModes = isset($this->viewModes) && is_array($this->viewModes) ? $this->viewModes : null;
+						if ($viewModes === null) {
+							$viewModes = FreshRSS_ViewMode::getAllModes();
+						}
+						foreach ($viewModes as $mode):
+						?>
+							<option value="<?= htmlspecialchars($mode->id(), ENT_COMPAT, 'UTF-8') ?>"<?= FreshRSS_Context::userConf()->view_mode === $mode->id() ? ' selected="selected"' : '' ?>>
+								<?= htmlspecialchars($mode->name(), ENT_NOQUOTES, 'UTF-8') ?>
+							</option>
+						<?php endforeach; ?>
 					</select>
 					</select>
 				</div>
 				</div>
 			</div>
 			</div>

+ 1 - 0
docs/en/developers/03_Backend/05_Extensions.md

@@ -184,6 +184,7 @@ The following events are available:
 * `post_update` (`function(none) -> none`): **TODO** add documentation.
 * `post_update` (`function(none) -> none`): **TODO** add documentation.
 * `simplepie_after_init` (`function(\SimplePie\SimplePie $simplePie, FreshRSS_Feed $feed, bool $result): void`): Triggered after fetching an RSS/Atom feed with SimplePie. Useful for instance to get the HTTP response headers (e.g. `$simplePie->data['headers']`).
 * `simplepie_after_init` (`function(\SimplePie\SimplePie $simplePie, FreshRSS_Feed $feed, bool $result): void`): Triggered after fetching an RSS/Atom feed with SimplePie. Useful for instance to get the HTTP response headers (e.g. `$simplePie->data['headers']`).
 * `simplepie_before_init` (`function(\SimplePie\SimplePie $simplePie, FreshRSS_Feed $feed): void`): Triggered before fetching an RSS/Atom feed with SimplePie.
 * `simplepie_before_init` (`function(\SimplePie\SimplePie $simplePie, FreshRSS_Feed $feed): void`): Triggered before fetching an RSS/Atom feed with SimplePie.
+* `view_modes` (`function(array<FreshRSS_ViewMode> $viewModes): array|null`): Allow extensions to register additional view modes than *normal*, *reader*, *global*.
 
 
 > ℹ️ Note: the `simplepie_*` hooks are only fired for feeds using SimplePie via pull, i.e. normal RSS/Atom feeds. This excludes WebSub (push), and the various HTML or JSON Web scraping methods.
 > ℹ️ Note: the `simplepie_*` hooks are only fired for feeds using SimplePie via pull, i.e. normal RSS/Atom feeds. This excludes WebSub (push), and the various HTML or JSON Web scraping methods.
 
 

+ 1 - 0
docs/fr/developers/03_Backend/05_Extensions.md

@@ -261,6 +261,7 @@ The following events are available:
 * `post_update` (`function(none) -> none`): **TODO** add documentation
 * `post_update` (`function(none) -> none`): **TODO** add documentation
 * `simplepie_after_init` (`function(\SimplePie\SimplePie $simplePie, FreshRSS_Feed $feed, bool $result): void`): Triggered after fetching an RSS/Atom feed with SimplePie. Useful for instance to get the HTTP response headers (e.g. `$simplePie->data['headers']`).
 * `simplepie_after_init` (`function(\SimplePie\SimplePie $simplePie, FreshRSS_Feed $feed, bool $result): void`): Triggered after fetching an RSS/Atom feed with SimplePie. Useful for instance to get the HTTP response headers (e.g. `$simplePie->data['headers']`).
 * `simplepie_before_init` (`function(\SimplePie\SimplePie $simplePie, FreshRSS_Feed $feed): void`): Triggered before fetching an RSS/Atom feed with SimplePie.
 * `simplepie_before_init` (`function(\SimplePie\SimplePie $simplePie, FreshRSS_Feed $feed): void`): Triggered before fetching an RSS/Atom feed with SimplePie.
+* `view_modes` (`function(array<FreshRSS_ViewMode> $viewModes): array|null`): permet aux extensions de déclarer d’autres modes de vue que *normale*, *lecture*, *globale*.
 
 
 > ℹ️ Note: the `simplepie_*` hooks are only fired for feeds using SimplePie via pull, i.e. normal RSS/Atom feeds. This excludes WebSub (push), and the various HTML or JSON Web scraping methods.
 > ℹ️ Note: the `simplepie_*` hooks are only fired for feeds using SimplePie via pull, i.e. normal RSS/Atom feeds. This excludes WebSub (push), and the various HTML or JSON Web scraping methods.
 
 

+ 4 - 0
lib/Minz/ExtensionManager.php

@@ -102,6 +102,10 @@ final class Minz_ExtensionManager {
 			'list' => [],
 			'list' => [],
 			'signature' => 'PassArguments',
 			'signature' => 'PassArguments',
 		],
 		],
+		'view_modes' => [	// function($viewModes = array) -> array | null
+			'list' => [],
+			'signature' => 'OneToOne',
+		],
 	];
 	];
 
 
 	/** Remove extensions and hooks from a previous initialisation */
 	/** Remove extensions and hooks from a previous initialisation */