♻️ Major code restructuring
This commit is contained in:
parent
a96e203bb3
commit
fa66d628a0
221 changed files with 228 additions and 315 deletions
18
packages/pagetop-admin/Cargo.toml
Normal file
18
packages/pagetop-admin/Cargo.toml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "pagetop-admin"
|
||||
version = "0.0.16"
|
||||
edition = "2021"
|
||||
|
||||
authors = [
|
||||
"Manuel Cillero <manuel@cillero.es>"
|
||||
]
|
||||
description = """\
|
||||
Module for PageTop that provides a common place for other modules to handle general site \
|
||||
settings for administrators.\
|
||||
"""
|
||||
homepage = "https://pagetop.cillero.es"
|
||||
repository = "https://github.com/manuelcillero/pagetop"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
pagetop = { version = "0.0", path = "../../" }
|
||||
27
packages/pagetop-admin/README.md
Normal file
27
packages/pagetop-admin/README.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
Módulo para **PageTop** que proporciona a otros módulos un lugar común donde presentar a los
|
||||
administradores sus opciones de configuración.
|
||||
|
||||
[PageTop](https://github.com/manuelcillero/pagetop/tree/main/pagetop), es un entorno de desarrollo
|
||||
basado en algunos de los *crates* más estables y populares del ecosistema Rust para proporcionar
|
||||
APIs, patrones de desarrollo y buenas prácticas para la creación de soluciones web SSR (*Server-Side
|
||||
Rendering*).
|
||||
|
||||
|
||||
# 🚧 Advertencia
|
||||
|
||||
**PageTop** sólo libera actualmente versiones de desarrollo. La API no es estable y los cambios son
|
||||
constantes. No puede considerarse preparado hasta que se libere la versión **0.1.0**.
|
||||
|
||||
|
||||
# 📜 Licencia
|
||||
|
||||
Este proyecto tiene licencia, de hecho tiene dos, puedes aplicar cualquiera de las siguientes a tu
|
||||
elección:
|
||||
|
||||
* Licencia Apache versión 2.0
|
||||
([LICENSE-APACHE](https://github.com/manuelcillero/pagetop/blob/main/LICENSE-APACHE) o
|
||||
[http://www.apache.org/licenses/LICENSE-2.0]).
|
||||
|
||||
* Licencia MIT
|
||||
([LICENSE-MIT](https://github.com/manuelcillero/pagetop/blob/main/LICENSE-MIT) o
|
||||
[http://opensource.org/licenses/MIT]).
|
||||
40
packages/pagetop-admin/src/lib.rs
Normal file
40
packages/pagetop-admin/src/lib.rs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
use pagetop::prelude::*;
|
||||
|
||||
static_locales!(LOCALES_ADMIN);
|
||||
|
||||
mod summary;
|
||||
|
||||
#[derive(AssignHandle)]
|
||||
pub struct Admin;
|
||||
|
||||
impl PackageTrait for Admin {
|
||||
fn name(&self) -> L10n {
|
||||
L10n::t("package_name", &LOCALES_ADMIN)
|
||||
}
|
||||
|
||||
fn description(&self) -> L10n {
|
||||
L10n::t("package_description", &LOCALES_ADMIN)
|
||||
}
|
||||
|
||||
fn actions(&self) -> Vec<Action> {
|
||||
actions![
|
||||
action::page::BeforePrepareBody::new(before_prepare_body),
|
||||
action::component::BeforePrepareComponent::<Menu>::new(before_prepare_menu)
|
||||
.filter_by_referer_id("admin-menu-test"),
|
||||
]
|
||||
}
|
||||
|
||||
fn configure_service(&self, scfg: &mut service::web::ServiceConfig) {
|
||||
scfg.service(
|
||||
service::web::scope("/admin").route("", service::web::get().to(summary::summary)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn before_prepare_body(page: &mut Page) {
|
||||
page.alter_body_classes(ClassesOp::Add, "test-admin");
|
||||
}
|
||||
|
||||
fn before_prepare_menu(component: &mut Menu, _cx: &mut Context) {
|
||||
component.alter_id("admin-menu-test-altered");
|
||||
}
|
||||
2
packages/pagetop-admin/src/locale/en-US/admin.ftl
Normal file
2
packages/pagetop-admin/src/locale/en-US/admin.ftl
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
package_name = Admin module
|
||||
package_description = Administration module.
|
||||
2
packages/pagetop-admin/src/locale/es-ES/admin.ftl
Normal file
2
packages/pagetop-admin/src/locale/es-ES/admin.ftl
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
package_name = Admin module
|
||||
package_description = Módulo de administración.
|
||||
170
packages/pagetop-admin/src/summary.rs
Normal file
170
packages/pagetop-admin/src/summary.rs
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
use crate::LOCALES_ADMIN;
|
||||
|
||||
use pagetop::prelude::*;
|
||||
|
||||
pub async fn summary(request: service::HttpRequest) -> ResultPage<Markup, ErrorPage> {
|
||||
let top_menu = Menu::new()
|
||||
.with_id("admin-menu-test")
|
||||
.add_item(menu::Item::label(L10n::t("package_name", &LOCALES_ADMIN)))
|
||||
.add_item(menu::Item::label(L10n::n("Ejemplo \"Label\"")))
|
||||
.add_item(menu::Item::link(L10n::n("Ejemplo \"Link\""), |_| {
|
||||
"https://www.google.es"
|
||||
}))
|
||||
.add_item(menu::Item::link_blank(
|
||||
L10n::n("Ejemplo \"LinkBlank\""),
|
||||
|_| "https://www.google.es",
|
||||
))
|
||||
.add_item(menu::Item::submenu(
|
||||
L10n::n("Ejemplo Submenú"),
|
||||
menu::Submenu::new()
|
||||
.with_title(L10n::n("Título submenú"))
|
||||
.add_item(menu::Item::link(L10n::n("Opción \"Link\""), |_| {
|
||||
"https://www.google.es"
|
||||
}))
|
||||
.add_item(menu::Item::link_blank(
|
||||
L10n::n("Opción \"LinkBlank\""),
|
||||
|_| "https://www.google.es",
|
||||
))
|
||||
.add_item(menu::Item::submenu(
|
||||
L10n::n("Otro submenú con un texto muy, pero que muy largo"),
|
||||
menu::Submenu::new()
|
||||
.add_item(menu::Item::label(L10n::n("Opción \"Label\"")))
|
||||
.add_item(menu::Item::link(L10n::n("Opción \"Link\""), |_| {
|
||||
"https://www.google.es"
|
||||
}))
|
||||
.add_item(menu::Item::link_blank(
|
||||
L10n::n("Opción \"LinkBlank\""),
|
||||
|_| "https://www.google.es",
|
||||
))
|
||||
.add_item(menu::Item::label(L10n::n("Opción \"Label\""))),
|
||||
))
|
||||
.add_item(menu::Item::label(L10n::n("Opción \"Label\""))),
|
||||
))
|
||||
.add_item(menu::Item::megamenu(
|
||||
L10n::n("Ejemplo Megamenú 1"),
|
||||
menu::Megamenu::new()
|
||||
.add_group(
|
||||
menu::Group::new()
|
||||
.add_element(menu::Element::submenu(
|
||||
menu::Submenu::new()
|
||||
.with_title(L10n::n("Título submenú"))
|
||||
.add_item(menu::Item::label(L10n::n("Opción \"Label\"")))
|
||||
.add_item(menu::Item::link(L10n::n("Opción \"Link\""), |_| {
|
||||
"https://www.google.es"
|
||||
}))
|
||||
.add_item(menu::Item::link_blank(
|
||||
L10n::n("Opción \"LinkBlank\""),
|
||||
|_| "https://www.google.es",
|
||||
)),
|
||||
))
|
||||
.add_element(menu::Element::submenu(
|
||||
menu::Submenu::new()
|
||||
.with_title(L10n::n("Título submenú"))
|
||||
.add_item(menu::Item::label(L10n::n("Opción \"Label\"")))
|
||||
.add_item(menu::Item::link(L10n::n("Opción \"Link\""), |_| {
|
||||
"https://www.google.es"
|
||||
}))
|
||||
.add_item(menu::Item::link_blank(
|
||||
L10n::n("Opción \"LinkBlank\""),
|
||||
|_| "https://www.google.es",
|
||||
)),
|
||||
)),
|
||||
)
|
||||
.add_group(
|
||||
menu::Group::new().add_element(menu::Element::submenu(
|
||||
menu::Submenu::new()
|
||||
.add_item(menu::Item::label(L10n::n("Opción \"Label\"")))
|
||||
.add_item(menu::Item::link(L10n::n("Opción \"Link\""), |_| {
|
||||
"https://www.google.es"
|
||||
}))
|
||||
.add_item(menu::Item::link_blank(
|
||||
L10n::n("Opción \"LinkBlank\""),
|
||||
|_| "https://www.google.es",
|
||||
))
|
||||
.add_item(menu::Item::label(L10n::n("Opción \"Label\""))),
|
||||
)),
|
||||
)
|
||||
.add_group(
|
||||
menu::Group::new()
|
||||
.add_element(menu::Element::submenu(
|
||||
menu::Submenu::new()
|
||||
.with_title(L10n::n("Título submenú"))
|
||||
.add_item(menu::Item::label(L10n::n("Opción \"Label\"")))
|
||||
.add_item(menu::Item::link(L10n::n("Opción \"Link\""), |_| {
|
||||
"https://www.google.es"
|
||||
}))
|
||||
.add_item(menu::Item::link_blank(
|
||||
L10n::n("Opción \"LinkBlank\""),
|
||||
|_| "https://www.google.es",
|
||||
)),
|
||||
))
|
||||
.add_element(menu::Element::submenu(
|
||||
menu::Submenu::new()
|
||||
.with_title(L10n::n("Título submenú"))
|
||||
.add_item(menu::Item::label(L10n::n("Opción \"Label\"")))
|
||||
.add_item(menu::Item::link(L10n::n("Opción \"Link\""), |_| {
|
||||
"https://www.google.es"
|
||||
}))
|
||||
.add_item(menu::Item::link_blank(
|
||||
L10n::n("Opción \"LinkBlank\""),
|
||||
|_| "https://www.google.es",
|
||||
)),
|
||||
)),
|
||||
)
|
||||
.add_group(
|
||||
menu::Group::new().add_element(menu::Element::submenu(
|
||||
menu::Submenu::new()
|
||||
.add_item(menu::Item::label(L10n::n("Opción \"Label\"")))
|
||||
.add_item(menu::Item::link(L10n::n("Opción \"Link\""), |_| {
|
||||
"https://www.google.es"
|
||||
}))
|
||||
.add_item(menu::Item::link_blank(
|
||||
L10n::n("Opción \"LinkBlank\""),
|
||||
|_| "https://www.google.es",
|
||||
))
|
||||
.add_item(menu::Item::label(L10n::n("Opción \"Label\""))),
|
||||
)),
|
||||
),
|
||||
));
|
||||
|
||||
let side_menu = Menu::new()
|
||||
.add_item(menu::Item::label(L10n::n("Opción 1")))
|
||||
.add_item(menu::Item::link(L10n::n("Opción 2"), |_| {
|
||||
"https://www.google.es"
|
||||
}))
|
||||
.add_item(menu::Item::link_blank(L10n::n("Opción 3"), |_| {
|
||||
"https://www.google.es"
|
||||
}))
|
||||
.add_item(menu::Item::submenu(
|
||||
L10n::n("Submenú 1"),
|
||||
menu::Submenu::new()
|
||||
.add_item(menu::Item::label(L10n::n("Opción 1")))
|
||||
.add_item(menu::Item::label(L10n::n("Opción 2"))),
|
||||
)) /*
|
||||
.add_item(menu::Item::separator()) */
|
||||
.add_item(menu::Item::submenu(
|
||||
L10n::n("Submenú 2"),
|
||||
menu::Submenu::new()
|
||||
.add_item(menu::Item::label(L10n::n("Opción 1")))
|
||||
.add_item(menu::Item::label(L10n::n("Opción 2"))),
|
||||
))
|
||||
.add_item(menu::Item::label(L10n::n("Opción 4")));
|
||||
|
||||
Page::new(request)
|
||||
//.with_context(ContextOp::Theme("Bootsier"))
|
||||
.with_title(L10n::n("Admin"))
|
||||
.with_component_in("top-menu", side_menu)
|
||||
.with_component_in(
|
||||
"content",
|
||||
flex::Container::new()
|
||||
.add_item(flex::Item::new().add_component(Html::with(html! {
|
||||
p { "Columna 1"}
|
||||
})))
|
||||
.add_item(flex::Item::new().add_component(top_menu))
|
||||
.add_item(flex::Item::new().add_component(Html::with(html! {
|
||||
p { "Columna 3"}
|
||||
}))),
|
||||
)
|
||||
.with_template("admin")
|
||||
.render()
|
||||
}
|
||||
21
packages/pagetop-bootsier/Cargo.toml
Normal file
21
packages/pagetop-bootsier/Cargo.toml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
[package]
|
||||
name = "pagetop-bootsier"
|
||||
version = "0.0.13"
|
||||
edition = "2021"
|
||||
|
||||
authors = [
|
||||
"Manuel Cillero <manuel@cillero.es>"
|
||||
]
|
||||
description = """\
|
||||
Theme for PageTop that uses the Bootstrap framework for page layout and components display.\
|
||||
"""
|
||||
homepage = "https://pagetop.cillero.es"
|
||||
repository = "https://github.com/manuelcillero/pagetop"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
pagetop = { version = "0.0", path = "../../" }
|
||||
static-files = "0.2.3"
|
||||
|
||||
[build-dependencies]
|
||||
pagetop-build = { version = "0.0", path = "../../helpers/pagetop-build" }
|
||||
27
packages/pagetop-bootsier/README.md
Normal file
27
packages/pagetop-bootsier/README.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
Tema para **PageTop** que utiliza el *framework* [Bootstrap](https://getbootstrap.com/) para la
|
||||
composición de páginas y visualización de componentes.
|
||||
|
||||
[PageTop](https://github.com/manuelcillero/pagetop/tree/main/pagetop), es un entorno de desarrollo
|
||||
basado en algunos de los *crates* más estables y populares del ecosistema Rust para proporcionar
|
||||
APIs, patrones de desarrollo y buenas prácticas para la creación de soluciones web SSR (*Server-Side
|
||||
Rendering*).
|
||||
|
||||
|
||||
# 🚧 Advertencia
|
||||
|
||||
**PageTop** sólo libera actualmente versiones de desarrollo. La API no es estable y los cambios son
|
||||
constantes. No puede considerarse preparado hasta que se libere la versión **0.1.0**.
|
||||
|
||||
|
||||
# 📜 Licencia
|
||||
|
||||
Este proyecto tiene licencia, de hecho tiene dos, puedes aplicar cualquiera de las siguientes a tu
|
||||
elección:
|
||||
|
||||
* Licencia Apache versión 2.0
|
||||
([LICENSE-APACHE](https://github.com/manuelcillero/pagetop/blob/main/LICENSE-APACHE) o
|
||||
[http://www.apache.org/licenses/LICENSE-2.0]).
|
||||
|
||||
* Licencia MIT
|
||||
([LICENSE-MIT](https://github.com/manuelcillero/pagetop/blob/main/LICENSE-MIT) o
|
||||
[http://opensource.org/licenses/MIT]).
|
||||
7
packages/pagetop-bootsier/build.rs
Normal file
7
packages/pagetop-bootsier/build.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
use pagetop_build::StaticFilesBundle;
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
StaticFilesBundle::from_dir("./static")
|
||||
.with_name("bootsier")
|
||||
.build()
|
||||
}
|
||||
239
packages/pagetop-bootsier/src/lib.rs
Normal file
239
packages/pagetop-bootsier/src/lib.rs
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
use pagetop::prelude::*;
|
||||
|
||||
static_locales!(LOCALES_BOOTSIER);
|
||||
|
||||
static_files!(bootsier);
|
||||
|
||||
#[derive(AssignHandle)]
|
||||
pub struct Bootsier;
|
||||
|
||||
impl PackageTrait for Bootsier {
|
||||
fn theme(&self) -> Option<ThemeRef> {
|
||||
Some(&Bootsier)
|
||||
}
|
||||
|
||||
fn configure_service(&self, scfg: &mut service::web::ServiceConfig) {
|
||||
service_for_static_files!(scfg, bootsier => "/bootsier");
|
||||
}
|
||||
}
|
||||
|
||||
impl ThemeTrait for Bootsier {
|
||||
#[rustfmt::skip]
|
||||
fn regions(&self) -> Vec<(&'static str, L10n)> {
|
||||
vec![
|
||||
("header", L10n::t("header", &LOCALES_BOOTSIER)),
|
||||
("nav_branding", L10n::t("nav_branding", &LOCALES_BOOTSIER)),
|
||||
("nav_main", L10n::t("nav_main", &LOCALES_BOOTSIER)),
|
||||
("nav_additional", L10n::t("nav_additional", &LOCALES_BOOTSIER)),
|
||||
("breadcrumb", L10n::t("breadcrumb", &LOCALES_BOOTSIER)),
|
||||
("content", L10n::t("breadcrumb", &LOCALES_BOOTSIER)),
|
||||
("sidebar_first", L10n::t("sidebar_first", &LOCALES_BOOTSIER)),
|
||||
("sidebar_second", L10n::t("sidebar_second", &LOCALES_BOOTSIER)),
|
||||
("footer", L10n::t("footer", &LOCALES_BOOTSIER)),
|
||||
]
|
||||
}
|
||||
|
||||
fn prepare_body(&self, page: &mut Page) -> Markup {
|
||||
match page.template() {
|
||||
"admin" => html! {
|
||||
body class=[page.body_classes().get()] {
|
||||
@for region in &[
|
||||
"top-menu",
|
||||
"side-menu",
|
||||
"content"
|
||||
] {
|
||||
(self.prepare_region(page, region))
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => html! {
|
||||
body class=[page.body_classes().get()] {
|
||||
(self.prepare_region(page, "header"))
|
||||
(self.prepare_region(page, "nav_branding"))
|
||||
(self.prepare_region(page, "nav_main"))
|
||||
(self.prepare_region(page, "nav_additional"))
|
||||
(self.prepare_region(page, "breadcrumb"))
|
||||
(self.prepare_region(page, "content"))
|
||||
(self.prepare_region(page, "sidebar_first"))
|
||||
(self.prepare_region(page, "sidebar_second"))
|
||||
(self.prepare_region(page, "footer"))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn after_prepare_body(&self, page: &mut Page) {
|
||||
page.alter_favicon(Some(Favicon::new().with_icon("/base/favicon.ico")))
|
||||
.alter_context(ContextOp::AddStyleSheet(
|
||||
StyleSheet::at("/bootsier/css/bootstrap.min.css")
|
||||
.with_version("5.1.3")
|
||||
.with_weight(-99),
|
||||
))
|
||||
.alter_context(ContextOp::AddJavaScript(
|
||||
JavaScript::at("/bootsier/js/bootstrap.bundle.min.js")
|
||||
.with_version("5.1.3")
|
||||
.with_weight(-99),
|
||||
))
|
||||
.alter_context(ContextOp::AddBaseAssets)
|
||||
.alter_context(ContextOp::AddStyleSheet(
|
||||
StyleSheet::at("/bootsier/css/styles.css").with_version("0.0.1"),
|
||||
));
|
||||
}
|
||||
|
||||
fn before_prepare_component(&self, component: &mut dyn ComponentTrait, _cx: &mut Context) {
|
||||
match component.handle() {
|
||||
h if Icon::matches_handle(h) => {
|
||||
if let Some(i) = component_as_mut::<Icon>(component) {
|
||||
match i.font_size() {
|
||||
FontSize::ExtraLarge => {
|
||||
i.replace_classes(i.font_size().to_string(), "fs-1");
|
||||
}
|
||||
FontSize::XxLarge => {
|
||||
i.replace_classes(i.font_size().to_string(), "fs-2");
|
||||
}
|
||||
FontSize::XLarge => {
|
||||
i.replace_classes(i.font_size().to_string(), "fs-3");
|
||||
}
|
||||
FontSize::Large => {
|
||||
i.replace_classes(i.font_size().to_string(), "fs-4");
|
||||
}
|
||||
FontSize::Medium => {
|
||||
i.replace_classes(i.font_size().to_string(), "fs-5");
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
h if Button::matches_handle(h) => {
|
||||
if let Some(b) = component_as_mut::<Button>(component) {
|
||||
match b.style() {
|
||||
ButtonStyle::Default => {
|
||||
b.replace_classes(b.style().to_string(), "btn btn-primary");
|
||||
}
|
||||
ButtonStyle::Info => {
|
||||
b.replace_classes(b.style().to_string(), "btn btn-info");
|
||||
}
|
||||
ButtonStyle::Success => {
|
||||
b.replace_classes(b.style().to_string(), "btn btn-success");
|
||||
}
|
||||
ButtonStyle::Warning => {
|
||||
b.replace_classes(b.style().to_string(), "btn btn-warning");
|
||||
}
|
||||
ButtonStyle::Danger => {
|
||||
b.replace_classes(b.style().to_string(), "btn btn-danger");
|
||||
}
|
||||
ButtonStyle::Light => {
|
||||
b.replace_classes(b.style().to_string(), "btn btn-light");
|
||||
}
|
||||
ButtonStyle::Dark => {
|
||||
b.replace_classes(b.style().to_string(), "btn btn-dark");
|
||||
}
|
||||
ButtonStyle::Link => {
|
||||
b.replace_classes(b.style().to_string(), "btn btn-link");
|
||||
}
|
||||
};
|
||||
match b.font_size() {
|
||||
FontSize::ExtraLarge => {
|
||||
b.replace_classes(b.font_size().to_string(), "fs-1");
|
||||
}
|
||||
FontSize::XxLarge => {
|
||||
b.replace_classes(b.font_size().to_string(), "fs-2");
|
||||
}
|
||||
FontSize::XLarge => {
|
||||
b.replace_classes(b.font_size().to_string(), "fs-3");
|
||||
}
|
||||
FontSize::Large => {
|
||||
b.replace_classes(b.font_size().to_string(), "fs-4");
|
||||
}
|
||||
FontSize::Medium => {
|
||||
b.replace_classes(b.font_size().to_string(), "fs-5");
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
h if Heading::matches_handle(h) => {
|
||||
if let Some(h) = component_as_mut::<Heading>(component) {
|
||||
match h.size() {
|
||||
HeadingSize::ExtraLarge => {
|
||||
h.replace_classes(h.size().to_string(), "display-1");
|
||||
}
|
||||
HeadingSize::XxLarge => {
|
||||
h.replace_classes(h.size().to_string(), "display-2");
|
||||
}
|
||||
HeadingSize::XLarge => {
|
||||
h.replace_classes(h.size().to_string(), "display-3");
|
||||
}
|
||||
HeadingSize::Large => {
|
||||
h.replace_classes(h.size().to_string(), "display-4");
|
||||
}
|
||||
HeadingSize::Medium => {
|
||||
h.replace_classes(h.size().to_string(), "display-5");
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
h if Paragraph::matches_handle(h) => {
|
||||
if let Some(p) = component_as_mut::<Paragraph>(component) {
|
||||
match p.font_size() {
|
||||
FontSize::ExtraLarge => {
|
||||
p.replace_classes(p.font_size().to_string(), "fs-1");
|
||||
}
|
||||
FontSize::XxLarge => {
|
||||
p.replace_classes(p.font_size().to_string(), "fs-2");
|
||||
}
|
||||
FontSize::XLarge => {
|
||||
p.replace_classes(p.font_size().to_string(), "fs-3");
|
||||
}
|
||||
FontSize::Large => {
|
||||
p.replace_classes(p.font_size().to_string(), "fs-4");
|
||||
}
|
||||
FontSize::Medium => {
|
||||
p.replace_classes(p.font_size().to_string(), "fs-5");
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn render_component(&self, component: &dyn ComponentTrait, cx: &mut Context) -> Option<Markup> {
|
||||
match component.handle() {
|
||||
h if Error404::matches_handle(h) => Some(html! {
|
||||
div class="jumbotron" {
|
||||
div class="media" {
|
||||
img
|
||||
src="/bootsier/images/caution.png"
|
||||
class="mr-4"
|
||||
style="width: 20%; max-width: 188px"
|
||||
alt="Caution!";
|
||||
div class="media-body" {
|
||||
h1 class="display-4" { ("RESOURCE NOT FOUND") }
|
||||
p class="lead" {
|
||||
(L10n::t("e404-description", &LOCALES_BOOTSIER)
|
||||
.escaped(cx.langid()))
|
||||
}
|
||||
hr class="my-4";
|
||||
p {
|
||||
(L10n::t("e404-description", &LOCALES_BOOTSIER)
|
||||
.escaped(cx.langid()))
|
||||
}
|
||||
a
|
||||
class="btn btn-primary btn-lg"
|
||||
href="/"
|
||||
role="button"
|
||||
{
|
||||
(L10n::t("back-homepage", &LOCALES_BOOTSIER)
|
||||
.escaped(cx.langid()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
6
packages/pagetop-bootsier/src/locale/en-US/bootsier.ftl
Normal file
6
packages/pagetop-bootsier/src/locale/en-US/bootsier.ftl
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
e404-description = Oops! Page Not Found
|
||||
e404-message = The page you are looking for may have been removed, had its name changed, or is temporarily unavailable.
|
||||
e500-description = Oops! Unexpected Error
|
||||
e500-message = We're having an issue. Please report this error to an administrator.
|
||||
back-homepage = Back to homepage
|
||||
|
||||
9
packages/pagetop-bootsier/src/locale/en-US/regions.ftl
Normal file
9
packages/pagetop-bootsier/src/locale/en-US/regions.ftl
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
header = Header
|
||||
nav_branding = Navigation branding region
|
||||
nav_main = Main navigation region
|
||||
nav_additional = Additional navigation region (eg search form, social icons, etc)
|
||||
breadcrumb = Breadcrumb
|
||||
content = Main content
|
||||
sidebar_first = Sidebar first
|
||||
sidebar_second = Sidebar second
|
||||
footer = Footer
|
||||
5
packages/pagetop-bootsier/src/locale/es-ES/bootsier.ftl
Normal file
5
packages/pagetop-bootsier/src/locale/es-ES/bootsier.ftl
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
e404-description = ¡Vaya! Página No Encontrada
|
||||
e404-message = La página que está buscando puede haber sido eliminada, cambiada de nombre o no está disponible temporalmente.
|
||||
e500-description = ¡Vaya! Error Inesperado
|
||||
e500-message = Está ocurriendo una incidencia. Por favor, informe de este error a un administrador.
|
||||
back-homepage = Volver al inicio
|
||||
9
packages/pagetop-bootsier/src/locale/es-ES/regions.ftl
Normal file
9
packages/pagetop-bootsier/src/locale/es-ES/regions.ftl
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
header = Cabecera
|
||||
nav_branding = Navegación y marca
|
||||
nav_main = Navegación principal
|
||||
nav_additional = Navegación adicional (p.e. formulario de búsqueda, iconos sociales, etc.)
|
||||
breadcrumb = Ruta de posicionamiento
|
||||
content = Contenido principal
|
||||
sidebar_first = Barra lateral primera
|
||||
sidebar_second = Barra lateral segunda
|
||||
footer = Pie
|
||||
7
packages/pagetop-bootsier/static/css/bootstrap.min.css
vendored
Normal file
7
packages/pagetop-bootsier/static/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
13
packages/pagetop-bootsier/static/css/styles.css
Normal file
13
packages/pagetop-bootsier/static/css/styles.css
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/* OVERRIDE COMPONENT STYLES */
|
||||
|
||||
/* Heading component */
|
||||
|
||||
.pt-heading__subtitle {
|
||||
margin-top: calc(-1 * var(--pt-gap-0-35));
|
||||
}
|
||||
|
||||
/* Button component */
|
||||
|
||||
.btn-link {
|
||||
text-decoration: none;
|
||||
}
|
||||
BIN
packages/pagetop-bootsier/static/images/caution.png
Normal file
BIN
packages/pagetop-bootsier/static/images/caution.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
7
packages/pagetop-bootsier/static/js/bootstrap.bundle.min.js
vendored
Normal file
7
packages/pagetop-bootsier/static/js/bootstrap.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
21
packages/pagetop-bulmix/Cargo.toml
Normal file
21
packages/pagetop-bulmix/Cargo.toml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
[package]
|
||||
name = "pagetop-bulmix"
|
||||
version = "0.0.12"
|
||||
edition = "2021"
|
||||
|
||||
authors = [
|
||||
"Manuel Cillero <manuel@cillero.es>"
|
||||
]
|
||||
description = """\
|
||||
Theme for PageTop that uses the Bulma framework for page layout and components display.\
|
||||
"""
|
||||
homepage = "https://pagetop.cillero.es"
|
||||
repository = "https://github.com/manuelcillero/pagetop"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
pagetop = { version = "0.0", path = "../../" }
|
||||
static-files = "0.2.3"
|
||||
|
||||
[build-dependencies]
|
||||
pagetop-build = { version = "0.0", path = "../../helpers/pagetop-build" }
|
||||
27
packages/pagetop-bulmix/README.md
Normal file
27
packages/pagetop-bulmix/README.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
Tema para **PageTop** que utiliza el *framework* [Bulma](https://bulma.io/) para la composición de
|
||||
páginas y visualización de componentes.
|
||||
|
||||
[PageTop](https://github.com/manuelcillero/pagetop/tree/main/pagetop), es un entorno de desarrollo
|
||||
basado en algunos de los *crates* más estables y populares del ecosistema Rust para proporcionar
|
||||
APIs, patrones de desarrollo y buenas prácticas para la creación de soluciones web SSR (*Server-Side
|
||||
Rendering*).
|
||||
|
||||
|
||||
# 🚧 Advertencia
|
||||
|
||||
**PageTop** sólo libera actualmente versiones de desarrollo. La API no es estable y los cambios son
|
||||
constantes. No puede considerarse preparado hasta que se libere la versión **0.1.0**.
|
||||
|
||||
|
||||
# 📜 Licencia
|
||||
|
||||
Este proyecto tiene licencia, de hecho tiene dos, puedes aplicar cualquiera de las siguientes a tu
|
||||
elección:
|
||||
|
||||
* Licencia Apache versión 2.0
|
||||
([LICENSE-APACHE](https://github.com/manuelcillero/pagetop/blob/main/LICENSE-APACHE) o
|
||||
[http://www.apache.org/licenses/LICENSE-2.0]).
|
||||
|
||||
* Licencia MIT
|
||||
([LICENSE-MIT](https://github.com/manuelcillero/pagetop/blob/main/LICENSE-MIT) o
|
||||
[http://opensource.org/licenses/MIT]).
|
||||
7
packages/pagetop-bulmix/build.rs
Normal file
7
packages/pagetop-bulmix/build.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
use pagetop_build::StaticFilesBundle;
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
StaticFilesBundle::from_dir("./static")
|
||||
.with_name("bulmix")
|
||||
.build()
|
||||
}
|
||||
159
packages/pagetop-bulmix/src/lib.rs
Normal file
159
packages/pagetop-bulmix/src/lib.rs
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
use pagetop::prelude::*;
|
||||
|
||||
static_files!(bulmix);
|
||||
|
||||
#[derive(AssignHandle)]
|
||||
pub struct Bulmix;
|
||||
|
||||
impl PackageTrait for Bulmix {
|
||||
fn theme(&self) -> Option<ThemeRef> {
|
||||
Some(&Bulmix)
|
||||
}
|
||||
|
||||
fn configure_service(&self, scfg: &mut service::web::ServiceConfig) {
|
||||
service_for_static_files!(scfg, bulmix => "/bulmix");
|
||||
}
|
||||
}
|
||||
|
||||
impl ThemeTrait for Bulmix {
|
||||
fn after_prepare_body(&self, page: &mut Page) {
|
||||
page.alter_favicon(Some(Favicon::new().with_icon("/base/favicon.ico")))
|
||||
.alter_context(ContextOp::AddStyleSheet(
|
||||
StyleSheet::at("/bulmix/css/bulma.min.css")
|
||||
.with_version("0.9.4")
|
||||
.with_weight(-99),
|
||||
))
|
||||
.alter_context(ContextOp::AddBaseAssets)
|
||||
.alter_context(ContextOp::AddStyleSheet(
|
||||
StyleSheet::at("/bulmix/css/styles.css").with_version("0.0.1"),
|
||||
));
|
||||
}
|
||||
|
||||
fn before_prepare_component(&self, component: &mut dyn ComponentTrait, _cx: &mut Context) {
|
||||
match component.handle() {
|
||||
h if Icon::matches_handle(h) => {
|
||||
if let Some(i) = component_as_mut::<Icon>(component) {
|
||||
match i.font_size() {
|
||||
FontSize::ExtraLarge => {
|
||||
i.replace_classes(i.font_size().to_string(), "is-size-1");
|
||||
}
|
||||
FontSize::XxLarge => {
|
||||
i.replace_classes(i.font_size().to_string(), "is-size-2");
|
||||
}
|
||||
FontSize::XLarge => {
|
||||
i.replace_classes(i.font_size().to_string(), "is-size-3");
|
||||
}
|
||||
FontSize::Large => {
|
||||
i.replace_classes(i.font_size().to_string(), "is-size-4");
|
||||
}
|
||||
FontSize::Medium => {
|
||||
i.replace_classes(i.font_size().to_string(), "is-size-5");
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
h if Button::matches_handle(h) => {
|
||||
if let Some(b) = component_as_mut::<Button>(component) {
|
||||
match b.style() {
|
||||
ButtonStyle::Default => {
|
||||
b.replace_classes(b.style().to_string(), "button is-primary");
|
||||
}
|
||||
ButtonStyle::Info => {
|
||||
b.replace_classes(b.style().to_string(), "button is-info");
|
||||
}
|
||||
ButtonStyle::Success => {
|
||||
b.replace_classes(b.style().to_string(), "button is-success");
|
||||
}
|
||||
ButtonStyle::Warning => {
|
||||
b.replace_classes(b.style().to_string(), "button is-warning");
|
||||
}
|
||||
ButtonStyle::Danger => {
|
||||
b.replace_classes(b.style().to_string(), "button is-danger");
|
||||
}
|
||||
ButtonStyle::Light => {
|
||||
b.replace_classes(b.style().to_string(), "button is-light");
|
||||
}
|
||||
ButtonStyle::Dark => {
|
||||
b.replace_classes(b.style().to_string(), "button is-dark");
|
||||
}
|
||||
ButtonStyle::Link => {
|
||||
b.replace_classes(b.style().to_string(), "button is-text");
|
||||
}
|
||||
};
|
||||
match b.font_size() {
|
||||
FontSize::ExtraLarge => {
|
||||
b.replace_classes(b.font_size().to_string(), "is-size-1");
|
||||
}
|
||||
FontSize::XxLarge => {
|
||||
b.replace_classes(b.font_size().to_string(), "is-size-2");
|
||||
}
|
||||
FontSize::XLarge => {
|
||||
b.replace_classes(b.font_size().to_string(), "is-size-3");
|
||||
}
|
||||
FontSize::Large => {
|
||||
b.replace_classes(b.font_size().to_string(), "is-size-4");
|
||||
}
|
||||
FontSize::Medium => {
|
||||
b.replace_classes(b.font_size().to_string(), "is-size-5");
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
h if Heading::matches_handle(h) => {
|
||||
if let Some(h) = component_as_mut::<Heading>(component) {
|
||||
match h.size() {
|
||||
HeadingSize::Subtitle => {
|
||||
h.replace_classes(h.size().to_string(), "subtitle")
|
||||
}
|
||||
_ => h.add_classes("title"),
|
||||
};
|
||||
}
|
||||
}
|
||||
h if Paragraph::matches_handle(h) => {
|
||||
if let Some(p) = component_as_mut::<Paragraph>(component) {
|
||||
p.add_classes("block");
|
||||
match p.font_size() {
|
||||
FontSize::ExtraLarge => {
|
||||
p.replace_classes(p.font_size().to_string(), "is-size-1");
|
||||
}
|
||||
FontSize::XxLarge => {
|
||||
p.replace_classes(p.font_size().to_string(), "is-size-2");
|
||||
}
|
||||
FontSize::XLarge => {
|
||||
p.replace_classes(p.font_size().to_string(), "is-size-3");
|
||||
}
|
||||
FontSize::Large => {
|
||||
p.replace_classes(p.font_size().to_string(), "is-size-4");
|
||||
}
|
||||
FontSize::Medium => {
|
||||
p.replace_classes(p.font_size().to_string(), "is-size-5");
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn render_component(
|
||||
&self,
|
||||
component: &dyn ComponentTrait,
|
||||
_cx: &mut Context,
|
||||
) -> Option<Markup> {
|
||||
match component.handle() {
|
||||
h if Icon::matches_handle(h) => {
|
||||
if let Some(i) = component_as_ref::<Icon>(component) {
|
||||
return match i.icon_name().get() {
|
||||
None => None,
|
||||
_ => Some(html! { span class="icon" { i class=[i.classes().get()] {} } }),
|
||||
};
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
1
packages/pagetop-bulmix/static/css/bulma-rtl.min.css
vendored
Normal file
1
packages/pagetop-bulmix/static/css/bulma-rtl.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
packages/pagetop-bulmix/static/css/bulma-rtl.min.css.map
Normal file
1
packages/pagetop-bulmix/static/css/bulma-rtl.min.css.map
Normal file
File diff suppressed because one or more lines are too long
1
packages/pagetop-bulmix/static/css/bulma.min.css
vendored
Normal file
1
packages/pagetop-bulmix/static/css/bulma.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
packages/pagetop-bulmix/static/css/bulma.min.css.map
Normal file
1
packages/pagetop-bulmix/static/css/bulma.min.css.map
Normal file
File diff suppressed because one or more lines are too long
11
packages/pagetop-bulmix/static/css/styles.css
Normal file
11
packages/pagetop-bulmix/static/css/styles.css
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
/* OVERRIDE COMPONENT STYLES */
|
||||
|
||||
/* Button component */
|
||||
|
||||
.is-link {
|
||||
text-decoration: none;
|
||||
}
|
||||
21
packages/pagetop-homedemo/Cargo.toml
Normal file
21
packages/pagetop-homedemo/Cargo.toml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
[package]
|
||||
name = "pagetop-homedemo"
|
||||
version = "0.0.12"
|
||||
edition = "2021"
|
||||
|
||||
authors = [
|
||||
"Manuel Cillero <manuel@cillero.es>"
|
||||
]
|
||||
description = """\
|
||||
Module that shows a demo home page to present PageTop.\
|
||||
"""
|
||||
homepage = "https://pagetop.cillero.es"
|
||||
repository = "https://github.com/manuelcillero/pagetop"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
pagetop = { version = "0.0", path = "../../" }
|
||||
static-files = "0.2.3"
|
||||
|
||||
[build-dependencies]
|
||||
pagetop-build = { version = "0.0", path = "../../helpers/pagetop-build" }
|
||||
26
packages/pagetop-homedemo/README.md
Normal file
26
packages/pagetop-homedemo/README.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
Módulo que muestra una página de inicio de demostración para presentar **PageTop**.
|
||||
|
||||
[PageTop](https://github.com/manuelcillero/pagetop/tree/main/pagetop), es un entorno de desarrollo
|
||||
basado en algunos de los *crates* más estables y populares del ecosistema Rust para proporcionar
|
||||
APIs, patrones de desarrollo y buenas prácticas para la creación de soluciones web SSR (*Server-Side
|
||||
Rendering*).
|
||||
|
||||
|
||||
# 🚧 Advertencia
|
||||
|
||||
**PageTop** sólo libera actualmente versiones de desarrollo. La API no es estable y los cambios son
|
||||
constantes. No puede considerarse preparado hasta que se libere la versión **0.1.0**.
|
||||
|
||||
|
||||
# 📜 Licencia
|
||||
|
||||
Este proyecto tiene licencia, de hecho tiene dos, puedes aplicar cualquiera de las siguientes a tu
|
||||
elección:
|
||||
|
||||
* Licencia Apache versión 2.0
|
||||
([LICENSE-APACHE](https://github.com/manuelcillero/pagetop/blob/main/LICENSE-APACHE) o
|
||||
[http://www.apache.org/licenses/LICENSE-2.0]).
|
||||
|
||||
* Licencia MIT
|
||||
([LICENSE-MIT](https://github.com/manuelcillero/pagetop/blob/main/LICENSE-MIT) o
|
||||
[http://opensource.org/licenses/MIT]).
|
||||
7
packages/pagetop-homedemo/build.rs
Normal file
7
packages/pagetop-homedemo/build.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
use pagetop_build::StaticFilesBundle;
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
StaticFilesBundle::from_dir("./static")
|
||||
.with_name("homedemo")
|
||||
.build()
|
||||
}
|
||||
214
packages/pagetop-homedemo/src/lib.rs
Normal file
214
packages/pagetop-homedemo/src/lib.rs
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
use pagetop::prelude::*;
|
||||
|
||||
static_locales!(LOCALES_HOMEDEMO);
|
||||
|
||||
static_files!(homedemo);
|
||||
|
||||
#[derive(AssignHandle)]
|
||||
pub struct HomeDemo;
|
||||
|
||||
impl PackageTrait for HomeDemo {
|
||||
fn name(&self) -> L10n {
|
||||
L10n::t("package_name", &LOCALES_HOMEDEMO)
|
||||
}
|
||||
|
||||
fn description(&self) -> L10n {
|
||||
L10n::t("package_description", &LOCALES_HOMEDEMO)
|
||||
}
|
||||
|
||||
fn configure_service(&self, scfg: &mut service::web::ServiceConfig) {
|
||||
service_for_static_files!(scfg, homedemo => "/homedemo");
|
||||
scfg.route("/", service::web::get().to(demo));
|
||||
}
|
||||
}
|
||||
|
||||
async fn demo(request: service::HttpRequest) -> ResultPage<Markup, ErrorPage> {
|
||||
Page::new(request)
|
||||
.with_title(L10n::t("page_title", &LOCALES_HOMEDEMO))
|
||||
.with_context(ContextOp::AddStyleSheet(StyleSheet::at(
|
||||
"/homedemo/css/styles.css",
|
||||
)))
|
||||
.with_body_classes(ClassesOp::Add, "default-homepage")
|
||||
.with_component_in("content", hello_world())
|
||||
.with_component_in("content", welcome())
|
||||
.with_component_in("content", about_pagetop())
|
||||
.with_component_in("content", promo_pagetop())
|
||||
.with_component_in("content", reporting_issues())
|
||||
.render()
|
||||
}
|
||||
|
||||
fn hello_world() -> Wrapper {
|
||||
Wrapper::header().with_id("hello-world").add_component(
|
||||
flex::Container::new()
|
||||
.with_direction(flex::Direction::Column(BreakPoint::MD))
|
||||
.add_item(
|
||||
flex::Item::new()
|
||||
.with_inner_classes(ClassesOp::Add, "hello-col-text")
|
||||
.with_size(flex::ItemSize::Percent40)
|
||||
.add_component(
|
||||
Heading::h1(L10n::t("page_title", &LOCALES_HOMEDEMO))
|
||||
.with_size(HeadingSize::Medium),
|
||||
)
|
||||
.add_component(
|
||||
Paragraph::translated(L10n::t("hello_intro", &LOCALES_HOMEDEMO).with_arg(
|
||||
"app",
|
||||
format!(
|
||||
"<span class=\"app-name\">{}</span>",
|
||||
&config::SETTINGS.app.name,
|
||||
),
|
||||
))
|
||||
.with_font_size(FontSize::Medium),
|
||||
)
|
||||
.add_component(Paragraph::translated(
|
||||
L10n::t("hello_powered", &LOCALES_HOMEDEMO).with_arg(
|
||||
"pagetop",
|
||||
format!(
|
||||
"<a href=\"{}\" target=\"_blank\">{}</a>",
|
||||
"https://pagetop.cillero.es", "PageTop",
|
||||
),
|
||||
),
|
||||
))
|
||||
.add_component(
|
||||
Button::anchor(
|
||||
"https://github.com/manuelcillero/pagetop",
|
||||
L10n::t("hello_code", &LOCALES_HOMEDEMO),
|
||||
)
|
||||
.with_target(ButtonTarget::Blank)
|
||||
.with_left_icon(Some(Icon::with("git")))
|
||||
.with_classes(ClassesOp::Add, "code-link")
|
||||
.with_font_size(FontSize::Medium),
|
||||
)
|
||||
.add_component(
|
||||
Button::anchor("#welcome", L10n::t("hello_welcome", &LOCALES_HOMEDEMO))
|
||||
.with_style(ButtonStyle::Link)
|
||||
.with_left_icon(Some(Icon::with("arrow-down-circle-fill")))
|
||||
.with_classes(ClassesOp::Add, "welcome-link")
|
||||
.with_font_size(FontSize::Medium),
|
||||
),
|
||||
)
|
||||
.add_item(
|
||||
flex::Item::new()
|
||||
.with_inner_classes(ClassesOp::Add, "hello-col-image")
|
||||
.with_size(flex::ItemSize::Percent60)
|
||||
.add_component(Image::with("/homedemo/images/header.svg")),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn welcome() -> Wrapper {
|
||||
Wrapper::section()
|
||||
.with_id("welcome")
|
||||
.with_classes(ClassesOp::Add, "welcome-col-text")
|
||||
.add_component(Heading::h2(L10n::t("welcome_page", &LOCALES_HOMEDEMO)))
|
||||
.add_component(
|
||||
Heading::h3(L10n::t("welcome_subtitle", &LOCALES_HOMEDEMO).with_arg(
|
||||
"app",
|
||||
format!(
|
||||
"<span class=\"app-name\">{}</span>",
|
||||
&config::SETTINGS.app.name
|
||||
),
|
||||
))
|
||||
.with_size(HeadingSize::Subtitle),
|
||||
)
|
||||
.add_component(
|
||||
Paragraph::translated(L10n::t("welcome_text1", &LOCALES_HOMEDEMO))
|
||||
.with_font_size(FontSize::Medium),
|
||||
)
|
||||
.add_component(Paragraph::translated(L10n::t(
|
||||
"welcome_text2",
|
||||
&LOCALES_HOMEDEMO,
|
||||
)))
|
||||
}
|
||||
|
||||
fn about_pagetop() -> Wrapper {
|
||||
Wrapper::new().with_id("pagetop").add_component(
|
||||
flex::Container::new()
|
||||
.with_direction(flex::Direction::Column(BreakPoint::SM))
|
||||
.add_item(
|
||||
flex::Item::new()
|
||||
.with_inner_classes(ClassesOp::Add, "pagetop-col-image")
|
||||
.with_size(flex::ItemSize::Percent40)
|
||||
.add_component(Image::with("/homedemo/images/about.svg")),
|
||||
)
|
||||
.add_item(
|
||||
flex::Item::new()
|
||||
.with_inner_classes(ClassesOp::Add, "pagetop-col-text")
|
||||
.add_component(Heading::h2(L10n::t("pagetop_title", &LOCALES_HOMEDEMO)))
|
||||
.add_component(
|
||||
Paragraph::translated(L10n::t("pagetop_text1", &LOCALES_HOMEDEMO))
|
||||
.with_font_size(FontSize::Medium),
|
||||
)
|
||||
.add_component(Paragraph::translated(L10n::t(
|
||||
"pagetop_text2",
|
||||
&LOCALES_HOMEDEMO,
|
||||
)))
|
||||
.add_component(Paragraph::translated(
|
||||
L10n::t("pagetop_text3", &LOCALES_HOMEDEMO)
|
||||
.with_arg("href", "https://docs.rs/pagetop/latest/pagetop"),
|
||||
)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn promo_pagetop() -> Wrapper {
|
||||
Wrapper::new().with_id("promo").add_component(
|
||||
flex::Container::new()
|
||||
.with_direction(flex::Direction::Column(BreakPoint::MD))
|
||||
.add_item(
|
||||
flex::Item::new()
|
||||
.with_inner_classes(ClassesOp::Add, "promo-col-text")
|
||||
.with_size(flex::ItemSize::Percent60)
|
||||
.add_component(Heading::h2(L10n::t(
|
||||
"pagetop_promo_title",
|
||||
&LOCALES_HOMEDEMO,
|
||||
)))
|
||||
.add_component(
|
||||
Paragraph::translated(
|
||||
L10n::t("pagetop_promo_text1", &LOCALES_HOMEDEMO).with_arg(
|
||||
"pagetop",
|
||||
format!(
|
||||
"<a href=\"{}\" target=\"_blank\">{}</a>",
|
||||
"https://crates.io/crates/pagetop", "PageTop",
|
||||
),
|
||||
),
|
||||
)
|
||||
.with_font_size(FontSize::Medium),
|
||||
),
|
||||
)
|
||||
.add_item(
|
||||
flex::Item::new()
|
||||
.with_inner_classes(ClassesOp::Add, "promo-col-image")
|
||||
.with_size(flex::ItemSize::Percent40)
|
||||
.add_component(Image::with("/homedemo/images/pagetop.png")),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn reporting_issues() -> Wrapper {
|
||||
Wrapper::new().with_id("reporting").add_component(
|
||||
flex::Container::new()
|
||||
.with_direction(flex::Direction::Column(BreakPoint::MD))
|
||||
.add_item(
|
||||
flex::Item::new()
|
||||
.with_inner_classes(ClassesOp::Add, "reporting-col-image")
|
||||
.add_component(Image::with("/homedemo/images/support.jpg")),
|
||||
)
|
||||
.add_item(
|
||||
flex::Item::new()
|
||||
.with_inner_classes(ClassesOp::Add, "reporting-col-text")
|
||||
.with_size(flex::ItemSize::Percent50)
|
||||
.add_component(Heading::h2(L10n::t(
|
||||
"report_problems_title",
|
||||
&LOCALES_HOMEDEMO,
|
||||
)))
|
||||
.add_component(
|
||||
Paragraph::translated(L10n::t("report_problems_text1", &LOCALES_HOMEDEMO))
|
||||
.with_font_size(FontSize::Medium),
|
||||
)
|
||||
.add_component(Paragraph::translated(L10n::t(
|
||||
"report_problems_text2",
|
||||
&LOCALES_HOMEDEMO,
|
||||
))),
|
||||
),
|
||||
)
|
||||
}
|
||||
26
packages/pagetop-homedemo/src/locale/en-US/homepage.ftl
Normal file
26
packages/pagetop-homedemo/src/locale/en-US/homepage.ftl
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package_name = Default homepage
|
||||
package_description = Displays a demo homepage when none is configured.
|
||||
|
||||
page_title = Hello world!
|
||||
|
||||
hello_intro = This page is used to test the proper operation of { $app } after installation.
|
||||
hello_powered = This web solution is powered by { $pagetop }.
|
||||
hello_code = Code
|
||||
hello_welcome = Welcome
|
||||
|
||||
welcome_page = Welcome page
|
||||
welcome_subtitle = Are you user of { $app }?
|
||||
welcome_text1 = If you don't know what this page is about, this probably means that the site is either experiencing problems or is undergoing routine maintenance.
|
||||
welcome_text2 = If the problem persists, please contact your system administrator.
|
||||
|
||||
pagetop_title = About PageTop
|
||||
pagetop_text1 = If you can read this page, it means that the PageTop server is working properly, but has not yet been configured.
|
||||
pagetop_text2 = PageTop defines an interface for the most stable and popular Rust packages to build modular, extensible and configurable web solutions.
|
||||
pagetop_text3 = For more information on PageTop please visit the <a href="{ $href }" target="_blank">technical documentation</a>.
|
||||
|
||||
pagetop_promo_title = Promoting PageTop
|
||||
pagetop_promo_text1 = You are free to use the image below on applications powered by { $pagetop }. Thanks for using PageTop!
|
||||
|
||||
report_problems_title = Reporting problems
|
||||
report_problems_text1 = Please use the GitHub tool to report bugs in PageTop. However, check "existing bug reports" before reporting a new bug.
|
||||
report_problems_text2 = Please report bugs specific to modules (such as admin, and others) to respective repositories, not to PageTop itself.
|
||||
26
packages/pagetop-homedemo/src/locale/es-ES/homepage.ftl
Normal file
26
packages/pagetop-homedemo/src/locale/es-ES/homepage.ftl
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package_name = Página de inicio predeterminada
|
||||
package_description = Muestra una página de demostración predeterminada cuando no hay ninguna configurada.
|
||||
|
||||
page_title = ¡Hola mundo!
|
||||
|
||||
hello_intro = Esta página se utiliza para comprobar el correcto funcionamiento de { $app } después de la instalación.
|
||||
hello_powered = Esta solución web funciona con { $pagetop }.
|
||||
hello_code = Código
|
||||
hello_welcome = Bienvenida
|
||||
|
||||
welcome_page = Página de bienvenida
|
||||
welcome_subtitle = ¿Eres usuario de { $app }?
|
||||
welcome_text1 = Si no sabes de qué trata esta página, probablemente significa que el sitio está experimentando problemas o está pasando por un mantenimiento de rutina.
|
||||
welcome_text2 = Si el problema persiste, póngase en contacto con el administrador del sistema.
|
||||
|
||||
pagetop_title = Sobre PageTop
|
||||
pagetop_text1 = Si puedes leer esta página, significa que el servidor PageTop funciona correctamente, pero aún no se ha configurado.
|
||||
pagetop_text2 = PageTop define una interfaz para los paquetes Rust más estables y populares para crear soluciones web modulares, extensibles y configurables.
|
||||
pagetop_text3 = Para más información sobre PageTop, por favor visita la <a href="{ $href }" target="_blank">documentación técnica</a>.
|
||||
|
||||
pagetop_promo_title = Promociona PageTop
|
||||
pagetop_promo_text1 = Eres libre de usar la siguiente imagen en aplicaciones desarrolladas con { $pagetop }. ¡Gracias por usar PageTop!
|
||||
|
||||
report_problems_title = Informando problemas
|
||||
report_problems_text1 = Utiliza la herramienta GitHub para informar errores en PageTop. Sin embargo, comprueba los "informes de errores existentes" antes de informar de un nuevo error.
|
||||
report_problems_text2 = Informa de errores específicos de los módulos (como admin y otros) en sus repositorios respectivos, no a PageTop en sí.
|
||||
109
packages/pagetop-homedemo/static/css/styles.css
Normal file
109
packages/pagetop-homedemo/static/css/styles.css
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
body.default-homepage span.app-name {
|
||||
font-weight: 400;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#hello-world,
|
||||
#welcome,
|
||||
#pagetop,
|
||||
#promo,
|
||||
#reporting {
|
||||
padding: 2rem 5%;
|
||||
}
|
||||
|
||||
body.default-homepage [class$="-col-text"] {
|
||||
padding: 0 5%;
|
||||
text-align: center;
|
||||
}
|
||||
body.default-homepage [class$="-col-image"] {
|
||||
padding: 1rem 5%;
|
||||
}
|
||||
/*
|
||||
#hello-world a {
|
||||
margin: .25rem;
|
||||
}
|
||||
#hello-world a.code-link {
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
border-radius: 1.5rem;
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
#hello-world a.welcome-link {
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#hello-world .hello-col-image {
|
||||
padding-top: 3em;
|
||||
}
|
||||
*/
|
||||
#welcome > div.container {
|
||||
padding: 2rem 1rem;
|
||||
border-radius: 28px;
|
||||
background: url("/homedemo/images/welcome.jpg") center center no-repeat;
|
||||
background-size: auto;
|
||||
background-size: cover;
|
||||
color: #fff;
|
||||
}
|
||||
#welcome > div.container > h2 {
|
||||
color: #fff;
|
||||
}
|
||||
#welcome > div.container > h3 {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
#reporting .reporting-col-image img {
|
||||
border-radius: 40px;
|
||||
}
|
||||
|
||||
/* BREAKPOINTS */
|
||||
|
||||
/* MD - Applies <= 768px */
|
||||
@media screen and (min-width: 48em) {
|
||||
body.default-homepage [class$="-col-image"] {
|
||||
padding-top: 5%;
|
||||
}
|
||||
|
||||
#reporting .reporting-col-text {
|
||||
padding-left: 1rem;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
/* LG - Applies >= 992px */
|
||||
@media screen and (min-width: 62em) {
|
||||
#hello-world .hello-col-text {
|
||||
padding-top: 2rem;
|
||||
padding-left: 10%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#welcome {
|
||||
padding-left: 10%;
|
||||
padding-right: 10%;
|
||||
}
|
||||
#welcome > div.container {
|
||||
padding: 2rem 8rem;
|
||||
}
|
||||
|
||||
#promo .promo-col-text {
|
||||
padding-top: 1rem;
|
||||
padding-right: 0;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
/* XL - Applies >= 1280px */
|
||||
@media screen and (min-width: 80em) {
|
||||
body.default-homepage [class$="-col-image"] {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
#hello-world .hello-col-text {
|
||||
padding-top: 4rem;
|
||||
}
|
||||
|
||||
#pagetop .pagetop-col-text {
|
||||
padding-top: 3rem;
|
||||
padding-left: 3rem;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
1
packages/pagetop-homedemo/static/images/about.svg
Normal file
1
packages/pagetop-homedemo/static/images/about.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 33 KiB |
1
packages/pagetop-homedemo/static/images/header.svg
Normal file
1
packages/pagetop-homedemo/static/images/header.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 52 KiB |
BIN
packages/pagetop-homedemo/static/images/pagetop.png
Normal file
BIN
packages/pagetop-homedemo/static/images/pagetop.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
BIN
packages/pagetop-homedemo/static/images/support.jpg
Normal file
BIN
packages/pagetop-homedemo/static/images/support.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 81 KiB |
BIN
packages/pagetop-homedemo/static/images/welcome.jpg
Normal file
BIN
packages/pagetop-homedemo/static/images/welcome.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
18
packages/pagetop-node/Cargo.toml
Normal file
18
packages/pagetop-node/Cargo.toml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "pagetop-node"
|
||||
version = "0.0.16"
|
||||
edition = "2021"
|
||||
|
||||
authors = [
|
||||
"Manuel Cillero <manuel@cillero.es>"
|
||||
]
|
||||
description = """\
|
||||
Module for PageTop to create, extend or customize the types of content that a website can \
|
||||
handle.\
|
||||
"""
|
||||
homepage = "https://pagetop.cillero.es"
|
||||
repository = "https://github.com/manuelcillero/pagetop"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
pagetop = { version = "0.0", path = "../../", features = ["database"], default-features = false }
|
||||
27
packages/pagetop-node/README.md
Normal file
27
packages/pagetop-node/README.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
Módulo para **PageTop** para crear, extender o personalizar los tipos de contenido que puede
|
||||
administrar un sitio web.
|
||||
|
||||
[PageTop](https://github.com/manuelcillero/pagetop/tree/main/pagetop), es un entorno de desarrollo
|
||||
basado en algunos de los *crates* más estables y populares del ecosistema Rust para proporcionar
|
||||
APIs, patrones de desarrollo y buenas prácticas para la creación de soluciones web SSR (*Server-Side
|
||||
Rendering*).
|
||||
|
||||
|
||||
# 🚧 Advertencia
|
||||
|
||||
**PageTop** sólo libera actualmente versiones de desarrollo. La API no es estable y los cambios son
|
||||
constantes. No puede considerarse preparado hasta que se libere la versión **0.1.0**.
|
||||
|
||||
|
||||
# 📜 Licencia
|
||||
|
||||
Este proyecto tiene licencia, de hecho tiene dos, puedes aplicar cualquiera de las siguientes a tu
|
||||
elección:
|
||||
|
||||
* Licencia Apache versión 2.0
|
||||
([LICENSE-APACHE](https://github.com/manuelcillero/pagetop/blob/main/LICENSE-APACHE) o
|
||||
[http://www.apache.org/licenses/LICENSE-2.0]).
|
||||
|
||||
* Licencia MIT
|
||||
([LICENSE-MIT](https://github.com/manuelcillero/pagetop/blob/main/LICENSE-MIT) o
|
||||
[http://opensource.org/licenses/MIT]).
|
||||
44
packages/pagetop-node/src/lib.rs
Normal file
44
packages/pagetop-node/src/lib.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
use pagetop::prelude::*;
|
||||
|
||||
static_locales!(LOCALES_NODE);
|
||||
|
||||
//mod entity;
|
||||
mod migration;
|
||||
|
||||
#[derive(AssignHandle)]
|
||||
pub struct Node;
|
||||
|
||||
impl PackageTrait for Node {
|
||||
fn name(&self) -> L10n {
|
||||
L10n::t("package_name", &LOCALES_NODE)
|
||||
}
|
||||
|
||||
fn description(&self) -> L10n {
|
||||
L10n::t("package_description", &LOCALES_NODE)
|
||||
}
|
||||
|
||||
fn configure_service(&self, scfg: &mut service::web::ServiceConfig) {
|
||||
scfg.route("/node", service::web::get().to(node));
|
||||
}
|
||||
|
||||
fn actions(&self) -> Vec<Action> {
|
||||
actions![action::page::BeforePrepareBody::new(before_prepare_body).with_weight(-1)]
|
||||
}
|
||||
|
||||
fn migrations(&self) -> Vec<MigrationItem> {
|
||||
migrations![
|
||||
m20220316_000001_create_table_node_type,
|
||||
m20220316_000002_create_table_node,
|
||||
m20220316_000003_create_table_node_access,
|
||||
m20220316_000004_create_table_node_revision,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
async fn node(request: service::HttpRequest) -> ResultPage<Markup, ErrorPage> {
|
||||
Page::new(request).with_title(L10n::n("Nodo")).render()
|
||||
}
|
||||
|
||||
fn before_prepare_body(page: &mut Page) {
|
||||
page.alter_body_classes(ClassesOp::Add, "test-node");
|
||||
}
|
||||
2
packages/pagetop-node/src/locale/en-US/homepage.ftl
Normal file
2
packages/pagetop-node/src/locale/en-US/homepage.ftl
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
package_name = Node
|
||||
package_description = Allows content to be submitted to the site and displayed on pages.
|
||||
2
packages/pagetop-node/src/locale/es-ES/homepage.ftl
Normal file
2
packages/pagetop-node/src/locale/es-ES/homepage.ftl
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
package_name = Nodo
|
||||
package_description = Permite enviar contenidos al sitio y mostrarlos en páginas.
|
||||
4
packages/pagetop-node/src/migration.rs
Normal file
4
packages/pagetop-node/src/migration.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
pub mod m20220316_000001_create_table_node_type;
|
||||
pub mod m20220316_000002_create_table_node;
|
||||
pub mod m20220316_000003_create_table_node_access;
|
||||
pub mod m20220316_000004_create_table_node_revision;
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
use pagetop::prelude::*;
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(Iden)]
|
||||
enum NodeType {
|
||||
Table, // node_type: Stores information about all defined Node types.
|
||||
|
||||
Type, // The machine-readable name of this type.
|
||||
Name, // The human-readable name of this type.
|
||||
Description, // Descripción breve del tipo.
|
||||
Help, // Help information shown to the user when creating a Node of this type.
|
||||
HasTitle, // Boolean indicating whether this type uses the Node.Title field.
|
||||
TitleLabel, // The label displayed for the title field on the edit form.
|
||||
Custom, // A boolean indicating whether this type is defined by a module (FALSE) or
|
||||
// by a user via Add content type (TRUE).
|
||||
Locked, // A boolean indicating whether the administrator can change the machine
|
||||
// name of this type.
|
||||
Disabled, // A boolean indicating whether the node type is disabled.
|
||||
OrigType, // The original machine-readable name of this node type, this may be
|
||||
// different from the current type name if the locked field is 0.
|
||||
}
|
||||
|
||||
new_migration!(Migration);
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(NodeType::Table)
|
||||
.if_not_exists()
|
||||
.col(
|
||||
ColumnDef::new(NodeType::Type)
|
||||
.integer()
|
||||
.not_null()
|
||||
.auto_increment()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(NodeType::Name).string().not_null())
|
||||
.col(ColumnDef::new(NodeType::Description).string().not_null())
|
||||
.col(ColumnDef::new(NodeType::Help).string().not_null())
|
||||
.col(ColumnDef::new(NodeType::HasTitle).string().not_null())
|
||||
.col(ColumnDef::new(NodeType::TitleLabel).string().not_null())
|
||||
.col(ColumnDef::new(NodeType::Custom).string().not_null())
|
||||
.col(ColumnDef::new(NodeType::Locked).string().not_null())
|
||||
.col(ColumnDef::new(NodeType::Disabled).string().not_null())
|
||||
.col(ColumnDef::new(NodeType::OrigType).string().not_null())
|
||||
.to_owned(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_table(Table::drop().table(NodeType::Table).to_owned())
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
use pagetop::prelude::*;
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(Iden)]
|
||||
enum Node {
|
||||
Table, // node: The base table for nodes.
|
||||
|
||||
Nid, // The primary identifier for a node.
|
||||
Vid, // The current NodeRevision.vid version identifier.
|
||||
Type, // The NodeType.type of this node.
|
||||
Language, // The {languages}.language of this node.
|
||||
Title, // The title of this node, always treated as non-markup plain text.
|
||||
Uid, // The User.uid that owns this node; initially, this is the user that
|
||||
// created it.
|
||||
Status, // Boolean indicating whether the node is published (visible to
|
||||
// non-administrators).
|
||||
Created, // The Unix timestamp when the node was created.
|
||||
Changed, // The Unix timestamp when the node was most recently saved.
|
||||
Comment, // Whether comments are allowed on this node: 0 = no, 1 = closed (read
|
||||
// only), 2 = open (read/write).
|
||||
Promote, // Boolean indicating whether the node should be displayed on the front
|
||||
// page.
|
||||
Sticky, // Boolean indicating whether the node should be displayed at the top of
|
||||
// lists in which it appears.
|
||||
Tnid, // The translation set id for this node, which equals the node id of the
|
||||
// source post in each set.
|
||||
Translate, // A boolean indicating whether this translation page needs to be updated.
|
||||
}
|
||||
|
||||
new_migration!(Migration);
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(Node::Table)
|
||||
.if_not_exists()
|
||||
.col(
|
||||
ColumnDef::new(Node::Nid)
|
||||
.integer()
|
||||
.not_null()
|
||||
.auto_increment()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(Node::Vid).string().not_null())
|
||||
.col(ColumnDef::new(Node::Type).string().not_null())
|
||||
.col(ColumnDef::new(Node::Language).string().not_null())
|
||||
.col(ColumnDef::new(Node::Title).string().not_null())
|
||||
.col(ColumnDef::new(Node::Uid).string().not_null())
|
||||
.col(ColumnDef::new(Node::Status).string().not_null())
|
||||
.col(ColumnDef::new(Node::Created).string().not_null())
|
||||
.col(ColumnDef::new(Node::Changed).string().not_null())
|
||||
.col(ColumnDef::new(Node::Comment).string().not_null())
|
||||
.col(ColumnDef::new(Node::Promote).string().not_null())
|
||||
.col(ColumnDef::new(Node::Sticky).string().not_null())
|
||||
.col(ColumnDef::new(Node::Tnid).string().not_null())
|
||||
.col(ColumnDef::new(Node::Translate).string().not_null())
|
||||
.to_owned(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_table(Table::drop().table(Node::Table).to_owned())
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
use pagetop::prelude::*;
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(Iden)]
|
||||
enum NodeAccess {
|
||||
Table, // node_access: Identifies which realm/grant pairs a user must possess in
|
||||
// order to view, update, or delete specific nodes.
|
||||
|
||||
Nid, // The Node.nid this record affects.
|
||||
Gid, // The grant ID a user must possess in the specified realm to gain this
|
||||
// row's privileges on the node.
|
||||
Realm, // The realm in which the user must possess the grant ID. Each node access
|
||||
// node can define one or more realms.
|
||||
GrantView, // Boolean indicating whether a user with the realm/grant pair can view this
|
||||
// node.
|
||||
GrantUpdate, // Boolean indicating whether a user with the realm/grant pair can edit this
|
||||
// node.
|
||||
GrantDelete, // Boolean indicating whether a user with the realm/grant pair can delete
|
||||
// this node.
|
||||
}
|
||||
|
||||
new_migration!(Migration);
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(NodeAccess::Table)
|
||||
.if_not_exists()
|
||||
.col(
|
||||
ColumnDef::new(NodeAccess::Nid)
|
||||
.integer()
|
||||
.not_null()
|
||||
.auto_increment()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(NodeAccess::Gid).string().not_null())
|
||||
.col(ColumnDef::new(NodeAccess::Realm).string().not_null())
|
||||
.col(ColumnDef::new(NodeAccess::GrantView).string().not_null())
|
||||
.col(ColumnDef::new(NodeAccess::GrantUpdate).string().not_null())
|
||||
.col(ColumnDef::new(NodeAccess::GrantDelete).string().not_null())
|
||||
.to_owned(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_table(Table::drop().table(NodeAccess::Table).to_owned())
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
use pagetop::prelude::*;
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(Iden)]
|
||||
enum NodeRevision {
|
||||
Table, // node_revision: Stores information about each saved version of a Node.
|
||||
|
||||
Nid, // The Node this version belongs to.
|
||||
Vid, // The primary identifier for this version.
|
||||
Uid, // The User.uid that created this version.
|
||||
Title, // The title of this version.
|
||||
Log, // The log entry explaining the changes in this version.
|
||||
Timestamp, // A Unix timestamp indicating when this version was created.
|
||||
Status, // Boolean indicating whether the node (at the time of this revision) is
|
||||
// published (visible to non-administrators).
|
||||
Comment, // Whether comments are allowed on this node (at the time of this revision):
|
||||
// 0 = no, 1 = closed (read only), 2 = open (read/write).
|
||||
Promote, // Boolean indicating whether the node (at the time of this revision) should
|
||||
// be displayed on the front page.
|
||||
Sticky, // Boolean indicating whether the node (at the time of this revision) should
|
||||
// be displayed at the top of lists in which it appears.
|
||||
}
|
||||
|
||||
new_migration!(Migration);
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(NodeRevision::Table)
|
||||
.if_not_exists()
|
||||
.col(
|
||||
ColumnDef::new(NodeRevision::Nid)
|
||||
.integer()
|
||||
.not_null()
|
||||
.auto_increment()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(NodeRevision::Vid).string().not_null())
|
||||
.col(ColumnDef::new(NodeRevision::Uid).string().not_null())
|
||||
.col(ColumnDef::new(NodeRevision::Title).string().not_null())
|
||||
.col(ColumnDef::new(NodeRevision::Log).string().not_null())
|
||||
.col(ColumnDef::new(NodeRevision::Timestamp).string().not_null())
|
||||
.col(ColumnDef::new(NodeRevision::Status).string().not_null())
|
||||
.col(ColumnDef::new(NodeRevision::Comment).string().not_null())
|
||||
.col(ColumnDef::new(NodeRevision::Promote).string().not_null())
|
||||
.col(ColumnDef::new(NodeRevision::Sticky).string().not_null())
|
||||
.to_owned(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_table(Table::drop().table(NodeRevision::Table).to_owned())
|
||||
.await
|
||||
}
|
||||
}
|
||||
19
packages/pagetop-user/Cargo.toml
Normal file
19
packages/pagetop-user/Cargo.toml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "pagetop-user"
|
||||
version = "0.0.14"
|
||||
edition = "2021"
|
||||
|
||||
authors = [
|
||||
"Manuel Cillero <manuel@cillero.es>"
|
||||
]
|
||||
description = """\
|
||||
Module to add user management, roles, permissions and sessions in applications developed with \
|
||||
PageTop.\
|
||||
"""
|
||||
homepage = "https://pagetop.cillero.es"
|
||||
repository = "https://github.com/manuelcillero/pagetop"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
pagetop = { version = "0.0", path = "../../", features = ["database"], default-features = false }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
27
packages/pagetop-user/README.md
Normal file
27
packages/pagetop-user/README.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
Módulo para añadir gestión de usuarios, roles, permisos y sesiones en aplicaciones desarrolladas con
|
||||
**PageTop**.
|
||||
|
||||
[PageTop](https://github.com/manuelcillero/pagetop/tree/main/pagetop), es un entorno de desarrollo
|
||||
basado en algunos de los *crates* más estables y populares del ecosistema Rust para proporcionar
|
||||
APIs, patrones de desarrollo y buenas prácticas para la creación de soluciones web SSR (*Server-Side
|
||||
Rendering*).
|
||||
|
||||
|
||||
# 🚧 Advertencia
|
||||
|
||||
**PageTop** sólo libera actualmente versiones de desarrollo. La API no es estable y los cambios son
|
||||
constantes. No puede considerarse preparado hasta que se libere la versión **0.1.0**.
|
||||
|
||||
|
||||
# 📜 Licencia
|
||||
|
||||
Este proyecto tiene licencia, de hecho tiene dos, puedes aplicar cualquiera de las siguientes a tu
|
||||
elección:
|
||||
|
||||
* Licencia Apache versión 2.0
|
||||
([LICENSE-APACHE](https://github.com/manuelcillero/pagetop/blob/main/LICENSE-APACHE) o
|
||||
[http://www.apache.org/licenses/LICENSE-2.0]).
|
||||
|
||||
* Licencia MIT
|
||||
([LICENSE-MIT](https://github.com/manuelcillero/pagetop/blob/main/LICENSE-MIT) o
|
||||
[http://opensource.org/licenses/MIT]).
|
||||
65
packages/pagetop-user/src/lib.rs
Normal file
65
packages/pagetop-user/src/lib.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
use pagetop::prelude::*;
|
||||
|
||||
static_locales!(LOCALES_USER);
|
||||
|
||||
mod migration;
|
||||
|
||||
#[derive(AssignHandle)]
|
||||
pub struct User;
|
||||
|
||||
impl PackageTrait for User {
|
||||
fn name(&self) -> L10n {
|
||||
L10n::t("package_name", &LOCALES_USER)
|
||||
}
|
||||
|
||||
fn description(&self) -> L10n {
|
||||
L10n::t("package_description", &LOCALES_USER)
|
||||
}
|
||||
|
||||
fn configure_service(&self, scfg: &mut service::web::ServiceConfig) {
|
||||
scfg.route("/user/login", service::web::get().to(login));
|
||||
}
|
||||
|
||||
fn migrations(&self) -> Vec<MigrationItem> {
|
||||
migrations![
|
||||
m20220312_000001_create_table_role,
|
||||
m20220312_000002_create_table_role_permission,
|
||||
m20220312_000003_create_table_user,
|
||||
m20220312_000004_create_table_user_role,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
async fn login(request: service::HttpRequest) -> ResultPage<Markup, ErrorPage> {
|
||||
Page::new(request)
|
||||
.with_title(L10n::n("Identificación del usuario"))
|
||||
.with_component_in(
|
||||
"content",
|
||||
Wrapper::new()
|
||||
.with_id("welcome")
|
||||
.add_component(form_login()),
|
||||
)
|
||||
.render()
|
||||
}
|
||||
|
||||
fn form_login() -> Form {
|
||||
Form::new()
|
||||
.with_id("user-login")
|
||||
.with_element(
|
||||
form::Input::textfield()
|
||||
.with_name("name")
|
||||
.with_label(L10n::t("username", &LOCALES_USER))
|
||||
.with_help_text(
|
||||
L10n::t("username_help", &LOCALES_USER)
|
||||
.with_arg("app", config::SETTINGS.app.name.to_owned()),
|
||||
)
|
||||
.with_autofocus(true),
|
||||
)
|
||||
.with_element(
|
||||
form::Input::password()
|
||||
.with_name("pass")
|
||||
.with_label(L10n::t("password", &LOCALES_USER))
|
||||
.with_help_text(L10n::t("password_help", &LOCALES_USER)),
|
||||
)
|
||||
.with_element(form::ActionButton::submit().with_value(L10n::t("login", &LOCALES_USER)))
|
||||
}
|
||||
8
packages/pagetop-user/src/locale/en-US/homepage.ftl
Normal file
8
packages/pagetop-user/src/locale/en-US/homepage.ftl
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package_name = User
|
||||
package_description = Manages the user registration and login system.
|
||||
|
||||
username = User name
|
||||
password = Password
|
||||
username_help = Enter your { $app } username.
|
||||
password_help = Enter the password that accompanies your username.
|
||||
login = Log in
|
||||
8
packages/pagetop-user/src/locale/es-ES/homepage.ftl
Normal file
8
packages/pagetop-user/src/locale/es-ES/homepage.ftl
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package_name = Usuario
|
||||
package_description = Gestiona el registro de usuarios y el sistema de accesos.
|
||||
|
||||
username = Nombre de usuario
|
||||
password = Contraseña
|
||||
username_help = Introduzca su nombre de usuario en { $app }.
|
||||
password_help = Introduzca la contraseña asociada a su nombre de usuario.
|
||||
login = Iniciar sesión
|
||||
4
packages/pagetop-user/src/migration.rs
Normal file
4
packages/pagetop-user/src/migration.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
pub mod m20220312_000001_create_table_role;
|
||||
pub mod m20220312_000002_create_table_role_permission;
|
||||
pub mod m20220312_000003_create_table_user;
|
||||
pub mod m20220312_000004_create_table_user_role;
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
use pagetop::prelude::*;
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(Iden)]
|
||||
enum Role {
|
||||
Table, // role: Store user roles.
|
||||
|
||||
Rid, // Primary Key: Unique role ID.
|
||||
Name, // Unique role name.
|
||||
Weight, // The weight of this role in listings and the user interface.
|
||||
}
|
||||
|
||||
new_migration!(Migration);
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(Role::Table)
|
||||
.if_not_exists()
|
||||
.col(
|
||||
ColumnDef::new(Role::Rid)
|
||||
.unsigned()
|
||||
.not_null()
|
||||
.auto_increment()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(Role::Name)
|
||||
.string_len(64)
|
||||
.not_null()
|
||||
.unique_key(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(Role::Weight)
|
||||
.integer()
|
||||
.not_null()
|
||||
.default(10),
|
||||
)
|
||||
// INDEXES.
|
||||
.index(
|
||||
Index::create()
|
||||
.name("weight-name")
|
||||
.col(Role::Weight)
|
||||
.col(Role::Name),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Built-in roles.
|
||||
db::exec::<InsertStatement>(
|
||||
Query::insert()
|
||||
.into_table(Role::Table)
|
||||
.columns(vec![Role::Name, Role::Weight])
|
||||
.values_panic(vec!["anonymous".into(), "1".into()])
|
||||
.values_panic(vec!["authenticated".into(), "2".into()])
|
||||
.values_panic(vec!["administrator".into(), "3".into()]),
|
||||
)
|
||||
.await
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_table(Table::drop().table(Role::Table).to_owned())
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
use pagetop::prelude::*;
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(Iden)]
|
||||
enum RolePermission {
|
||||
Table, // role_permission: Stores the permissions assigned to user roles.
|
||||
|
||||
Rid, // Foreign Key: Role::Rid.
|
||||
Permission, // A single permission granted to the role identified by Rid.
|
||||
}
|
||||
|
||||
#[derive(Iden)]
|
||||
enum Role {
|
||||
Table,
|
||||
Rid,
|
||||
/* ... */
|
||||
}
|
||||
|
||||
new_migration!(Migration);
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(RolePermission::Table)
|
||||
.if_not_exists()
|
||||
.col(ColumnDef::new(RolePermission::Rid).unsigned().not_null())
|
||||
.col(
|
||||
ColumnDef::new(RolePermission::Permission)
|
||||
.string_len(128)
|
||||
.not_null(),
|
||||
)
|
||||
// INDEXES.
|
||||
.primary_key(
|
||||
Index::create()
|
||||
.col(RolePermission::Rid)
|
||||
.col(RolePermission::Permission),
|
||||
)
|
||||
.index(
|
||||
Index::create()
|
||||
.name("permission")
|
||||
.col(RolePermission::Permission),
|
||||
)
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk_role_permission-rid")
|
||||
.from(RolePermission::Table, RolePermission::Rid)
|
||||
.to(Role::Table, Role::Rid)
|
||||
.on_delete(ForeignKeyAction::Restrict)
|
||||
.on_update(ForeignKeyAction::Restrict),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_table(Table::drop().table(RolePermission::Table).to_owned())
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
use pagetop::prelude::*;
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(Iden)]
|
||||
enum User {
|
||||
Table, // user: Stores user data.
|
||||
|
||||
Uid, // Primary Key: Unique user ID.
|
||||
Name, // Unique user name.
|
||||
Pass, // User's password (hashed).
|
||||
Mail, // User's e-mail address.
|
||||
Created, // Timestamp for when user was created.
|
||||
Changed, // Timestamp for when user was changed.
|
||||
Access, // Timestamp for previous time user accessed the site.
|
||||
Login, // Timestamp for user's last login.
|
||||
Status, // Whether the user is active(1) or blocked(0).
|
||||
Timezone, // User's time zone.
|
||||
}
|
||||
|
||||
new_migration!(Migration);
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(User::Table)
|
||||
.if_not_exists()
|
||||
.col(
|
||||
ColumnDef::new(User::Uid)
|
||||
.unsigned()
|
||||
.not_null()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(User::Name)
|
||||
.string_len(60)
|
||||
.not_null()
|
||||
.unique_key(),
|
||||
)
|
||||
.col(ColumnDef::new(User::Pass).string_len(128).not_null())
|
||||
.col(ColumnDef::new(User::Mail).string_len(255))
|
||||
.col(ColumnDef::new(User::Created).timestamp().not_null())
|
||||
.col(ColumnDef::new(User::Changed).timestamp().not_null())
|
||||
.col(ColumnDef::new(User::Access).timestamp().not_null())
|
||||
.col(ColumnDef::new(User::Login).timestamp().not_null())
|
||||
.col(ColumnDef::new(User::Status).boolean().not_null())
|
||||
.col(ColumnDef::new(User::Timezone).string_len(32))
|
||||
.to_owned(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_table(Table::drop().table(User::Table).to_owned())
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
use pagetop::prelude::*;
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(Iden)]
|
||||
enum UserRole {
|
||||
Table, // user_role: Maps users to roles.
|
||||
|
||||
Uid, // Foreign Key: User::Uid for user.
|
||||
Rid, // Foreign Key: Role::Rid for role.
|
||||
}
|
||||
|
||||
#[derive(Iden)]
|
||||
enum User {
|
||||
Table,
|
||||
Uid,
|
||||
/* ... */
|
||||
}
|
||||
|
||||
#[derive(Iden)]
|
||||
enum Role {
|
||||
Table,
|
||||
Rid,
|
||||
/* ... */
|
||||
}
|
||||
|
||||
new_migration!(Migration);
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(UserRole::Table)
|
||||
.if_not_exists()
|
||||
.col(ColumnDef::new(UserRole::Uid).unsigned().not_null())
|
||||
.col(ColumnDef::new(UserRole::Rid).unsigned().not_null())
|
||||
// INDEXES.
|
||||
.primary_key(Index::create().col(UserRole::Uid).col(UserRole::Rid))
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk_user_role-uid")
|
||||
.from(UserRole::Table, UserRole::Uid)
|
||||
.to(User::Table, User::Uid)
|
||||
.on_delete(ForeignKeyAction::Restrict)
|
||||
.on_update(ForeignKeyAction::Restrict),
|
||||
)
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk_user_role-rid")
|
||||
.from(UserRole::Table, UserRole::Rid)
|
||||
.to(Role::Table, Role::Rid)
|
||||
.on_delete(ForeignKeyAction::Restrict)
|
||||
.on_update(ForeignKeyAction::Restrict),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_table(Table::drop().table(UserRole::Table).to_owned())
|
||||
.await
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue