diff --git a/extensions/pagetop-admin/src/action.rs b/extensions/pagetop-admin/src/action.rs index 6bfa7ce5..2d9670e8 100644 --- a/extensions/pagetop-admin/src/action.rs +++ b/extensions/pagetop-admin/src/action.rs @@ -87,9 +87,8 @@ impl ActionBag { /// bag.add(AdminSection { /// key: "tools".to_owned(), /// path: "/admin/tools".to_owned(), -/// title: Lc::n("Tools"), -/// permission: None, -/// weight: 60, +/// title: Lc::n("Tools"), +/// weight: 60, /// }); /// } /// // En Extension::actions(): @@ -130,10 +129,18 @@ impl DeclareAdminSections { /// # Ejemplo /// /// ```rust,no_run -/// use pagetop::locale::Lc; +/// use pagetop::prelude::*; /// use pagetop_admin::action::{DeclareAdminPages, PageBag}; /// use pagetop_admin::registry::{AdminPage, AdminPageKind}; /// +/// struct ExportPermission; +/// +/// impl Permission for ExportPermission { +/// fn key(&self) -> CowStr { +/// "tools:export".into() +/// } +/// } +/// /// fn declare_pages(bag: &mut PageBag) { /// bag.add(AdminPage { /// path: "/admin/tools/export".to_owned(), @@ -141,7 +148,7 @@ impl DeclareAdminSections { /// title: Lc::n("Export"), /// description: Some(Lc::n("Export site data.")), /// weight: 0, -/// permission: None, +/// permission: &ExportPermission, /// kind: AdminPageKind::View, /// }); /// } diff --git a/extensions/pagetop-admin/src/handlers.rs b/extensions/pagetop-admin/src/handlers.rs index c9a3d979..d197514f 100644 --- a/extensions/pagetop-admin/src/handlers.rs +++ b/extensions/pagetop-admin/src/handlers.rs @@ -69,7 +69,6 @@ fn render_sections(cx: &Context, candidates: &[&'static AdminSection]) -> Option let sections_with_pages: Vec<_> = candidates .iter() .copied() - .filter(|section| section.is_visible(cx)) .map(|section| { let pages: Vec<_> = reg .pages_for_section(§ion.key) @@ -131,7 +130,7 @@ pub async fn config_form_get(request: HttpRequest) -> Result return Err(ErrorPage::NotFound(Some(request.clone()))); }; - require_permission(&request, page.permission_key())?; + require_permission(&request, page.permission)?; let title = page.title.clone(); let mut form = ConfigForm::with_schema(schema.clone()); @@ -170,7 +169,7 @@ pub async fn config_form_post( return Err(ErrorPage::NotFound(Some(request.clone()))); }; - require_permission(&request, page.permission_key())?; + require_permission(&request, page.permission)?; let mut save_error = false; diff --git a/extensions/pagetop-admin/src/lib.rs b/extensions/pagetop-admin/src/lib.rs index fbf6b053..e2ed8718 100644 --- a/extensions/pagetop-admin/src/lib.rs +++ b/extensions/pagetop-admin/src/lib.rs @@ -50,7 +50,7 @@ fn declare_pages(bag: &mut PageBag) { title: Lc::n("My App"), description: Some(Lc::n("Configure My App.")), weight: 0, - permission: Some(&MyPermission::Config), + permission: &MyPermission::Config, kind: AdminPageKind::View, }); } @@ -63,7 +63,7 @@ enum MyPermission { impl Permission for MyPermission { fn key(&self) -> CowStr { match self { - Self::Config => "myapp.config".into(), + Self::Config => "myapp:config".into(), } } } diff --git a/extensions/pagetop-admin/src/registry.rs b/extensions/pagetop-admin/src/registry.rs index 4ebbbd4b..586cfc45 100644 --- a/extensions/pagetop-admin/src/registry.rs +++ b/extensions/pagetop-admin/src/registry.rs @@ -14,38 +14,12 @@ use crate::action::{ }; use crate::settings::SettingsSchema; -// **< AdminPermission >**************************************************************************** - -/// Permisos propios de `pagetop-admin`. -#[derive(Clone, Copy, Debug)] -pub enum AdminPermission { - /// Acceso por defecto a una página de administración que no declara un permiso propio. - Access, - /// Acceso a la sección integrada "people". - AccessPeople, - /// Acceso a la sección integrada "structure". - AccessStructure, - /// Acceso a la sección integrada "config". - AccessConfig, - /// Acceso a la sección integrada "reports". - AccessReports, -} - -impl Permission for AdminPermission { - fn key(&self) -> CowStr { - match self { - Self::Access => "admin:access".into(), - Self::AccessPeople => "admin.access_people".into(), - Self::AccessStructure => "admin.access_structure".into(), - Self::AccessConfig => "admin.access_config".into(), - Self::AccessReports => "admin.access_reports".into(), - } - } -} - // **< Tipos del registro >************************************************************************* -/// Sección del panel de administración (agrupación en el sidebar). +/// Sección del panel de administración (agrupación de páginas en el menú y en el *dashboard*). +/// +/// Una sección no tiene permiso propio: se muestra a quien pueda acceder a alguna de sus páginas, y +/// se oculta si no le queda ninguna. #[derive(Clone)] pub struct AdminSection { /// Identificador único de la sección (p. ej. `"config"`). @@ -54,24 +28,10 @@ pub struct AdminSection { pub path: String, /// Título visible en el sidebar. pub title: Lc, - /// Permiso requerido para ver la sección (`None` = siempre visible). - pub permission: Option, /// Peso para ordenar en el sidebar (menor = antes). pub weight: i32, } -impl AdminSection { - /// Devuelve `true` si el usuario actual puede ver esta sección. - pub fn is_visible(&self, cx: &Context) -> bool { - match self.permission { - None => true, - Some(permission) => cx - .request() - .is_some_and(|request| has_permission(request, permission)), - } - } -} - /// Página del panel de administración. #[derive(Clone)] pub struct AdminPage { @@ -85,22 +45,18 @@ pub struct AdminPage { pub description: Option, /// Peso dentro de la sección (menor = antes). pub weight: i32, - /// Permiso requerido para acceder (`None` = requiere [`AdminPermission::Access`]). - pub permission: Option, + /// Permiso requerido para acceder. Es obligatorio: cada página declara el suyo, y es también + /// el que decide si su sección se muestra. + pub permission: PermissionRef, /// Tipo de página y datos asociados. pub kind: AdminPageKind, } impl AdminPage { - /// Permiso efectivo: el declarado, o [`AdminPermission::Access`] si no se especificó ninguno. - pub fn permission_key(&self) -> PermissionRef { - self.permission.unwrap_or(&AdminPermission::Access) - } - /// Devuelve `true` si el usuario actual puede acceder a esta página. pub fn is_accessible(&self, cx: &Context) -> bool { cx.request() - .is_some_and(|request| has_permission(request, self.permission_key())) + .is_some_and(|request| has_permission(request, self.permission)) } } @@ -277,11 +233,9 @@ pub fn global() -> &'static AdminRegistry { pub fn can_access_admin(cx: &Context) -> bool { let reg = global(); reg.ordered_sections().into_iter().any(|section| { - section.is_visible(cx) - && reg - .pages_for_section(§ion.key) - .into_iter() - .any(|page| page.is_accessible(cx)) + reg.pages_for_section(§ion.key) + .into_iter() + .any(|page| page.is_accessible(cx)) }) } @@ -306,9 +260,6 @@ pub fn admin_navbar(cx: &Context) -> Navbar { )); for section in reg.ordered_sections() { - if !section.is_visible(cx) { - continue; - } let pages: Vec<_> = reg .pages_for_section(§ion.key) .into_iter() diff --git a/extensions/pagetop-admin/src/seed.rs b/extensions/pagetop-admin/src/seed.rs index cf4c6640..b0fea446 100644 --- a/extensions/pagetop-admin/src/seed.rs +++ b/extensions/pagetop-admin/src/seed.rs @@ -3,7 +3,7 @@ use pagetop::prelude::*; use crate::ADMIN_BASE_PATH; use crate::LOCALES_ADMIN; use crate::action::SectionBag; -use crate::registry::{AdminPermission, AdminSection}; +use crate::registry::AdminSection; /// Declara las secciones incorporadas del panel de administración. /// @@ -16,35 +16,30 @@ pub(crate) fn declare_default_sections(bag: &mut SectionBag) { key: "people".to_owned(), path: format!("{}/people", base), title: Lc::t("section-people", &LOCALES_ADMIN), - permission: Some(&AdminPermission::AccessPeople), weight: 10, }); bag.add(AdminSection { key: "structure".to_owned(), path: format!("{}/structure", base), title: Lc::t("section-structure", &LOCALES_ADMIN), - permission: Some(&AdminPermission::AccessStructure), weight: 20, }); bag.add(AdminSection { key: "config".to_owned(), path: format!("{}/config", base), title: Lc::t("section-config", &LOCALES_ADMIN), - permission: Some(&AdminPermission::AccessConfig), weight: 30, }); bag.add(AdminSection { key: "reports".to_owned(), path: format!("{}/reports", base), title: Lc::t("section-reports", &LOCALES_ADMIN), - permission: Some(&AdminPermission::AccessReports), weight: 40, }); bag.add(AdminSection { key: "help".to_owned(), path: format!("{}/help", base), title: Lc::t("section-help", &LOCALES_ADMIN), - permission: None, weight: 50, }); } diff --git a/extensions/pagetop-user/src/lib.rs b/extensions/pagetop-user/src/lib.rs index 6724df04..4147b1ca 100644 --- a/extensions/pagetop-user/src/lib.rs +++ b/extensions/pagetop-user/src/lib.rs @@ -155,7 +155,7 @@ fn declare_admin_pages(bag: &mut PageBag) { title: Lc::t("title-admin-users", &LOCALES_USER), description: Some(Lc::t("description-admin-users", &LOCALES_USER)), weight: 0, - permission: Some(&permission::UserPermission::AdminUsers), + permission: &permission::UserPermission::AdminUsers, kind: AdminPageKind::View, }); bag.add(AdminPage { @@ -164,7 +164,7 @@ fn declare_admin_pages(bag: &mut PageBag) { title: Lc::t("title-admin-roles", &LOCALES_USER), description: Some(Lc::t("description-admin-roles", &LOCALES_USER)), weight: 10, - permission: Some(&permission::UserPermission::AdminRoles), + permission: &permission::UserPermission::AdminRoles, kind: AdminPageKind::View, }); bag.add(AdminPage { @@ -173,7 +173,7 @@ fn declare_admin_pages(bag: &mut PageBag) { title: Lc::t("title-admin-permissions", &LOCALES_USER), description: Some(Lc::t("description-admin-permissions", &LOCALES_USER)), weight: 20, - permission: Some(&permission::UserPermission::AdminPermissions), + permission: &permission::UserPermission::AdminPermissions, kind: AdminPageKind::View, }); }