Actualiza formato del código aplicando cargo fmt

This commit is contained in:
Manuel Cillero 2022-07-20 00:01:01 +02:00
parent 4b5caf06a7
commit e6ea59785e
75 changed files with 1069 additions and 1160 deletions

View file

@ -22,16 +22,11 @@ impl ModuleTrait for Admin {
} }
fn configure_service(&self, cfg: &mut app::web::ServiceConfig) { fn configure_service(&self, cfg: &mut app::web::ServiceConfig) {
cfg.service( cfg.service(app::web::scope("/admin").route("", app::web::get().to(summary::summary)));
app::web::scope("/admin")
.route("", app::web::get().to(summary::summary))
);
} }
fn actions(&self) -> Vec<HookAction> { fn actions(&self) -> Vec<HookAction> {
vec![ vec![hook_action!(BeforeRenderPageHook => before_render_page)]
hook_action!(BeforeRenderPageHook => before_render_page)
]
} }
} }

View file

@ -1,19 +1,23 @@
use pagetop::prelude::*;
use super::l; use super::l;
use pagetop::prelude::*;
pub async fn summary() -> app::Result<Markup> { pub async fn summary() -> app::Result<Markup> {
let top_menu = Menu::new() let top_menu = Menu::new()
.with_item(MenuItem::label(l("module_name").as_str())) .with_item(MenuItem::label(l("module_name").as_str()))
.with_item(MenuItem::link("Opción 2", "https://www.google.es")) .with_item(MenuItem::link("Opción 2", "https://www.google.es"))
.with_item(MenuItem::link_blank("Opción 3", "https://www.google.es")) .with_item(MenuItem::link_blank("Opción 3", "https://www.google.es"))
.with_item(MenuItem::submenu("Submenú 1", Menu::new() .with_item(MenuItem::submenu(
.with_item(MenuItem::label("Opción 1")) "Submenú 1",
.with_item(MenuItem::label("Opción 2")) Menu::new()
.with_item(MenuItem::label("Opción 1"))
.with_item(MenuItem::label("Opción 2")),
)) ))
.with_item(MenuItem::separator()) .with_item(MenuItem::separator())
.with_item(MenuItem::submenu("Submenú 2", Menu::new() .with_item(MenuItem::submenu(
.with_item(MenuItem::label("Opción 1")) "Submenú 2",
.with_item(MenuItem::label("Opción 2")) Menu::new()
.with_item(MenuItem::label("Opción 1"))
.with_item(MenuItem::label("Opción 2")),
)) ))
.with_item(MenuItem::label("Opción 4")); .with_item(MenuItem::label("Opción 4"));
@ -21,38 +25,33 @@ pub async fn summary() -> app::Result<Markup> {
.with_item(MenuItem::label("Opción 1")) .with_item(MenuItem::label("Opción 1"))
.with_item(MenuItem::link("Opción 2", "https://www.google.es")) .with_item(MenuItem::link("Opción 2", "https://www.google.es"))
.with_item(MenuItem::link_blank("Opción 3", "https://www.google.es")) .with_item(MenuItem::link_blank("Opción 3", "https://www.google.es"))
.with_item(MenuItem::submenu("Submenú 1", Menu::new() .with_item(MenuItem::submenu(
.with_item(MenuItem::label("Opción 1")) "Submenú 1",
.with_item(MenuItem::label("Opción 2")) Menu::new()
.with_item(MenuItem::label("Opción 1"))
.with_item(MenuItem::label("Opción 2")),
)) ))
.with_item(MenuItem::separator()) .with_item(MenuItem::separator())
.with_item(MenuItem::submenu("Submenú 2", Menu::new() .with_item(MenuItem::submenu(
.with_item(MenuItem::label("Opción 1")) "Submenú 2",
.with_item(MenuItem::label("Opción 2")) Menu::new()
.with_item(MenuItem::label("Opción 1"))
.with_item(MenuItem::label("Opción 2")),
)) ))
.with_item(MenuItem::label("Opción 4")); .with_item(MenuItem::label("Opción 4"));
Page::new() Page::new()
.with_context(InContextOp::SetTheme("Bootsier")) .with_context(InContextOp::SetTheme("Bootsier"))
.with_title("Admin") .with_title("Admin")
.add_to("top-menu", top_menu) .add_to("top-menu", top_menu)
.add_to(
.add_to("content", grid::Row::new() "content",
.with_column(grid::Column::new() grid::Row::new()
.with_component(side_menu) .with_column(grid::Column::new().with_component(side_menu))
) .with_column(grid::Column::new().with_component(Chunck::with(html! {
.with_column(grid::Column::new()
.with_component(Chunck::with(html! {
p { "Columna 2"} p { "Columna 2"}
})) }))),
)
) )
.using_template("admin") .using_template("admin")
.render() .render()
} }

View file

@ -27,9 +27,7 @@ impl ModuleTrait for Node {
} }
fn actions(&self) -> Vec<HookAction> { fn actions(&self) -> Vec<HookAction> {
vec![ vec![hook_action!(BeforeRenderPageHook => before_render_page, -1)]
hook_action!(BeforeRenderPageHook => before_render_page, -1)
]
} }
fn migrations(&self) -> Vec<MigrationItem> { fn migrations(&self) -> Vec<MigrationItem> {
@ -43,11 +41,7 @@ impl ModuleTrait for Node {
} }
async fn node() -> app::Result<Markup> { async fn node() -> app::Result<Markup> {
Page::new() Page::new().with_title("Nodo").render()
.with_title(
"Nodo"
)
.render()
} }
fn before_render_page(page: &mut Page) { fn before_render_page(page: &mut Page) {

View file

@ -24,61 +24,35 @@ pub_migration!(Migration);
#[async_trait::async_trait] #[async_trait::async_trait]
impl MigrationTrait for Migration { impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.create_table(Table::create() manager
.table(NodeType::Table) .create_table(
.if_not_exists() Table::create()
.col(ColumnDef::new(NodeType::Type) .table(NodeType::Table)
.integer() .if_not_exists()
.not_null() .col(
.auto_increment() ColumnDef::new(NodeType::Type)
.primary_key(), .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(),
) )
.col(ColumnDef::new(NodeType::Name) .await
.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> { async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.drop_table(Table::drop() manager
.table(NodeType::Table) .drop_table(Table::drop().table(NodeType::Table).to_owned())
.to_owned() .await
)
.await
} }
} }

View file

@ -31,77 +31,39 @@ pub_migration!(Migration);
#[async_trait::async_trait] #[async_trait::async_trait]
impl MigrationTrait for Migration { impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.create_table(Table::create() manager
.table(Node::Table) .create_table(
.if_not_exists() Table::create()
.col(ColumnDef::new(Node::Nid) .table(Node::Table)
.integer() .if_not_exists()
.not_null() .col(
.auto_increment() ColumnDef::new(Node::Nid)
.primary_key(), .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(),
) )
.col(ColumnDef::new(Node::Vid) .await
.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> { async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.drop_table(Table::drop() manager
.table(Node::Table) .drop_table(Table::drop().table(Node::Table).to_owned())
.to_owned() .await
)
.await
} }
} }

View file

@ -23,45 +23,31 @@ pub_migration!(Migration);
#[async_trait::async_trait] #[async_trait::async_trait]
impl MigrationTrait for Migration { impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.create_table(Table::create() manager
.table(NodeAccess::Table) .create_table(
.if_not_exists() Table::create()
.col(ColumnDef::new(NodeAccess::Nid) .table(NodeAccess::Table)
.integer() .if_not_exists()
.not_null() .col(
.auto_increment() ColumnDef::new(NodeAccess::Nid)
.primary_key(), .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(),
) )
.col(ColumnDef::new(NodeAccess::Gid) .await
.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> { async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.drop_table(Table::drop() manager
.table(NodeAccess::Table) .drop_table(Table::drop().table(NodeAccess::Table).to_owned())
.to_owned() .await
)
.await
} }
} }

View file

@ -25,61 +25,35 @@ pub_migration!(Migration);
#[async_trait::async_trait] #[async_trait::async_trait]
impl MigrationTrait for Migration { impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.create_table(Table::create() manager
.table(NodeRevision::Table) .create_table(
.if_not_exists() Table::create()
.col(ColumnDef::new(NodeRevision::Nid) .table(NodeRevision::Table)
.integer() .if_not_exists()
.not_null() .col(
.auto_increment() ColumnDef::new(NodeRevision::Nid)
.primary_key(), .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(),
) )
.col(ColumnDef::new(NodeRevision::Vid) .await
.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> { async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.drop_table(Table::drop() manager
.table(NodeRevision::Table) .drop_table(Table::drop().table(NodeRevision::Table).to_owned())
.to_owned() .await
)
.await
} }
} }

View file

@ -37,12 +37,12 @@ impl ModuleTrait for User {
async fn login() -> app::Result<Markup> { async fn login() -> app::Result<Markup> {
Page::new() Page::new()
.with_title( .with_title("Identificación del usuario")
"Identificación del usuario" .add_to(
) "content",
.add_to("content", Container::new() Container::new()
.with_id("welcome") .with_id("welcome")
.with_component(form_login()) .with_component(form_login()),
) )
.render() .render()
} }
@ -50,18 +50,26 @@ async fn login() -> app::Result<Markup> {
fn form_login() -> Form { fn form_login() -> Form {
Form::new() Form::new()
.with_id("user-login") .with_id("user-login")
.with_element(form::Input::textfield() .with_element(
.with_name("name") form::Input::textfield()
.with_label(l("username").as_str()) .with_name("name")
.with_help_text(t("username_help", &args![ .with_label(l("username").as_str())
"app" => SETTINGS.app.name.to_owned() .with_help_text(
]).as_str()) t(
.with_autofocus(true) "username_help",
&args![
"app" => SETTINGS.app.name.to_owned()
],
)
.as_str(),
)
.with_autofocus(true),
) )
.with_element(form::Input::password() .with_element(
.with_name("pass") form::Input::password()
.with_label(l("password").as_str()) .with_name("pass")
.with_help_text(l("password_help").as_str()) .with_label(l("password").as_str())
.with_help_text(l("password_help").as_str()),
) )
.with_element(form::Button::submit(l("login").as_str())) .with_element(form::Button::submit(l("login").as_str()))
} }

View file

@ -14,51 +14,57 @@ pub_migration!(Migration);
#[async_trait::async_trait] #[async_trait::async_trait]
impl MigrationTrait for Migration { impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.create_table(Table::create() manager
.table(Role::Table) .create_table(
.if_not_exists() Table::create()
.col(ColumnDef::new(Role::Rid) .table(Role::Table)
.unsigned() .if_not_exists()
.not_null() .col(
.auto_increment() ColumnDef::new(Role::Rid)
.primary_key() .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(),
) )
.col(ColumnDef::new(Role::Name) .await?;
.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. // Built-in roles.
app::db::exec::<InsertStatement>(Query::insert() app::db::exec::<InsertStatement>(
.into_table(Role::Table) Query::insert()
.columns(vec![Role::Name, Role::Weight]) .into_table(Role::Table)
.values_panic(vec!["anonymous".into(), "1".into()]) .columns(vec![Role::Name, Role::Weight])
.values_panic(vec!["authenticated".into(), "2".into()]) .values_panic(vec!["anonymous".into(), "1".into()])
.values_panic(vec!["administrator".into(), "3".into()]) .values_panic(vec!["authenticated".into(), "2".into()])
.values_panic(vec!["administrator".into(), "3".into()]),
) )
.await.map(|_| ()) .await
.map(|_| ())
} }
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.drop_table(Table::drop() manager
.table(Role::Table) .drop_table(Table::drop().table(Role::Table).to_owned())
.to_owned() .await
)
.await
} }
} }

View file

@ -9,50 +9,55 @@ enum RolePermission {
} }
#[derive(Iden)] #[derive(Iden)]
enum Role { Table, Rid, /* ... */ } enum Role {
Table,
Rid,
/* ... */
}
pub_migration!(Migration); pub_migration!(Migration);
#[async_trait::async_trait] #[async_trait::async_trait]
impl MigrationTrait for Migration { impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.create_table(Table::create() manager
.table(RolePermission::Table) .create_table(
.if_not_exists() Table::create()
.col(ColumnDef::new(RolePermission::Rid) .table(RolePermission::Table)
.unsigned() .if_not_exists()
.not_null() .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(),
) )
.col(ColumnDef::new(RolePermission::Permission) .await
.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> { async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.drop_table(Table::drop() manager
.table(RolePermission::Table) .drop_table(Table::drop().table(RolePermission::Table).to_owned())
.to_owned() .await
)
.await
} }
} }

View file

@ -21,59 +21,39 @@ pub_migration!(Migration);
#[async_trait::async_trait] #[async_trait::async_trait]
impl MigrationTrait for Migration { impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.create_table(Table::create() manager
.table(User::Table) .create_table(
.if_not_exists() Table::create()
.col(ColumnDef::new(User::Uid) .table(User::Table)
.unsigned() .if_not_exists()
.not_null() .col(
.primary_key() 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(),
) )
.col(ColumnDef::new(User::Name) .await
.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> { async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.drop_table(Table::drop() manager
.table(User::Table) .drop_table(Table::drop().table(User::Table).to_owned())
.to_owned() .await
)
.await
} }
} }

View file

@ -9,56 +9,57 @@ enum UserRole {
} }
#[derive(Iden)] #[derive(Iden)]
enum User { Table, Uid, /* ... */ } enum User {
Table,
Uid,
/* ... */
}
#[derive(Iden)] #[derive(Iden)]
enum Role { Table, Rid, /* ... */ } enum Role {
Table,
Rid,
/* ... */
}
pub_migration!(Migration); pub_migration!(Migration);
#[async_trait::async_trait] #[async_trait::async_trait]
impl MigrationTrait for Migration { impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.create_table(Table::create() manager
.table(UserRole::Table) .create_table(
.if_not_exists() Table::create()
.col(ColumnDef::new(UserRole::Uid) .table(UserRole::Table)
.unsigned() .if_not_exists()
.not_null() .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(),
) )
.col(ColumnDef::new(UserRole::Rid) .await
.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> { async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.drop_table(Table::drop() manager
.table(UserRole::Table) .drop_table(Table::drop().table(UserRole::Table).to_owned())
.to_owned() .await
)
.await
} }
} }

View file

@ -13,7 +13,7 @@ fn main() -> std::io::Result<()> {
fn build_resource_dir(dir: &str, name: &str) -> std::io::Result<()> { fn build_resource_dir(dir: &str, name: &str) -> std::io::Result<()> {
let mut resource = resource_dir(dir); let mut resource = resource_dir(dir);
resource.with_generated_filename( resource.with_generated_filename(
Path::new(env::var("OUT_DIR").unwrap().as_str()).join(format!("{}.rs", name)) Path::new(env::var("OUT_DIR").unwrap().as_str()).join(format!("{}.rs", name)),
); );
resource.with_module_name(format!("resources_{}", name)); resource.with_module_name(format!("resources_{}", name));
resource.build() resource.build()

View file

@ -1,6 +1,4 @@
pub use actix_web::{ pub use actix_web::{http, web, App, HttpRequest, HttpResponse, HttpServer, Responder, Result};
App, HttpRequest, HttpResponse, HttpServer, Responder, Result, http, web
};
mod banner; mod banner;

View file

@ -1,10 +1,10 @@
use crate::{Lazy, base, trace}; use super::AppTrait;
use crate::config::SETTINGS; use crate::config::SETTINGS;
use crate::core::{module, theme}; use crate::core::{module, theme};
use super::AppTrait; use crate::{base, trace, Lazy};
use std::io::Error;
use actix_web::dev::Server; use actix_web::dev::Server;
use std::io::Error;
pub struct Application { pub struct Application {
server: Server, server: Server,
@ -26,9 +26,7 @@ impl Application {
Lazy::force(&super::db::DBCONN); Lazy::force(&super::db::DBCONN);
// Habilita los módulos predeterminados. // Habilita los módulos predeterminados.
module::all::enable_modules(vec![ module::all::enable_modules(vec![&base::module::homepage::DefaultHomePage]);
&base::module::homepage::DefaultHomePage,
]);
// Habilita los módulos de la aplicación. // Habilita los módulos de la aplicación.
module::all::enable_modules(app.enable_modules()); module::all::enable_modules(app.enable_modules());
@ -59,12 +57,12 @@ impl Application {
.wrap(tracing_actix_web::TracingLogger::default()) .wrap(tracing_actix_web::TracingLogger::default())
.configure(&module::all::modules) .configure(&module::all::modules)
.configure(&theme::all::themes) .configure(&theme::all::themes)
}) })
.bind(format!("{}:{}", .bind(format!(
&SETTINGS.webserver.bind_address, "{}:{}",
&SETTINGS.webserver.bind_port &SETTINGS.webserver.bind_address, &SETTINGS.webserver.bind_port
))? ))?
.run(); .run();
Ok(Self { server }) Ok(Self { server })
} }

View file

@ -14,7 +14,8 @@ pub fn print_on_startup() {
if SETTINGS.app.name.len() > maxlen { if SETTINGS.app.name.len() > maxlen {
app = format!("{}...", app); app = format!("{}...", app);
} }
println!("\n{} {}\n\n Powered by PageTop {}\n", println!(
"\n{} {}\n\n Powered by PageTop {}\n",
FIGFONT.convert(&app).unwrap(), FIGFONT.convert(&app).unwrap(),
&SETTINGS.app.description, &SETTINGS.app.description,
env!("CARGO_PKG_VERSION") env!("CARGO_PKG_VERSION")
@ -22,7 +23,8 @@ pub fn print_on_startup() {
return; return;
} }
} }
println!("\n{}\n{}\n\nPowered by PageTop {}\n", println!(
"\n{}\n{}\n\nPowered by PageTop {}\n",
&SETTINGS.app.name, &SETTINGS.app.name,
&SETTINGS.app.description, &SETTINGS.app.description,
env!("CARGO_PKG_VERSION") env!("CARGO_PKG_VERSION")

View file

@ -1,30 +1,27 @@
use crate::Lazy;
use crate::config::SETTINGS; use crate::config::SETTINGS;
use crate::Lazy;
use figlet_rs::FIGfont; use figlet_rs::FIGfont;
pub static FIGFONT: Lazy<FIGfont> = Lazy::new(|| { pub static FIGFONT: Lazy<FIGfont> = Lazy::new(|| {
let slant = include_str!("slant.flf"); let slant = include_str!("slant.flf");
let small = include_str!("small.flf"); let small = include_str!("small.flf");
let speed = include_str!("speed.flf"); let speed = include_str!("speed.flf");
let starwars = include_str!("starwars.flf"); let starwars = include_str!("starwars.flf");
FIGfont::from_content( FIGfont::from_content(match SETTINGS.app.startup_banner.to_lowercase().as_str() {
match SETTINGS.app.startup_banner.to_lowercase().as_str() { "off" => slant,
"off" => slant, "slant" => slant,
"slant" => slant, "small" => small,
"small" => small, "speed" => speed,
"speed" => speed, "starwars" => starwars,
"starwars" => starwars, _ => {
_ => { println!(
println!( "\n FIGfont \"{}\" not found for banner. {}. {}.",
"\n FIGfont \"{}\" not found for banner. {}. {}.", SETTINGS.app.startup_banner, "Using \"Slant\"", "Check the settings file",
SETTINGS.app.startup_banner, );
"Using \"Slant\"", slant
"Check the settings file",
);
slant
}
} }
).unwrap() })
.unwrap()
}); });

View file

@ -1,8 +1,8 @@
use crate::{Lazy, run_now, trace};
use crate::config::SETTINGS; use crate::config::SETTINGS;
use crate::db::*; use crate::db::*;
use crate::{run_now, trace, Lazy};
use sea_orm::{ConnectionTrait, ConnectOptions, Database, DatabaseBackend, Statement}; use sea_orm::{ConnectOptions, ConnectionTrait, Database, DatabaseBackend, Statement};
use tracing_unwrap::ResultExt; use tracing_unwrap::ResultExt;
pub static DBCONN: Lazy<DbConn> = Lazy::new(|| { pub static DBCONN: Lazy<DbConn> = Lazy::new(|| {
@ -14,31 +14,36 @@ pub static DBCONN: Lazy<DbConn> = Lazy::new(|| {
let db_uri = match SETTINGS.database.db_type.as_str() { let db_uri = match SETTINGS.database.db_type.as_str() {
"mysql" | "postgres" => { "mysql" | "postgres" => {
let mut tmp_uri = DbUri::parse(format!( let mut tmp_uri = DbUri::parse(
"{}://{}/{}", format!(
&SETTINGS.database.db_type, "{}://{}/{}",
&SETTINGS.database.db_host, &SETTINGS.database.db_type,
&SETTINGS.database.db_name &SETTINGS.database.db_host,
).as_str()).unwrap(); &SETTINGS.database.db_name
tmp_uri.set_username( )
&SETTINGS.database.db_user.as_str() .as_str(),
).unwrap(); )
.unwrap();
tmp_uri
.set_username(&SETTINGS.database.db_user.as_str())
.unwrap();
// https://github.com/launchbadge/sqlx/issues/1624 // https://github.com/launchbadge/sqlx/issues/1624
tmp_uri.set_password( tmp_uri
Some(&SETTINGS.database.db_pass.as_str()) .set_password(Some(&SETTINGS.database.db_pass.as_str()))
).unwrap(); .unwrap();
if SETTINGS.database.db_port != 0 { if SETTINGS.database.db_port != 0 {
tmp_uri.set_port( tmp_uri.set_port(Some(SETTINGS.database.db_port)).unwrap();
Some(SETTINGS.database.db_port)
).unwrap();
} }
tmp_uri tmp_uri
}, }
"sqlite" => DbUri::parse( "sqlite" => DbUri::parse(
format!("{}://{}", format!(
&SETTINGS.database.db_type, "{}://{}",
&SETTINGS.database.db_name &SETTINGS.database.db_type, &SETTINGS.database.db_name
).as_str()).unwrap(), )
.as_str(),
)
.unwrap(),
_ => { _ => {
trace::error!( trace::error!(
"Unrecognized database type \"{}\"", "Unrecognized database type \"{}\"",
@ -48,42 +53,44 @@ pub static DBCONN: Lazy<DbConn> = Lazy::new(|| {
} }
}; };
run_now( run_now(Database::connect::<ConnectOptions>({
Database::connect::<ConnectOptions>({ let mut db_opt = ConnectOptions::new(db_uri.to_string());
let mut db_opt = ConnectOptions::new(db_uri.to_string()); db_opt.max_connections(SETTINGS.database.max_pool_size);
db_opt.max_connections(SETTINGS.database.max_pool_size); db_opt.into()
db_opt.into() }))
}) .expect_or_log("Failed to connect to database")
).expect_or_log("Failed to connect to database")
});
static DBBACKEND: Lazy<DatabaseBackend> = Lazy::new(|| {
DBCONN.get_database_backend()
}); });
static DBBACKEND: Lazy<DatabaseBackend> = Lazy::new(|| DBCONN.get_database_backend());
pub async fn query<Q: QueryStatementWriter>(stmt: &mut Q) -> Result<Vec<QueryResult>, DbErr> { pub async fn query<Q: QueryStatementWriter>(stmt: &mut Q) -> Result<Vec<QueryResult>, DbErr> {
DBCONN.query_all(Statement::from_string( DBCONN
*DBBACKEND, .query_all(Statement::from_string(
match *DBBACKEND { *DBBACKEND,
DatabaseBackend::MySql => stmt.to_string(MysqlQueryBuilder), match *DBBACKEND {
DatabaseBackend::Postgres => stmt.to_string(PostgresQueryBuilder), DatabaseBackend::MySql => stmt.to_string(MysqlQueryBuilder),
DatabaseBackend::Sqlite => stmt.to_string(SqliteQueryBuilder), DatabaseBackend::Postgres => stmt.to_string(PostgresQueryBuilder),
} DatabaseBackend::Sqlite => stmt.to_string(SqliteQueryBuilder),
)).await },
))
.await
} }
pub async fn exec<Q: QueryStatementWriter>(stmt: &mut Q) -> Result<Option<QueryResult>, DbErr> { pub async fn exec<Q: QueryStatementWriter>(stmt: &mut Q) -> Result<Option<QueryResult>, DbErr> {
DBCONN.query_one(Statement::from_string( DBCONN
*DBBACKEND, .query_one(Statement::from_string(
match *DBBACKEND { *DBBACKEND,
DatabaseBackend::MySql => stmt.to_string(MysqlQueryBuilder), match *DBBACKEND {
DatabaseBackend::Postgres => stmt.to_string(PostgresQueryBuilder), DatabaseBackend::MySql => stmt.to_string(MysqlQueryBuilder),
DatabaseBackend::Sqlite => stmt.to_string(SqliteQueryBuilder), DatabaseBackend::Postgres => stmt.to_string(PostgresQueryBuilder),
} DatabaseBackend::Sqlite => stmt.to_string(SqliteQueryBuilder),
)).await },
))
.await
} }
pub async fn exec_raw(stmt: String) -> Result<ExecResult, DbErr> { pub async fn exec_raw(stmt: String) -> Result<ExecResult, DbErr> {
DBCONN.execute(Statement::from_string(*DBBACKEND, stmt)).await DBCONN
.execute(Statement::from_string(*DBBACKEND, stmt))
.await
} }

View file

@ -2,8 +2,7 @@ use crate::core::module::ModuleTrait;
use crate::core::theme::ThemeTrait; use crate::core::theme::ThemeTrait;
pub trait AppTrait: Send + Sync { pub trait AppTrait: Send + Sync {
fn bootstrap(&self) { fn bootstrap(&self) {}
}
fn enable_modules(&self) -> Vec<&'static dyn ModuleTrait> { fn enable_modules(&self) -> Vec<&'static dyn ModuleTrait> {
vec![] vec![]

View file

@ -1,24 +1,22 @@
use crate::{Lazy, trace};
use crate::config::SETTINGS; use crate::config::SETTINGS;
use crate::{trace, Lazy};
use unic_langid::LanguageIdentifier; use unic_langid::LanguageIdentifier;
/// Almacena el Identificador de Idioma Unicode ([Unicode Language Identifier] /// Almacena el Identificador de Idioma Unicode ([Unicode Language Identifier]
/// (https://unicode.org/reports/tr35/tr35.html#Unicode_language_identifier)) de /// (https://unicode.org/reports/tr35/tr35.html#Unicode_language_identifier)) de
/// la aplicación, obtenido de `SETTINGS.app.language`. /// la aplicación, obtenido de `SETTINGS.app.language`.
pub static LANGID: Lazy<LanguageIdentifier> = Lazy::new(|| { pub static LANGID: Lazy<LanguageIdentifier> = Lazy::new(|| match SETTINGS.app.language.parse() {
match SETTINGS.app.language.parse() { Ok(language) => language,
Ok(language) => language, Err(_) => {
Err(_) => { trace::warn!(
trace::warn!( "{}, {} \"{}\"! {}, {}",
"{}, {} \"{}\"! {}, {}", "Failed to parse language",
"Failed to parse language", "unrecognized Unicode Language Identifier",
"unrecognized Unicode Language Identifier", SETTINGS.app.language,
SETTINGS.app.language, "Using \"en-US\"",
"Using \"en-US\"", "check the settings file",
"check the settings file", );
); "en-US".parse().unwrap()
"en-US".parse().unwrap()
}
} }
}); });

View file

@ -1,5 +1,5 @@
use crate::Lazy;
use crate::config::SETTINGS; use crate::config::SETTINGS;
use crate::Lazy;
use tracing_appender::non_blocking::WorkerGuard; use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::EnvFilter; use tracing_subscriber::EnvFilter;
@ -18,14 +18,11 @@ use tracing_subscriber::EnvFilter;
/// enviarán antes de terminar la ejecución. /// enviarán antes de terminar la ejecución.
pub static TRACING: Lazy<WorkerGuard> = Lazy::new(|| { pub static TRACING: Lazy<WorkerGuard> = Lazy::new(|| {
let env_filter = EnvFilter::try_new(&SETTINGS.log.tracing) let env_filter = EnvFilter::try_new(&SETTINGS.log.tracing).unwrap_or(EnvFilter::new("Info"));
.unwrap_or(EnvFilter::new("Info"));
let rolling = SETTINGS.log.rolling.to_lowercase(); let rolling = SETTINGS.log.rolling.to_lowercase();
let (non_blocking, guard) = match rolling.as_str() { let (non_blocking, guard) = match rolling.as_str() {
"stdout" => tracing_appender::non_blocking( "stdout" => tracing_appender::non_blocking(std::io::stdout()),
std::io::stdout()
),
_ => tracing_appender::non_blocking({ _ => tracing_appender::non_blocking({
let path = &SETTINGS.log.path; let path = &SETTINGS.log.path;
let prefix = &SETTINGS.log.prefix; let prefix = &SETTINGS.log.prefix;
@ -37,14 +34,12 @@ pub static TRACING: Lazy<WorkerGuard> = Lazy::new(|| {
_ => { _ => {
println!( println!(
"Rolling value \"{}\" not valid. {}. {}.", "Rolling value \"{}\" not valid. {}. {}.",
SETTINGS.log.rolling, SETTINGS.log.rolling, "Using \"daily\"", "Check the settings file",
"Using \"daily\"",
"Check the settings file",
); );
tracing_appender::rolling::daily(path, prefix) tracing_appender::rolling::daily(path, prefix)
} }
} }
}) }),
}; };
let subscriber = tracing_subscriber::fmt() let subscriber = tracing_subscriber::fmt()
.with_env_filter(env_filter) .with_env_filter(env_filter)
@ -58,9 +53,7 @@ pub static TRACING: Lazy<WorkerGuard> = Lazy::new(|| {
_ => { _ => {
println!( println!(
"Tracing format \"{}\" not valid. {}. {}.", "Tracing format \"{}\" not valid. {}. {}.",
SETTINGS.log.format, SETTINGS.log.format, "Using \"Full\"", "Check the settings file",
"Using \"Full\"",
"Check the settings file",
); );
subscriber.init(); subscriber.init();
} }

View file

@ -1,44 +1,24 @@
mod container; mod container;
pub use container::{ pub use container::{Container, ContainerType, COMPONENT_CONTAINER};
COMPONENT_CONTAINER, Container, ContainerType
};
pub mod grid; pub mod grid;
mod chunck; mod chunck;
pub use chunck::{ pub use chunck::{Chunck, COMPONENT_CHUNCK};
COMPONENT_CHUNCK, Chunck
};
mod icon; mod icon;
pub use icon::{ pub use icon::{Icon, COMPONENT_ICON};
COMPONENT_ICON, Icon
};
mod heading; mod heading;
pub use heading::{ pub use heading::{Heading, HeadingDisplay, HeadingType, COMPONENT_HEADING};
COMPONENT_HEADING, Heading, HeadingDisplay, HeadingType
};
mod paragraph; mod paragraph;
pub use paragraph::{ pub use paragraph::{Paragraph, ParagraphDisplay, COMPONENT_PARAGRAPH};
COMPONENT_PARAGRAPH, Paragraph, ParagraphDisplay
};
mod anchor; mod anchor;
pub use anchor::{ pub use anchor::{Anchor, AnchorIcon, AnchorTarget, AnchorType, COMPONENT_ANCHOR};
COMPONENT_ANCHOR, Anchor, AnchorIcon, AnchorTarget, AnchorType
};
mod block; mod block;
pub use block::{ pub use block::{Block, COMPONENT_BLOCK};
COMPONENT_BLOCK, Block
};
mod image; mod image;
pub use image::{ pub use image::{Image, COMPONENT_IMAGE};
COMPONENT_IMAGE, Image
};
mod menu; mod menu;
pub use menu::{ pub use menu::{Menu, MenuItem, MenuItemType, COMPONENT_MENU, COMPONENT_MENUITEM};
COMPONENT_MENU, COMPONENT_MENUITEM, Menu, MenuItem, MenuItemType
};
pub mod form; pub mod form;
pub use form::{ pub use form::{Form, FormMethod, COMPONENT_FORM};
COMPONENT_FORM, Form, FormMethod
};

View file

@ -98,7 +98,10 @@ impl Anchor {
} }
pub fn button(href: &str, html: Markup) -> Self { pub fn button(href: &str, html: Markup) -> Self {
Anchor::new().with_type(AnchorType::Button).with_href(href).with_html(html) Anchor::new()
.with_type(AnchorType::Button)
.with_href(href)
.with_html(html)
} }
pub fn location(id: &str) -> Self { pub fn location(id: &str) -> Self {
@ -186,10 +189,13 @@ impl Anchor {
pub fn alter_type(&mut self, anchor_type: AnchorType) -> &mut Self { pub fn alter_type(&mut self, anchor_type: AnchorType) -> &mut Self {
self.anchor_type = anchor_type; self.anchor_type = anchor_type;
self.classes.alter(ClassesOp::SetDefault, match self.anchor_type { self.classes.alter(
AnchorType::Button => "btn btn-primary", ClassesOp::SetDefault,
_ => "", match self.anchor_type {
}); AnchorType::Button => "btn btn-primary",
_ => "",
},
);
self self
} }

View file

@ -71,7 +71,6 @@ impl ComponentTrait for Block {
} }
impl Block { impl Block {
// Block BUILDER. // Block BUILDER.
pub fn with_weight(mut self, weight: isize) -> Self { pub fn with_weight(mut self, weight: isize) -> Self {

View file

@ -7,7 +7,13 @@ hook_before_render_component!(
Container Container
); );
pub enum ContainerType { Header, Footer, Main, Section, Wrapper } pub enum ContainerType {
Header,
Footer,
Main,
Section,
Wrapper,
}
pub struct Container { pub struct Container {
weight : isize, weight : isize,
@ -84,7 +90,7 @@ impl ComponentTrait for Container {
div id=[self.id().get()] class=[self.classes().get()] { div id=[self.id().get()] class=[self.classes().get()] {
(self.components().render(context)) (self.components().render(context))
} }
} },
} }
} }

View file

@ -1,21 +1,11 @@
mod form; mod form;
pub use form::{ pub use form::{Form, FormMethod, COMPONENT_FORM};
COMPONENT_FORM, Form, FormMethod
};
mod input; mod input;
pub use input::{ pub use input::{Input, InputType, COMPONENT_INPUT};
COMPONENT_INPUT, Input, InputType
};
mod hidden; mod hidden;
pub use hidden::{ pub use hidden::{Hidden, COMPONENT_HIDDEN};
COMPONENT_HIDDEN, Hidden
};
mod date; mod date;
pub use date::{ pub use date::{Date, COMPONENT_DATE};
COMPONENT_DATE, Date
};
mod button; mod button;
pub use button::{ pub use button::{Button, ButtonType, COMPONENT_BUTTON};
COMPONENT_BUTTON, Button, ButtonType
};

View file

@ -2,7 +2,11 @@ use crate::prelude::*;
pub const COMPONENT_BUTTON: &str = "pagetop::component::form::button"; pub const COMPONENT_BUTTON: &str = "pagetop::component::form::button";
pub enum ButtonType {Button, Reset, Submit} pub enum ButtonType {
Button,
Reset,
Submit,
}
pub struct Button { pub struct Button {
weight : isize, weight : isize,
@ -52,7 +56,7 @@ impl ComponentTrait for Button {
}; };
let id = match self.name().get() { let id = match self.name().get() {
Some(name) => Some(concat_string!("edit-", name)), Some(name) => Some(concat_string!("edit-", name)),
_ => None _ => None,
}; };
html! { html! {
button button

View file

@ -101,7 +101,6 @@ impl ComponentTrait for Date {
} }
impl Date { impl Date {
// Date BUILDER. // Date BUILDER.
pub fn with_weight(mut self, weight: isize) -> Self { pub fn with_weight(mut self, weight: isize) -> Self {

View file

@ -7,7 +7,10 @@ hook_before_render_component!(
Form Form
); );
pub enum FormMethod {Get, Post} pub enum FormMethod {
Get,
Post,
}
pub struct Form { pub struct Form {
weight : isize, weight : isize,
@ -55,7 +58,7 @@ impl ComponentTrait for Form {
fn default_render(&self, context: &mut InContext) -> Markup { fn default_render(&self, context: &mut InContext) -> Markup {
let method = match self.method() { let method = match self.method() {
FormMethod::Get => None, FormMethod::Get => None,
FormMethod::Post => Some("post".to_owned()) FormMethod::Post => Some("post".to_owned()),
}; };
html! { html! {
form form
@ -80,7 +83,6 @@ impl ComponentTrait for Form {
} }
impl Form { impl Form {
// Form BUILDER. // Form BUILDER.
pub fn with_weight(mut self, weight: isize) -> Self { pub fn with_weight(mut self, weight: isize) -> Self {

View file

@ -28,7 +28,7 @@ impl ComponentTrait for Hidden {
fn default_render(&self, _: &mut InContext) -> Markup { fn default_render(&self, _: &mut InContext) -> Markup {
let id = match self.name().get() { let id = match self.name().get() {
Some(name) => Some(concat_string!("value-", name)), Some(name) => Some(concat_string!("value-", name)),
_ => None _ => None,
}; };
html! { html! {
input type="hidden" id=[id] name=[self.name().get()] value=[self.value().get()]; input type="hidden" id=[id] name=[self.name().get()] value=[self.value().get()];

View file

@ -2,7 +2,14 @@ use crate::prelude::*;
pub const COMPONENT_INPUT: &str = "pagetop::component::form::input"; pub const COMPONENT_INPUT: &str = "pagetop::component::form::input";
pub enum InputType {Email, Password, Search, Telephone, Textfield, Url} pub enum InputType {
Email,
Password,
Search,
Telephone,
Textfield,
Url,
}
pub struct Input { pub struct Input {
weight : isize, weight : isize,
@ -127,36 +134,46 @@ impl Input {
} }
pub fn password() -> Self { pub fn password() -> Self {
let mut input = Input::new() let mut input = Input::new().with_classes(
.with_classes(ClassesOp::Replace("form-type-textfield"), "form-type-password"); ClassesOp::Replace("form-type-textfield"),
"form-type-password",
);
input.input_type = InputType::Password; input.input_type = InputType::Password;
input input
} }
pub fn search() -> Self { pub fn search() -> Self {
let mut input = Input::new() let mut input = Input::new().with_classes(
.with_classes(ClassesOp::Replace("form-type-textfield"), "form-type-search"); ClassesOp::Replace("form-type-textfield"),
"form-type-search",
);
input.input_type = InputType::Search; input.input_type = InputType::Search;
input input
} }
pub fn email() -> Self { pub fn email() -> Self {
let mut input = Input::new() let mut input = Input::new().with_classes(
.with_classes(ClassesOp::Replace("form-type-textfield"), "form-type-email"); ClassesOp::Replace("form-type-textfield"),
"form-type-email"
);
input.input_type = InputType::Email; input.input_type = InputType::Email;
input input
} }
pub fn telephone() -> Self { pub fn telephone() -> Self {
let mut input = Input::new() let mut input = Input::new().with_classes(
.with_classes(ClassesOp::Replace("form-type-textfield"), "form-type-telephone"); ClassesOp::Replace("form-type-textfield"),
"form-type-telephone",
);
input.input_type = InputType::Telephone; input.input_type = InputType::Telephone;
input input
} }
pub fn url() -> Self { pub fn url() -> Self {
let mut input = Input::new() let mut input = Input::new().with_classes(
.with_classes(ClassesOp::Replace("form-type-textfield"), "form-type-url"); ClassesOp::Replace("form-type-textfield"),
"form-type-url"
);
input.input_type = InputType::Url; input.input_type = InputType::Url;
input input
} }
@ -267,8 +284,9 @@ impl Input {
pub fn alter_name(&mut self, name: &str) -> &mut Self { pub fn alter_name(&mut self, name: &str) -> &mut Self {
self.name.with_value(name); self.name.with_value(name);
self.alter_classes(ClassesOp::SetDefault, self.alter_classes(
concat_string!("form-item form-item-", name).as_str() ClassesOp::SetDefault,
concat_string!("form-item form-item-", name).as_str(),
); );
self self
} }

View file

@ -1,8 +1,4 @@
mod row; mod row;
pub use row::{ pub use row::{Row, COMPONENT_ROW};
COMPONENT_ROW, Row
};
mod column; mod column;
pub use column::{ pub use column::{Column, ColumnSize, COMPONENT_COLUMN};
COMPONENT_COLUMN, Column, ColumnSize
};

View file

@ -93,7 +93,6 @@ impl ComponentTrait for Column {
} }
impl Column { impl Column {
// Column BUILDER. // Column BUILDER.
pub fn with_weight(mut self, weight: isize) -> Self { pub fn with_weight(mut self, weight: isize) -> Self {

View file

@ -62,7 +62,6 @@ impl ComponentTrait for Row {
} }
impl Row { impl Row {
// Row BUILDER. // Row BUILDER.
pub fn with_weight(mut self, weight: isize) -> Self { pub fn with_weight(mut self, weight: isize) -> Self {

View file

@ -2,7 +2,14 @@ use crate::prelude::*;
pub const COMPONENT_HEADING: &str = "pagetop::component::heading"; pub const COMPONENT_HEADING: &str = "pagetop::component::heading";
pub enum HeadingType { H1, H2, H3, H4, H5, H6 } pub enum HeadingType {
H1,
H2,
H3,
H4,
H5,
H6,
}
pub enum HeadingDisplay { pub enum HeadingDisplay {
XxLarge, XxLarge,
@ -75,27 +82,39 @@ impl ComponentTrait for Heading {
impl Heading { impl Heading {
pub fn h1(html: Markup) -> Self { pub fn h1(html: Markup) -> Self {
Heading::new().with_heading_type(HeadingType::H1).with_html(html) Heading::new()
.with_heading_type(HeadingType::H1)
.with_html(html)
} }
pub fn h2(html: Markup) -> Self { pub fn h2(html: Markup) -> Self {
Heading::new().with_heading_type(HeadingType::H2).with_html(html) Heading::new()
.with_heading_type(HeadingType::H2)
.with_html(html)
} }
pub fn h3(html: Markup) -> Self { pub fn h3(html: Markup) -> Self {
Heading::new().with_heading_type(HeadingType::H3).with_html(html) Heading::new()
.with_heading_type(HeadingType::H3)
.with_html(html)
} }
pub fn h4(html: Markup) -> Self { pub fn h4(html: Markup) -> Self {
Heading::new().with_heading_type(HeadingType::H4).with_html(html) Heading::new()
.with_heading_type(HeadingType::H4)
.with_html(html)
} }
pub fn h5(html: Markup) -> Self { pub fn h5(html: Markup) -> Self {
Heading::new().with_heading_type(HeadingType::H5).with_html(html) Heading::new()
.with_heading_type(HeadingType::H5)
.with_html(html)
} }
pub fn h6(html: Markup) -> Self { pub fn h6(html: Markup) -> Self {
Heading::new().with_heading_type(HeadingType::H6).with_html(html) Heading::new()
.with_heading_type(HeadingType::H6)
.with_html(html)
} }
// Heading BUILDER. // Heading BUILDER.
@ -174,15 +193,18 @@ impl Heading {
pub fn alter_display(&mut self, display: HeadingDisplay) -> &mut Self { pub fn alter_display(&mut self, display: HeadingDisplay) -> &mut Self {
self.display = display; self.display = display;
self.classes.alter(ClassesOp::SetDefault, match &self.display() { self.classes.alter(
HeadingDisplay::XxLarge => "display-2", ClassesOp::SetDefault,
HeadingDisplay::Large => "display-3", match &self.display() {
HeadingDisplay::Medium => "display-4", HeadingDisplay::XxLarge => "display-2",
HeadingDisplay::Small => "display-5", HeadingDisplay::Large => "display-3",
HeadingDisplay::XxSmall => "display-6", HeadingDisplay::Medium => "display-4",
HeadingDisplay::Normal => "", HeadingDisplay::Small => "display-5",
HeadingDisplay::Subtitle => "", HeadingDisplay::XxSmall => "display-6",
}); HeadingDisplay::Normal => "",
HeadingDisplay::Subtitle => "",
},
);
self self
} }

View file

@ -32,11 +32,9 @@ impl ComponentTrait for Icon {
} }
fn before_render(&mut self, context: &mut InContext) { fn before_render(&mut self, context: &mut InContext) {
context context.alter(InContextOp::StyleSheet(AssetsOp::Add(
.alter(InContextOp::StyleSheet(AssetsOp::Add( StyleSheet::located("/theme/icons/bootstrap-icons.css").with_version("1.8.2"),
StyleSheet::located("/theme/icons/bootstrap-icons.css") )));
.with_version("1.8.2")
)));
} }
fn default_render(&self, _: &mut InContext) -> Markup { fn default_render(&self, _: &mut InContext) -> Markup {
@ -93,7 +91,10 @@ impl Icon {
pub fn alter_icon_name(&mut self, name: &str) -> &mut Self { pub fn alter_icon_name(&mut self, name: &str) -> &mut Self {
self.icon_name = name.to_owned(); self.icon_name = name.to_owned();
self.alter_classes(ClassesOp::SetDefault, concat_string!("bi-", self.icon_name).as_str()); self.alter_classes(
ClassesOp::SetDefault,
concat_string!("bi-", self.icon_name).as_str(),
);
self self
} }

View file

@ -94,10 +94,7 @@ impl MenuItem {
MenuItem { MenuItem {
weight : 0, weight : 0,
renderable: render_always, renderable: render_always,
item_type : MenuItemType::Link( item_type : MenuItemType::Link(label.to_owned(), path.to_owned()),
label.to_owned(),
path.to_owned(),
),
} }
} }
@ -105,10 +102,7 @@ impl MenuItem {
MenuItem { MenuItem {
weight : 0, weight : 0,
renderable: render_always, renderable: render_always,
item_type : MenuItemType::LinkBlank( item_type : MenuItemType::LinkBlank(label.to_owned(), path.to_owned()),
label.to_owned(),
path.to_owned(),
),
} }
} }
@ -132,10 +126,7 @@ impl MenuItem {
MenuItem { MenuItem {
weight : 0, weight : 0,
renderable: render_always, renderable: render_always,
item_type : MenuItemType::Submenu( item_type : MenuItemType::Submenu(label.to_owned(), menu),
label.to_owned(),
menu
),
} }
} }
@ -219,16 +210,13 @@ impl ComponentTrait for Menu {
fn default_render(&self, context: &mut InContext) -> Markup { fn default_render(&self, context: &mut InContext) -> Markup {
context context
.alter(InContextOp::StyleSheet(AssetsOp::Add( .alter(InContextOp::StyleSheet(AssetsOp::Add(
StyleSheet::located("/theme/menu/css/menu.css") StyleSheet::located("/theme/menu/css/menu.css").with_version("1.1.1"),
.with_version("1.1.1")
))) )))
.alter(InContextOp::StyleSheet(AssetsOp::Add( .alter(InContextOp::StyleSheet(AssetsOp::Add(
StyleSheet::located("/theme/menu/css/menu-clean.css") StyleSheet::located("/theme/menu/css/menu-clean.css").with_version("1.1.1"),
.with_version("1.1.1")
))) )))
.alter(InContextOp::JavaScript(AssetsOp::Add( .alter(InContextOp::JavaScript(AssetsOp::Add(
JavaScript::located("/theme/menu/js/menu.min.js") JavaScript::located("/theme/menu/js/menu.min.js").with_version("1.1.1"),
.with_version("1.1.1")
))) )))
.alter(InContextOp::AddJQuery); .alter(InContextOp::AddJQuery);
@ -257,7 +245,6 @@ impl ComponentTrait for Menu {
} }
impl Menu { impl Menu {
// Menu BUILDER. // Menu BUILDER.
pub fn with_weight(mut self, weight: isize) -> Self { pub fn with_weight(mut self, weight: isize) -> Self {

View file

@ -132,14 +132,17 @@ impl Paragraph {
pub fn alter_display(&mut self, display: ParagraphDisplay) -> &mut Self { pub fn alter_display(&mut self, display: ParagraphDisplay) -> &mut Self {
self.display = display; self.display = display;
self.classes.alter(ClassesOp::SetDefault, match &self.display() { self.classes.alter(
ParagraphDisplay::XxLarge => "fs-2", ClassesOp::SetDefault,
ParagraphDisplay::Large => "fs-3", match &self.display() {
ParagraphDisplay::Medium => "fs-4", ParagraphDisplay::XxLarge => "fs-2",
ParagraphDisplay::Small => "fs-5", ParagraphDisplay::Large => "fs-3",
ParagraphDisplay::XxSmall => "fs-6", ParagraphDisplay::Medium => "fs-4",
ParagraphDisplay::Normal => "", ParagraphDisplay::Small => "fs-5",
}); ParagraphDisplay::XxSmall => "fs-6",
ParagraphDisplay::Normal => "",
},
);
self self
} }

View file

@ -27,9 +27,9 @@ impl ModuleTrait for DefaultHomePage {
async fn demo() -> app::Result<Markup> { async fn demo() -> app::Result<Markup> {
Page::new() Page::new()
.with_title(l("page_title").as_str()) .with_title(l("page_title").as_str())
.with_context(InContextOp::StyleSheet(AssetsOp::Add( .with_context(InContextOp::StyleSheet(AssetsOp::Add(StyleSheet::located(
StyleSheet::located("/theme/module/homepage/styles.css") "/theme/module/homepage/styles.css",
))) ))))
.add_to("content", hello_world()) .add_to("content", hello_world())
.add_to("content", welcome()) .add_to("content", welcome())
.add_to("content", about_pagetop()) .add_to("content", about_pagetop())
@ -39,47 +39,57 @@ async fn demo() -> app::Result<Markup> {
} }
fn hello_world() -> Container { fn hello_world() -> Container {
Container::header() Container::header().with_id("hello-world").with_component(
.with_id("hello-world") grid::Row::new()
.with_component(grid::Row::new() .with_column(
.with_column(grid::Column::new() grid::Column::new()
.with_classes(ClassesOp::Add, "hello-col-text") .with_classes(ClassesOp::Add, "hello-col-text")
.with_size(grid::ColumnSize::Is4of12) .with_size(grid::ColumnSize::Is4of12)
.with_component(Heading::h1(html! { .with_component(
(l("page_title")) Heading::h1(html! {
}) (l("page_title"))
.with_display(HeadingDisplay::Medium) })
) .with_display(HeadingDisplay::Medium),
.with_component(Paragraph::with(html! { )
(e("hello_intro", &args![ .with_component(
"app" => format!("<strong>{}</strong>", &SETTINGS.app.name) Paragraph::with(html! {
])) (e("hello_intro", &args![
}) "app" => format!("<strong>{}</strong>", &SETTINGS.app.name)
.with_display(ParagraphDisplay::Small) ]))
) })
.with_component(Paragraph::with(html! { .with_display(ParagraphDisplay::Small),
)
.with_component(Paragraph::with(html! {
(e("hello_pagetop", &args![ (e("hello_pagetop", &args![
"pagetop" => "<a href=\"https://pagetop-rs\">PageTop</a>" "pagetop" => "<a href=\"https://pagetop-rs\">PageTop</a>"
])) ]))
}) }))
) .with_component(
.with_component(Anchor::button("#", html! { Anchor::button(
("Offered services") "#",
}) html! {
.with_left_icon(Icon::with("card-checklist")) ("Offered services")
.with_classes(ClassesOp::Add, "services-link") },
) )
.with_component(Anchor::button("#", html! { .with_left_icon(Icon::with("card-checklist"))
("Get quote") .with_classes(ClassesOp::Add, "services-link"),
}) )
.with_left_icon(Icon::with("envelope-open-heart-fill")) .with_component(
) Anchor::button(
"#",
html! {
("Get quote")
},
)
.with_left_icon(Icon::with("envelope-open-heart-fill")),
),
) )
.with_column(grid::Column::new() .with_column(
.with_classes(ClassesOp::Add, "hello-col-image") grid::Column::new()
.with_component(Image::image("/theme/images/demo-header.svg")) .with_classes(ClassesOp::Add, "hello-col-image")
) .with_component(Image::image("/theme/images/demo-header.svg")),
) ),
)
} }
fn welcome() -> Container { fn welcome() -> Container {
@ -89,100 +99,103 @@ fn welcome() -> Container {
.with_component(Heading::h2(html! { .with_component(Heading::h2(html! {
(t("welcome_to", &args!["app" => SETTINGS.app.name.as_str()])) (t("welcome_to", &args!["app" => SETTINGS.app.name.as_str()]))
})) }))
.with_component(Heading::h3(html! { .with_component(
Heading::h3(html! {
(l("welcome_subtitle")) (l("welcome_subtitle"))
}) })
.with_display(HeadingDisplay::Subtitle) .with_display(HeadingDisplay::Subtitle),
) )
.with_component(Paragraph::with(html! { .with_component(
Paragraph::with(html! {
(l("welcome_text1")) (l("welcome_text1"))
}) })
.with_display(ParagraphDisplay::Small) .with_display(ParagraphDisplay::Small),
) )
.with_component(Paragraph::with(html! { (l("welcome_text2")) })) .with_component(Paragraph::with(html! { (l("welcome_text2")) }))
} }
fn about_pagetop() -> Container { fn about_pagetop() -> Container {
Container::new() Container::new().with_id("pagetop").with_component(
.with_id("pagetop") grid::Row::new()
.with_component(grid::Row::new() .with_column(
.with_column(grid::Column::new() grid::Column::new()
.with_classes(ClassesOp::Add, "pagetop-col-image") .with_classes(ClassesOp::Add, "pagetop-col-image")
.with_size(grid::ColumnSize::Is5of12) .with_size(grid::ColumnSize::Is5of12)
.with_component(Image::image("/theme/images/demo-about.svg")) .with_component(Image::image("/theme/images/demo-about.svg")),
) )
.with_column(grid::Column::new() .with_column(
.with_classes(ClassesOp::Add, "pagetop-col-text") grid::Column::new()
.with_component(Heading::h2(html! { .with_classes(ClassesOp::Add, "pagetop-col-text")
(l("pagetop_title")) .with_component(Heading::h2(html! {
})) (l("pagetop_title"))
.with_component(Paragraph::with(html! { }))
(l("pagetop_text1")) .with_component(
}) Paragraph::with(html! {
.with_display(ParagraphDisplay::Small) (l("pagetop_text1"))
) })
.with_component(Paragraph::with(html! { .with_display(ParagraphDisplay::Small),
)
.with_component(Paragraph::with(html! {
(l("pagetop_text2")) (l("pagetop_text2"))
}) }))
) .with_component(Paragraph::with(html! {
.with_component(Paragraph::with(html! {
(l("pagetop_text3")) (l("pagetop_text3"))
}) })),
) ),
) )
)
} }
fn promo_pagetop() -> Container { fn promo_pagetop() -> Container {
Container::new() Container::new().with_id("promo").with_component(
.with_id("promo") grid::Row::new()
.with_component(grid::Row::new() .with_column(
.with_column(grid::Column::new() grid::Column::new()
.with_classes(ClassesOp::Add, "promo-col-image") .with_classes(ClassesOp::Add, "promo-col-image")
.with_size(grid::ColumnSize::Is5of12) .with_size(grid::ColumnSize::Is5of12)
.with_component(Image::image("/theme/images/demo-pagetop.svg")) .with_component(Image::image("/theme/images/demo-pagetop.svg")),
) )
.with_column(grid::Column::new() .with_column(
.with_classes(ClassesOp::Add, "promo-col-text") grid::Column::new()
.with_component(Heading::h2(html! { .with_classes(ClassesOp::Add, "promo-col-text")
.with_component(Heading::h2(html! {
(l("pagetop_promo_title")) (l("pagetop_promo_title"))
}) }))
) .with_component(
.with_component(Paragraph::with(html! { Paragraph::with(html! {
(e("pagetop_promo_text1", &args![ (e("pagetop_promo_text1", &args![
"pagetop" => "<a href=\"https://pagetop-rs\">PageTop</a>" "pagetop" => "<a href=\"https://pagetop-rs\">PageTop</a>"
])) ]))
}) })
.with_display(ParagraphDisplay::Small) .with_display(ParagraphDisplay::Small),
) ),
) ),
) )
} }
fn reporting_problems() -> Container { fn reporting_problems() -> Container {
Container::new() Container::new().with_id("reporting").with_component(
.with_id("reporting") grid::Row::new()
.with_component(grid::Row::new() .with_column(
.with_column(grid::Column::new() grid::Column::new()
.with_classes(ClassesOp::Add, "reporting-col-text") .with_classes(ClassesOp::Add, "reporting-col-text")
.with_size(grid::ColumnSize::Is7of12) .with_size(grid::ColumnSize::Is7of12)
.with_component(Heading::h2(html! { .with_component(Heading::h2(html! {
(l("report_problems_title")) (l("report_problems_title"))
}) }))
) .with_component(
.with_component(Paragraph::with(html! { Paragraph::with(html! {
(l("report_problems_text1")) (l("report_problems_text1"))
}) })
.with_display(ParagraphDisplay::Small) .with_display(ParagraphDisplay::Small),
) )
.with_component(Paragraph::with(html! { .with_component(Paragraph::with(html! {
(l("report_problems_text2")) (l("report_problems_text2"))
}) })),
)
) )
.with_column(grid::Column::new() .with_column(
.with_classes(ClassesOp::Add, "reporting-col-image") grid::Column::new()
.with_component(Image::image("/theme/images/demo-pagetop.svg")) .with_classes(ClassesOp::Add, "reporting-col-image")
) .with_component(Image::image("/theme/images/demo-pagetop.svg")),
) ),
)
} }

View file

@ -1,4 +1,7 @@
pub mod aliner; pub mod aliner;
pub mod minimal; pub mod minimal;
pub mod bootsier; pub mod bootsier;
pub mod bulmix; pub mod bulmix;

View file

@ -16,15 +16,11 @@ impl ThemeTrait for Aliner {
} }
fn before_render_page(&self, page: &mut Page) { fn before_render_page(&self, page: &mut Page) {
page page.alter_context(InContextOp::Favicon(Some(
.alter_context(InContextOp::Favicon(Some(Favicon::new() Favicon::new().with_icon("/theme/favicon.png"),
.with_icon("/theme/favicon.png") )))
))) .alter_context(InContextOp::StyleSheet(AssetsOp::Add(
.alter_context(InContextOp::StyleSheet(AssetsOp::Add( StyleSheet::located("/aliner/css/styles.css").with_weight(-99),
StyleSheet::located( )));
"/aliner/css/styles.css"
)
.with_weight(-99)
)));
} }
} }

View file

@ -18,21 +18,20 @@ impl ThemeTrait for Bootsier {
} }
fn before_render_page(&self, page: &mut Page) { fn before_render_page(&self, page: &mut Page) {
page page.alter_context(InContextOp::Favicon(Some(
.alter_context(InContextOp::Favicon(Some(Favicon::new() Favicon::new().with_icon("/theme/favicon.png"),
.with_icon("/theme/favicon.png") )))
))) .alter_context(InContextOp::StyleSheet(AssetsOp::Add(
.alter_context(InContextOp::StyleSheet(AssetsOp::Add( StyleSheet::located("/bootsier/css/bootstrap.min.css")
StyleSheet::located("/bootsier/css/bootstrap.min.css") .with_version("5.1.3")
.with_version("5.1.3") .with_weight(-99),
.with_weight(-99) )))
))) .alter_context(InContextOp::JavaScript(AssetsOp::Add(
.alter_context(InContextOp::JavaScript(AssetsOp::Add( JavaScript::located("/bootsier/js/bootstrap.bundle.min.js")
JavaScript::located("/bootsier/js/bootstrap.bundle.min.js") .with_version("5.1.3")
.with_version("5.1.3") .with_weight(-99),
.with_weight(-99) )))
))) .alter_context(InContextOp::AddJQuery);
.alter_context(InContextOp::AddJQuery);
} }
fn render_error_page(&self, mut s: app::http::StatusCode) -> app::Result<Markup> { fn render_error_page(&self, mut s: app::http::StatusCode) -> app::Result<Markup> {
@ -42,37 +41,40 @@ impl ThemeTrait for Bootsier {
app::http::StatusCode::NOT_FOUND => { app::http::StatusCode::NOT_FOUND => {
description = "e404-description"; description = "e404-description";
message = "e404-message"; message = "e404-message";
}, }
_ => { _ => {
s = app::http::StatusCode::INTERNAL_SERVER_ERROR; s = app::http::StatusCode::INTERNAL_SERVER_ERROR;
} }
} }
Page::new() Page::new()
.with_title(format!("Error {}", s.as_str()).as_str()) .with_title(format!("Error {}", s.as_str()).as_str())
.add_to("content", Chunck::with(html! { .add_to(
div class="jumbotron" { "content",
div class="media" { Chunck::with(html! {
img div class="jumbotron" {
src="/static/bootsier/images/caution.png" div class="media" {
class="mr-4" img
style="width: 20%; max-width: 188px" src="/static/bootsier/images/caution.png"
alt="Caution!"; class="mr-4"
div class="media-body" { style="width: 20%; max-width: 188px"
h1 class="display-4" { (s.as_str()) } alt="Caution!";
p class="lead" { (l(description)) } div class="media-body" {
hr class="my-4"; h1 class="display-4" { (s.as_str()) }
p { (l(message)) } p class="lead" { (l(description)) }
a hr class="my-4";
class="btn btn-primary btn-lg" p { (l(message)) }
href="/" a
role="button" class="btn btn-primary btn-lg"
{ href="/"
(l("back-homepage")) role="button"
{
(l("back-homepage"))
}
} }
} }
} }
} }),
})) )
.render() .render()
} }
} }

View file

@ -16,84 +16,100 @@ impl ThemeTrait for Bulmix {
} }
fn before_render_page(&self, page: &mut Page) { fn before_render_page(&self, page: &mut Page) {
page page.alter_context(InContextOp::Favicon(Some(
.alter_context(InContextOp::Favicon(Some(Favicon::new() Favicon::new().with_icon("/theme/favicon.png"),
.with_icon("/theme/favicon.png") )))
))) .alter_context(InContextOp::StyleSheet(AssetsOp::Add(
.alter_context(InContextOp::StyleSheet(AssetsOp::Add( StyleSheet::located("/bulmix/css/bulma.min.css")
StyleSheet::located("/bulmix/css/bulma.min.css") .with_version("0.9.4")
.with_version("0.9.4") .with_weight(-99),
.with_weight(-99) )))
))) .alter_context(InContextOp::AddJQuery);
.alter_context(InContextOp::AddJQuery);
} }
fn before_render_component( fn before_render_component(
&self, &self,
component: &mut dyn ComponentTrait, component: &mut dyn ComponentTrait,
_context: &mut InContext _context: &mut InContext,
) { ) {
match component.handler() { match component.handler() {
COMPONENT_ANCHOR => { COMPONENT_ANCHOR => {
let a = component_mut::<Anchor>(component); let a = component_mut::<Anchor>(component);
a.alter_classes(ClassesOp::SetDefault, match a.anchor_type() { a.alter_classes(
AnchorType::Button => "button is-primary", ClassesOp::SetDefault,
_ => "", match a.anchor_type() {
}); AnchorType::Button => "button is-primary",
}, _ => "",
},
);
}
COMPONENT_HEADING => { COMPONENT_HEADING => {
let h = component_mut::<Heading>(component); let h = component_mut::<Heading>(component);
h.alter_classes(ClassesOp::SetDefault, match h.display() { h.alter_classes(
HeadingDisplay::XxLarge => "title is-1", ClassesOp::SetDefault,
HeadingDisplay::Large => "title is-2", match h.display() {
HeadingDisplay::Medium => "title is-3", HeadingDisplay::XxLarge => "title is-1",
HeadingDisplay::Small => "title is-4", HeadingDisplay::Large => "title is-2",
HeadingDisplay::XxSmall => "title is-5", HeadingDisplay::Medium => "title is-3",
HeadingDisplay::Normal => "title", HeadingDisplay::Small => "title is-4",
HeadingDisplay::Subtitle => "subtitle", HeadingDisplay::XxSmall => "title is-5",
}); HeadingDisplay::Normal => "title",
}, HeadingDisplay::Subtitle => "subtitle",
},
);
}
COMPONENT_PARAGRAPH => { COMPONENT_PARAGRAPH => {
let p = component_mut::<Paragraph>(component); let p = component_mut::<Paragraph>(component);
p.alter_classes(ClassesOp::SetDefault, match p.display() { p.alter_classes(
ParagraphDisplay::XxLarge => "is-size-2", ClassesOp::SetDefault,
ParagraphDisplay::Large => "is-size-3", match p.display() {
ParagraphDisplay::Medium => "is-size-4", ParagraphDisplay::XxLarge => "is-size-2",
ParagraphDisplay::Small => "is-size-5", ParagraphDisplay::Large => "is-size-3",
ParagraphDisplay::XxSmall => "is-size-6", ParagraphDisplay::Medium => "is-size-4",
ParagraphDisplay::Normal => "", ParagraphDisplay::Small => "is-size-5",
}); ParagraphDisplay::XxSmall => "is-size-6",
}, ParagraphDisplay::Normal => "",
},
);
}
grid::COMPONENT_COLUMN => { grid::COMPONENT_COLUMN => {
let col = component_mut::<grid::Column>(component); let col = component_mut::<grid::Column>(component);
col.alter_classes(ClassesOp::SetDefault, concat_string!("column", match col.size() { col.alter_classes(
grid::ColumnSize::Default => "", ClassesOp::SetDefault,
grid::ColumnSize::Is1of12 => " is-1", concat_string!(
grid::ColumnSize::Is2of12 => " is-2", "column",
grid::ColumnSize::Is3of12 => " is-3", match col.size() {
grid::ColumnSize::Is4of12 => " is-4", grid::ColumnSize::Default => "",
grid::ColumnSize::Is5of12 => " is-5", grid::ColumnSize::Is1of12 => " is-1",
grid::ColumnSize::Is6of12 => " is-6", grid::ColumnSize::Is2of12 => " is-2",
grid::ColumnSize::Is7of12 => " is-7", grid::ColumnSize::Is3of12 => " is-3",
grid::ColumnSize::Is8of12 => " is-8", grid::ColumnSize::Is4of12 => " is-4",
grid::ColumnSize::Is9of12 => " is-9", grid::ColumnSize::Is5of12 => " is-5",
grid::ColumnSize::Is10of12 => " is-10", grid::ColumnSize::Is6of12 => " is-6",
grid::ColumnSize::Is11of12 => " is-11", grid::ColumnSize::Is7of12 => " is-7",
grid::ColumnSize::IsFull => " is-12", grid::ColumnSize::Is8of12 => " is-8",
}, " content").as_str()); grid::ColumnSize::Is9of12 => " is-9",
}, grid::ColumnSize::Is10of12 => " is-10",
grid::ColumnSize::Is11of12 => " is-11",
grid::ColumnSize::IsFull => " is-12",
},
" content"
)
.as_str(),
);
}
grid::COMPONENT_ROW => { grid::COMPONENT_ROW => {
let row = component_mut::<grid::Row>(component); let row = component_mut::<grid::Row>(component);
row.alter_classes(ClassesOp::SetDefault, "columns"); row.alter_classes(ClassesOp::SetDefault, "columns");
}, }
_ => {}, _ => {}
} }
} }
fn render_component( fn render_component(
&self, &self,
component: &dyn ComponentTrait, component: &dyn ComponentTrait,
_context: &mut InContext _context: &mut InContext,
) -> Option<Markup> { ) -> Option<Markup> {
match component.handler() { match component.handler() {
COMPONENT_ICON => { COMPONENT_ICON => {
@ -103,7 +119,7 @@ impl ThemeTrait for Bulmix {
i class=[icon.classes().get()] {}; i class=[icon.classes().get()] {};
} }
}) })
}, }
_ => None, _ => None,
} }
} }

View file

@ -22,19 +22,14 @@ pub static CONFIG: Lazy<Config> = Lazy::new(|| {
// Combina los archivos de configuración y asigna el modo de ejecución. // Combina los archivos de configuración y asigna el modo de ejecución.
settings settings
.merge( .merge(File::with_name(&format!("{}/{}.toml", CONFIG_DIR, "common")).required(false))
File::with_name( .unwrap()
&format!("{}/{}.toml", CONFIG_DIR, "common") .merge(File::with_name(&format!("{}/{}.toml", CONFIG_DIR, run_mode)).required(false))
).required(false)).unwrap() .unwrap()
.merge( .merge(File::with_name(&format!("{}/{}.toml", CONFIG_DIR, "local")).required(false))
File::with_name( .unwrap()
&format!("{}/{}.toml", CONFIG_DIR, run_mode) .set("app.run_mode", run_mode)
).required(false)).unwrap() .unwrap();
.merge(
File::with_name(
&format!("{}/{}.toml", CONFIG_DIR, "local")
).required(false)).unwrap()
.set("app.run_mode", run_mode).unwrap();
settings settings
}); });

View file

@ -1,4 +1,11 @@
pub mod component; // API to build new components. // API to build new components.
pub mod hook; // API to define functions that alter the behavior of PageTop core. pub mod component;
pub mod module; // API to add new features with modules.
pub mod theme; // API to create themes. // API to define functions that alter the behavior of PageTop core.
pub mod hook;
// API to add new features with modules.
pub mod module;
// API to create themes.
pub mod theme;

View file

@ -2,13 +2,8 @@ mod context;
pub use context::{InContext, InContextOp}; pub use context::{InContext, InContextOp};
mod definition; mod definition;
pub use definition::{
AnyComponent,
ComponentTrait,
component_ref,
component_mut,
};
use definition::render_component; use definition::render_component;
pub use definition::{component_mut, component_ref, AnyComponent, ComponentTrait};
mod bundle; mod bundle;
pub use bundle::ComponentsBundle; pub use bundle::ComponentsBundle;
@ -19,6 +14,10 @@ pub(crate) use all::common_components;
pub type Renderable = fn(_: &InContext) -> bool; pub type Renderable = fn(_: &InContext) -> bool;
pub fn render_always(_: &InContext) -> bool { true } pub fn render_always(_: &InContext) -> bool {
true
}
pub fn render_never(_: &InContext) -> bool { false } pub fn render_never(_: &InContext) -> bool {
false
}

View file

@ -1,12 +1,11 @@
use super::{ComponentTrait, ComponentsBundle};
use crate::Lazy; use crate::Lazy;
use super::{ComponentsBundle, ComponentTrait};
use std::sync::RwLock;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::RwLock;
static COMPONENTS: Lazy<RwLock<HashMap<&str, ComponentsBundle>>> = Lazy::new(|| { static COMPONENTS: Lazy<RwLock<HashMap<&str, ComponentsBundle>>> =
RwLock::new(HashMap::new()) Lazy::new(|| RwLock::new(HashMap::new()));
});
pub fn add_component_to(region: &'static str, component: impl ComponentTrait) { pub fn add_component_to(region: &'static str, component: impl ComponentTrait) {
let mut hmap = COMPONENTS.write().unwrap(); let mut hmap = COMPONENTS.write().unwrap();

View file

@ -1,5 +1,5 @@
use crate::html::{Markup, html}; use super::{ComponentTrait, InContext};
use super::{InContext, ComponentTrait}; use crate::html::{html, Markup};
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};

View file

@ -1,15 +1,14 @@
use crate::{Lazy, base, concat_string, util};
use crate::config::SETTINGS; use crate::config::SETTINGS;
use crate::html::*;
use crate::core::theme::ThemeTrait;
use crate::core::theme::all::theme_by_single_name; use crate::core::theme::all::theme_by_single_name;
use crate::core::theme::ThemeTrait;
use crate::html::*;
use crate::{base, concat_string, util, Lazy};
static DEFAULT_THEME: Lazy<&dyn ThemeTrait> = Lazy::new(|| { static DEFAULT_THEME: Lazy<&dyn ThemeTrait> =
match theme_by_single_name(&SETTINGS.app.theme) { Lazy::new(|| match theme_by_single_name(&SETTINGS.app.theme) {
Some(theme) => theme, Some(theme) => theme,
None => &base::theme::bootsier::Bootsier, None => &base::theme::bootsier::Bootsier,
} });
});
pub enum InContextOp { pub enum InContextOp {
SetTheme(&'static str), SetTheme(&'static str),
@ -47,28 +46,30 @@ impl InContext {
match op { match op {
InContextOp::SetTheme(theme_name) => { InContextOp::SetTheme(theme_name) => {
self.theme = theme_by_single_name(theme_name).unwrap_or(*DEFAULT_THEME); self.theme = theme_by_single_name(theme_name).unwrap_or(*DEFAULT_THEME);
}, }
InContextOp::AddMetadata(name, content) => { InContextOp::AddMetadata(name, content) => {
self.metadata.push((name.to_owned(), content.to_owned())); self.metadata.push((name.to_owned(), content.to_owned()));
}, }
InContextOp::Favicon(favicon) => { InContextOp::Favicon(favicon) => {
self.favicon = favicon; self.favicon = favicon;
}, }
InContextOp::StyleSheet(css) => { InContextOp::StyleSheet(css) => {
self.stylesheets.alter(css); self.stylesheets.alter(css);
}, }
InContextOp::JavaScript(js) => { InContextOp::JavaScript(js) => {
self.javascripts.alter(js); self.javascripts.alter(js);
}, }
InContextOp::AddJQuery => if !self.with_jquery { InContextOp::AddJQuery => {
self.javascripts.alter(AssetsOp::Add( if !self.with_jquery {
JavaScript::located("/theme/js/jquery.min.js") self.javascripts.alter(AssetsOp::Add(
.with_version("3.6.0") JavaScript::located("/theme/js/jquery.min.js")
.with_weight(isize::MIN) .with_version("3.6.0")
.with_mode(JSMode::Normal) .with_weight(isize::MIN)
)); .with_mode(JSMode::Normal),
self.with_jquery = true; ));
}, self.with_jquery = true;
}
}
} }
self self
} }

View file

@ -1,11 +1,13 @@
use crate::util;
use crate::html::{Markup, html};
use super::InContext; use super::InContext;
use crate::html::{html, Markup};
use crate::util;
pub use std::any::Any as AnyComponent; pub use std::any::Any as AnyComponent;
pub trait ComponentTrait: AnyComponent + Send + Sync { pub trait ComponentTrait: AnyComponent + Send + Sync {
fn new() -> Self where Self: Sized; fn new() -> Self
where
Self: Sized;
fn handler(&self) -> &'static str; fn handler(&self) -> &'static str;
@ -27,8 +29,7 @@ pub trait ComponentTrait: AnyComponent + Send + Sync {
} }
#[allow(unused_variables)] #[allow(unused_variables)]
fn before_render(&mut self, context: &mut InContext) { fn before_render(&mut self, context: &mut InContext) {}
}
#[allow(unused_variables)] #[allow(unused_variables)]
fn default_render(&self, context: &mut InContext) -> Markup { fn default_render(&self, context: &mut InContext) -> Markup {
@ -58,9 +59,9 @@ pub fn render_component(component: &mut dyn ComponentTrait, context: &mut InCont
match component.is_renderable(context) { match component.is_renderable(context) {
true => match context.theme().render_component(component, context) { true => match context.theme().render_component(component, context) {
Some(html) => html, Some(html) => html,
None => component.default_render(context) None => component.default_render(context),
}, },
false => html! {} false => html! {},
} }
} }

View file

@ -1,14 +1,10 @@
mod definition; mod definition;
pub use definition::{ pub use definition::{action_ref, AnyHook, HookTrait};
AnyHook,
HookTrait,
action_ref,
};
mod holder; mod holder;
pub use holder::HookAction; pub use holder::HookAction;
use holder::HooksHolder; use holder::HooksHolder;
mod all; mod all;
pub use all::run_actions;
pub(crate) use all::add_hook; pub(crate) use all::add_hook;
pub use all::run_actions;

View file

@ -1,13 +1,12 @@
use crate::Lazy;
use super::{HookAction, HooksHolder}; use super::{HookAction, HooksHolder};
use crate::Lazy;
use std::sync::RwLock;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::RwLock;
// Registered actions. // Registered actions.
static ACTIONS: Lazy<RwLock<HashMap<&str, HooksHolder>>> = Lazy::new(|| { static ACTIONS: Lazy<RwLock<HashMap<&str, HooksHolder>>> =
RwLock::new(HashMap::new()) Lazy::new(|| RwLock::new(HashMap::new()));
});
pub fn add_hook(hook: HookAction) { pub fn add_hook(hook: HookAction) {
let mut hmap = ACTIONS.write().unwrap(); let mut hmap = ACTIONS.write().unwrap();
@ -19,7 +18,10 @@ pub fn add_hook(hook: HookAction) {
} }
} }
pub fn run_actions<B, F>(action_handler: &str, f: F) where F: FnMut(&HookAction) -> B { pub fn run_actions<B, F>(action_handler: &str, f: F)
where
F: FnMut(&HookAction) -> B,
{
if let Some(actions) = ACTIONS.read().unwrap().get(action_handler) { if let Some(actions) = ACTIONS.read().unwrap().get(action_handler) {
actions.iter_map(f) actions.iter_map(f)
} }

View file

@ -1,7 +1,9 @@
pub use std::any::Any as AnyHook; pub use std::any::Any as AnyHook;
pub trait HookTrait: AnyHook + Send + Sync { pub trait HookTrait: AnyHook + Send + Sync {
fn new() -> Self where Self: Sized; fn new() -> Self
where
Self: Sized;
fn handler(&self) -> &'static str; fn handler(&self) -> &'static str;

View file

@ -30,7 +30,11 @@ impl HooksHolder {
actions.sort_by_key(|a| a.weight()); actions.sort_by_key(|a| a.weight());
} }
pub fn iter_map<B, F>(&self, f: F) where Self: Sized, F: FnMut(&HookAction) -> B { pub fn iter_map<B, F>(&self, f: F)
where
Self: Sized,
F: FnMut(&HookAction) -> B,
{
let _: Vec<_> = self.0.read().unwrap().iter().map(f).collect(); let _: Vec<_> = self.0.read().unwrap().iter().map(f).collect();
} }
} }

View file

@ -1,7 +1,4 @@
mod definition; mod definition;
pub use definition::{ pub use definition::{BaseModule, ModuleTrait};
BaseModule,
ModuleTrait,
};
pub(crate) mod all; pub(crate) mod all;

View file

@ -1,19 +1,14 @@
use crate::{Lazy, app, trace};
use crate::core::hook::add_hook;
use super::ModuleTrait; use super::ModuleTrait;
use crate::core::hook::add_hook;
use crate::{app, trace, Lazy};
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))] #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
use crate::{ use crate::{db::*, run_now};
db::*,
run_now,
};
use std::sync::RwLock; use std::sync::RwLock;
// Enabled modules. // Enabled modules.
static ENABLED_MODULES: Lazy<RwLock<Vec<&dyn ModuleTrait>>> = Lazy::new(|| { static ENABLED_MODULES: Lazy<RwLock<Vec<&dyn ModuleTrait>>> = Lazy::new(|| RwLock::new(Vec::new()));
RwLock::new(Vec::new())
});
/* Disabled modules. /* Disabled modules.
static DISABLED_MODULES: Lazy<RwLock<Vec<&dyn ModuleTrait>>> = Lazy::new(|| { static DISABLED_MODULES: Lazy<RwLock<Vec<&dyn ModuleTrait>>> = Lazy::new(|| {
@ -34,7 +29,12 @@ fn enable(module: &'static dyn ModuleTrait) {
} }
fn add_to(list: &mut Vec<&dyn ModuleTrait>, module: &'static dyn ModuleTrait) { fn add_to(list: &mut Vec<&dyn ModuleTrait>, module: &'static dyn ModuleTrait) {
if !ENABLED_MODULES.read().unwrap().iter().any(|m| m.handler() == module.handler()) { if !ENABLED_MODULES
.read()
.unwrap()
.iter()
.any(|m| m.handler() == module.handler())
{
if !list.iter().any(|m| m.handler() == module.handler()) { if !list.iter().any(|m| m.handler() == module.handler()) {
trace::debug!("Enabling module \"{}\"", module.single_name()); trace::debug!("Enabling module \"{}\"", module.single_name());
list.push(module); list.push(module);
@ -80,5 +80,6 @@ pub fn run_migrations() {
} }
} }
Migrator::up(&app::db::DBCONN, None) Migrator::up(&app::db::DBCONN, None)
}).unwrap(); })
.unwrap();
} }

View file

@ -1,5 +1,5 @@
use crate::{app, util};
use crate::core::hook::HookAction; use crate::core::hook::HookAction;
use crate::{app, util};
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))] #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
use crate::db::MigrationItem; use crate::db::MigrationItem;
@ -25,8 +25,7 @@ pub trait ModuleTrait: BaseModule + Send + Sync {
} }
#[allow(unused_variables)] #[allow(unused_variables)]
fn configure_service(&self, cfg: &mut app::web::ServiceConfig) { fn configure_service(&self, cfg: &mut app::web::ServiceConfig) {}
}
fn actions(&self) -> Vec<HookAction> { fn actions(&self) -> Vec<HookAction> {
vec![] vec![]

View file

@ -1,7 +1,4 @@
mod definition; mod definition;
pub use definition::{ pub use definition::{BaseTheme, ThemeTrait};
BaseTheme,
ThemeTrait,
};
pub(crate) mod all; pub(crate) mod all;

View file

@ -1,14 +1,12 @@
use crate::{Lazy, app, theme_static_files, trace};
use super::ThemeTrait; use super::ThemeTrait;
use crate::{app, theme_static_files, trace, Lazy};
use std::sync::RwLock; use std::sync::RwLock;
include!(concat!(env!("OUT_DIR"), "/theme.rs")); include!(concat!(env!("OUT_DIR"), "/theme.rs"));
// Temas registrados. // Temas registrados.
static THEMES: Lazy<RwLock<Vec<&dyn ThemeTrait>>> = Lazy::new(|| { static THEMES: Lazy<RwLock<Vec<&dyn ThemeTrait>>> = Lazy::new(|| RwLock::new(Vec::new()));
RwLock::new(Vec::new())
});
pub fn register_themes(themes: Vec<&'static dyn ThemeTrait>) { pub fn register_themes(themes: Vec<&'static dyn ThemeTrait>) {
for t in themes { for t in themes {
@ -25,9 +23,12 @@ fn register(theme: &'static dyn ThemeTrait) {
} }
pub fn theme_by_single_name(single_name: &str) -> Option<&'static dyn ThemeTrait> { pub fn theme_by_single_name(single_name: &str) -> Option<&'static dyn ThemeTrait> {
match THEMES.write().unwrap().iter().find( match THEMES
|t| t.single_name().to_lowercase() == single_name.to_lowercase() .write()
) { .unwrap()
.iter()
.find(|t| t.single_name().to_lowercase() == single_name.to_lowercase())
{
Some(theme) => Some(*theme), Some(theme) => Some(*theme),
_ => None, _ => None,
} }

View file

@ -1,9 +1,9 @@
use crate::{app, concat_string, util};
use crate::config::SETTINGS;
use crate::html::{Favicon, Markup, html};
use crate::core::component::{ComponentTrait, InContext, InContextOp};
use crate::response::page::Page;
use crate::base::component::Chunck; use crate::base::component::Chunck;
use crate::config::SETTINGS;
use crate::core::component::{ComponentTrait, InContext, InContextOp};
use crate::html::{html, Favicon, Markup};
use crate::response::page::Page;
use crate::{app, concat_string, util};
pub trait BaseTheme { pub trait BaseTheme {
fn single_name(&self) -> &'static str; fn single_name(&self) -> &'static str;
@ -22,15 +22,13 @@ pub trait ThemeTrait: BaseTheme + Send + Sync {
} }
#[allow(unused_variables)] #[allow(unused_variables)]
fn configure_service(&self, cfg: &mut app::web::ServiceConfig) { fn configure_service(&self, cfg: &mut app::web::ServiceConfig) {}
}
#[allow(unused_variables)] #[allow(unused_variables)]
fn before_render_page(&self, page: &mut Page) { fn before_render_page(&self, page: &mut Page) {
page page.alter_context(InContextOp::Favicon(Some(
.alter_context(InContextOp::Favicon(Some(Favicon::new() Favicon::new().with_icon("/theme/favicon.png"),
.with_icon("/theme/favicon.png") )));
)));
} }
fn render_page_head(&self, page: &mut Page) -> Markup { fn render_page_head(&self, page: &mut Page) -> Markup {
@ -81,55 +79,54 @@ pub trait ThemeTrait: BaseTheme + Send + Sync {
} }
#[allow(unused_variables)] #[allow(unused_variables)]
fn before_render_component( fn before_render_component(&self, component: &mut dyn ComponentTrait, context: &mut InContext) {
&self, /*
component: &mut dyn ComponentTrait, Cómo usarlo:
context: &mut InContext
) {
/*
Cómo usarlo:
match component.handler() { match component.handler() {
BLOCK_COMPONENT => { BLOCK_COMPONENT => {
let block = component_mut::<Block>(component); let block = component_mut::<Block>(component);
block.alter_title("New title"); block.alter_title("New title");
}, },
_ => {}, _ => {},
} }
*/ */
} }
#[allow(unused_variables)] #[allow(unused_variables)]
fn render_component( fn render_component(
&self, &self,
component: &dyn ComponentTrait, component: &dyn ComponentTrait,
context: &mut InContext context: &mut InContext,
) -> Option<Markup> { ) -> Option<Markup> {
None None
/* /*
Cómo usarlo: Cómo usarlo:
match component.handler() { match component.handler() {
BLOCK_COMPONENT => { BLOCK_COMPONENT => {
let block = component_ref::<Block>(component); let block = component_ref::<Block>(component);
match block.template() { match block.template() {
"default" => Some(block_default(block)), "default" => Some(block_default(block)),
_ => None, _ => None,
} }
}, },
_ => None, _ => None,
} }
*/ */
} }
fn render_error_page(&self, s: app::http::StatusCode) -> app::Result<Markup> { fn render_error_page(&self, s: app::http::StatusCode) -> app::Result<Markup> {
Page::new() Page::new()
.with_title(format!("Error {}", s.as_str()).as_str()) .with_title(format!("Error {}", s.as_str()).as_str())
.add_to("content", Chunck::with(html! { .add_to(
div { "content",
h1 { (s.as_str()) } Chunck::with(html! {
} div {
})) h1 { (s.as_str()) }
}
}),
)
.render() .render()
} }
} }

View file

@ -1,9 +1,9 @@
pub use maud::{DOCTYPE, Markup, PreEscaped, html}; pub use maud::{html, Markup, PreEscaped, DOCTYPE};
mod assets; mod assets;
pub use assets::{Assets, AssetsOp, SourceValue}; pub use assets::javascript::{JSMode, JavaScript};
pub use assets::javascript::{JavaScript, JSMode};
pub use assets::stylesheet::StyleSheet; pub use assets::stylesheet::StyleSheet;
pub use assets::{Assets, AssetsOp, SourceValue};
mod favicon; mod favicon;
pub use favicon::Favicon; pub use favicon::Favicon;
@ -15,4 +15,4 @@ mod identifier;
pub use identifier::IdentifierValue; pub use identifier::IdentifierValue;
mod classes; mod classes;
pub use classes::{Classes, ClassesOp, ClassValue}; pub use classes::{ClassValue, Classes, ClassesOp};

View file

@ -1,7 +1,7 @@
pub mod javascript; pub mod javascript;
pub mod stylesheet; pub mod stylesheet;
use crate::html::{Markup, html}; use crate::html::{html, Markup};
pub type SourceValue = &'static str; pub type SourceValue = &'static str;
@ -26,19 +26,21 @@ impl<T: AssetsTrait> Assets<T> {
pub fn alter(&mut self, op: AssetsOp<T>) -> &mut Self { pub fn alter(&mut self, op: AssetsOp<T>) -> &mut Self {
match op { match op {
AssetsOp::Add(asset) => match self.0.iter().position( AssetsOp::Add(asset) => {
|x| x.source() == asset.source() match self.0.iter().position(|x| x.source() == asset.source()) {
) { Some(index) => {
Some(index) => if self.0[index].weight() > asset.weight() { if self.0[index].weight() > asset.weight() {
self.0.remove(index); self.0.remove(index);
self.0.push(asset); self.0.push(asset);
}, }
_ => self.0.push(asset) }
_ => self.0.push(asset),
}
} }
AssetsOp::Remove(source) => if let Some(index) = self.0.iter().position( AssetsOp::Remove(source) => {
|x| x.source() == source if let Some(index) = self.0.iter().position(|x| x.source() == source) {
) { self.0.remove(index);
self.0.remove(index); }
} }
} }
self self
@ -53,4 +55,4 @@ impl<T: AssetsTrait> Assets<T> {
} }
} }
} }
} }

View file

@ -1,8 +1,12 @@
use crate::html::{Markup, html};
use super::{AssetsTrait, SourceValue}; use super::{AssetsTrait, SourceValue};
use crate::html::{html, Markup};
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum JSMode { Async, Defer, Normal } pub enum JSMode {
Async,
Defer,
Normal,
}
pub struct JavaScript { pub struct JavaScript {
source : SourceValue, source : SourceValue,

View file

@ -1,5 +1,5 @@
use crate::html::{Markup, html};
use super::{AssetsTrait, SourceValue}; use super::{AssetsTrait, SourceValue};
use crate::html::{html, Markup};
pub struct StyleSheet { pub struct StyleSheet {
source : SourceValue, source : SourceValue,

View file

@ -38,7 +38,7 @@ impl Classes {
match op { match op {
ClassesOp::Add => { ClassesOp::Add => {
self.added = concat_string!(self.added, " ", classes).trim().to_owned() self.added = concat_string!(self.added, " ", classes).trim().to_owned()
}, }
ClassesOp::AddAfter(class) => { ClassesOp::AddAfter(class) => {
let mut v_added: Vec<&str> = self.added.split_ascii_whitespace().collect(); let mut v_added: Vec<&str> = self.added.split_ascii_whitespace().collect();
@ -47,7 +47,7 @@ impl Classes {
_ => v_added.push(classes), _ => v_added.push(classes),
} }
self.added = v_added.join(" "); self.added = v_added.join(" ");
}, }
ClassesOp::AddBefore(class) => { ClassesOp::AddBefore(class) => {
let mut v_added: Vec<&str> = self.added.split_ascii_whitespace().collect(); let mut v_added: Vec<&str> = self.added.split_ascii_whitespace().collect();
@ -56,11 +56,11 @@ impl Classes {
_ => v_added.insert(0, classes), _ => v_added.insert(0, classes),
} }
self.added = v_added.join(" "); self.added = v_added.join(" ");
}, }
ClassesOp::AddFirst => { ClassesOp::AddFirst => {
self.added = concat_string!(classes, " ", self.added).trim().to_owned() self.added = concat_string!(classes, " ", self.added).trim().to_owned()
}, }
ClassesOp::Remove => { ClassesOp::Remove => {
let v_list: Vec<&str> = classes.split_ascii_whitespace().collect(); let v_list: Vec<&str> = classes.split_ascii_whitespace().collect();
@ -71,7 +71,7 @@ impl Classes {
} }
} }
self.added = v_added.join(" "); self.added = v_added.join(" ");
}, }
ClassesOp::Replace(class) => { ClassesOp::Replace(class) => {
let mut v_added: Vec<&str> = self.added.split_ascii_whitespace().collect(); let mut v_added: Vec<&str> = self.added.split_ascii_whitespace().collect();
@ -79,19 +79,21 @@ impl Classes {
Some(pos) => { Some(pos) => {
v_added.remove(pos); v_added.remove(pos);
v_added.insert(pos, classes); v_added.insert(pos, classes);
}, }
_ => v_added.push(classes), _ => v_added.push(classes),
} }
self.added = v_added.join(" "); self.added = v_added.join(" ");
}, }
ClassesOp::Reset => self.added = classes.to_owned(), ClassesOp::Reset => self.added = classes.to_owned(),
ClassesOp::SetDefault => self.default = classes.to_owned(), ClassesOp::SetDefault => self.default = classes.to_owned(),
ClassesOp::SetDefaultIfEmpty => if self.default.is_empty() { ClassesOp::SetDefaultIfEmpty => {
self.default = classes.to_owned() if self.default.is_empty() {
}, self.default = classes.to_owned()
}
}
} }
self self
} }
@ -100,7 +102,11 @@ impl Classes {
if self.default.is_empty() && self.added.is_empty() { if self.default.is_empty() && self.added.is_empty() {
None None
} else { } else {
Some(concat_string!(self.default, " ", self.added).trim().to_owned()) Some(
concat_string!(self.default, " ", self.added)
.trim()
.to_owned(),
)
} }
} }
} }

View file

@ -1,4 +1,4 @@
use crate::html::{Markup, PreEscaped, html}; use crate::html::{html, Markup, PreEscaped};
pub struct Favicon(Vec<String>); pub struct Favicon(Vec<String>);
@ -29,32 +29,29 @@ impl Favicon {
pub fn with_theme_color(mut self, color: &str) -> Self { pub fn with_theme_color(mut self, color: &str) -> Self {
self.0.push(format!( self.0.push(format!(
"<meta name=\"theme-color\" content=\"{}\">", color "<meta name=\"theme-color\" content=\"{}\">",
color
)); ));
self self
} }
pub fn with_ms_tile_color(mut self, color: &str) -> Self { pub fn with_ms_tile_color(mut self, color: &str) -> Self {
self.0.push(format!( self.0.push(format!(
"<meta name=\"msapplication-TileColor\" content=\"{}\">", color "<meta name=\"msapplication-TileColor\" content=\"{}\">",
color
)); ));
self self
} }
pub fn with_ms_tile_image(mut self, image: &str) -> Self { pub fn with_ms_tile_image(mut self, image: &str) -> Self {
self.0.push(format!( self.0.push(format!(
"<meta name=\"msapplication-TileImage\" content=\"{}\">", image "<meta name=\"msapplication-TileImage\" content=\"{}\">",
image
)); ));
self self
} }
fn add_item( fn add_item(mut self, rel: &str, source: &str, sizes: &str, color: &str) -> Self {
mut self,
rel : &str,
source: &str,
sizes : &str,
color : &str
) -> Self {
let mut link: String = format!("<link rel=\"{}\"", rel); let mut link: String = format!("<link rel=\"{}\"", rel);
if let Some(i) = source.rfind('.') { if let Some(i) = source.rfind('.') {
link = match source[i..].to_owned().to_lowercase().as_str() { link = match source[i..].to_owned().to_lowercase().as_str() {
@ -63,7 +60,7 @@ impl Favicon {
".jpg" => format!("{} type=\"image/jpg\"", link), ".jpg" => format!("{} type=\"image/jpg\"", link),
".png" => format!("{} type=\"image/png\"", link), ".png" => format!("{} type=\"image/png\"", link),
".svg" => format!("{} type=\"image/svg+xml\"", link), ".svg" => format!("{} type=\"image/svg+xml\"", link),
_ => link _ => link,
}; };
} }
if !sizes.is_empty() { if !sizes.is_empty() {

View file

@ -1,32 +1,42 @@
// External re-exports. // EXTERNAL RE-EXPORTS.
pub use once_cell::sync::Lazy;
pub use concat_string::concat_string; pub use concat_string::concat_string;
pub use doc_comment::doc_comment; pub use doc_comment::doc_comment;
pub use once_cell::sync::Lazy;
// Local. // LOCAL.
#[allow(unused_imports)] #[allow(unused_imports)]
pub(crate) use futures::executor::block_on as run_now; pub(crate) use futures::executor::block_on as run_now;
// Public APIs. // PUBLIC APIs.
pub mod config; // Gestión de la configuración. // Gestión de la configuración.
pub mod trace; // Registro de trazas y eventos de la aplicación. pub mod config;
pub mod locale; // Localización. // Registro de trazas y eventos de la aplicación.
pub mod html; // HTML en código. pub mod trace;
// Localización.
pub mod locale;
// HTML en código.
pub mod html;
// Acceso a base de datos.
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))] #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
pub mod db; // Acceso a base de datos. pub mod db;
pub mod app; // Prepare and run the application. // Prepare and run the application.
pub mod app;
pub mod core; // Main APIs for components, hooks, modules and themes. // Main APIs for components, hooks, modules and themes.
pub mod core;
pub mod response; // Tipos de respuestas web. // Tipos de respuestas web.
pub mod base; // Base de componentes, módulos y temas. pub mod response;
pub mod util; // Macros y funciones útiles. // Base de componentes, módulos y temas.
pub mod base;
// Macros y funciones útiles.
pub mod util;
// Internal re-exports. // INTERNAL RE-EXPORTS.
pub mod prelude; pub mod prelude;

View file

@ -1,6 +1,6 @@
pub use fluent_templates; pub use fluent_templates;
pub use fluent_templates::{Loader as Locale, static_loader as static_locale};
pub use fluent_templates::fluent_bundle::FluentValue; pub use fluent_templates::fluent_bundle::FluentValue;
pub use fluent_templates::{static_loader as static_locale, Loader as Locale};
#[macro_export] #[macro_export]
/// Permite integrar fácilmente localización en temas, módulos y componentes. /// Permite integrar fácilmente localización en temas, módulos y componentes.

View file

@ -1,41 +1,26 @@
//! Re-exporta recursos comunes. //! Re-exporta recursos comunes.
// Global macros and helpers. // Global macros and helpers.
pub use crate::{ pub use crate::{args, concat_string, theme_static_files, util};
args,
concat_string,
theme_static_files,
util,
};
pub use crate::config::SETTINGS; pub use crate::config::SETTINGS;
pub use crate::trace; pub use crate::trace;
pub use crate::localize; pub use crate::localize;
pub use crate::html::*; pub use crate::html::*;
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))] #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
pub use crate::{ pub use crate::{db, db::*, migration_item, pub_migration};
db,
db::*,
pub_migration,
migration_item,
};
pub use crate::app; pub use crate::app;
pub use crate::app::AppTrait;
pub use crate::app::application::Application; pub use crate::app::application::Application;
pub use crate::app::AppTrait;
pub use crate::core::{ pub use crate::core::{component::*, hook::*, module::*, theme::*};
component::*,
hook::*,
module::*,
theme::*,
};
pub use crate::{
hook_action,
hook_before_render_component,
};
pub use crate::{hook_action, hook_before_render_component};
pub use crate::response::page::*; pub use crate::response::page::*;

View file

@ -1,8 +1,5 @@
mod hook; mod hook;
pub use hook::{ pub use hook::{BeforeRenderPageHook, HOOK_BEFORE_RENDER_PAGE};
HOOK_BEFORE_RENDER_PAGE,
BeforeRenderPageHook,
};
mod definition; mod definition;
pub use definition::Page; pub use definition::Page;

View file

@ -1,9 +1,9 @@
use crate::{Lazy, app, trace}; use super::{BeforeRenderPageHook, HOOK_BEFORE_RENDER_PAGE};
use crate::config::SETTINGS; use crate::config::SETTINGS;
use crate::html::*;
use crate::core::hook::{action_ref, run_actions};
use crate::core::component::*; use crate::core::component::*;
use super::{HOOK_BEFORE_RENDER_PAGE, BeforeRenderPageHook}; use crate::core::hook::{action_ref, run_actions};
use crate::html::*;
use crate::{app, trace, Lazy};
use std::collections::HashMap; use std::collections::HashMap;
@ -34,7 +34,11 @@ static DEFAULT_DIRECTION: Lazy<Option<String>> = Lazy::new(|| {
} }
}); });
pub enum TextDirection { Auto, LeftToRight, RightToLeft } pub enum TextDirection {
Auto,
LeftToRight,
RightToLeft,
}
pub struct Page { pub struct Page {
context : InContext, context : InContext,
@ -48,7 +52,6 @@ pub struct Page {
} }
impl Page { impl Page {
pub fn new() -> Self { pub fn new() -> Self {
Page { Page {
context : InContext::new(), context : InContext::new(),
@ -100,15 +103,12 @@ impl Page {
self self
} }
pub fn add_to( pub fn add_to(mut self, region: &'static str, component: impl ComponentTrait) -> Self {
mut self,
region: &'static str,
component: impl ComponentTrait
) -> Self {
if let Some(regions) = self.regions.get_mut(region) { if let Some(regions) = self.regions.get_mut(region) {
regions.add(component); regions.add(component);
} else { } else {
self.regions.insert(region, ComponentsBundle::new_with(component)); self.regions
.insert(region, ComponentsBundle::new_with(component));
} }
self self
} }
@ -193,10 +193,9 @@ impl Page {
pub fn render(&mut self) -> app::Result<Markup> { pub fn render(&mut self) -> app::Result<Markup> {
// Acciones de los módulos antes de renderizar la página. // Acciones de los módulos antes de renderizar la página.
run_actions( run_actions(HOOK_BEFORE_RENDER_PAGE, |hook| {
HOOK_BEFORE_RENDER_PAGE, action_ref::<BeforeRenderPageHook>(&**hook).run(self)
|hook| action_ref::<BeforeRenderPageHook>(&**hook).run(self) });
);
// Acciones del tema antes de renderizar la página. // Acciones del tema antes de renderizar la página.
self.context.theme().before_render_page(self); self.context.theme().before_render_page(self);
@ -208,7 +207,7 @@ impl Page {
let head = self.context.theme().render_page_head(self); let head = self.context.theme().render_page_head(self);
// Finalmente, renderizar la página. // Finalmente, renderizar la página.
return Ok(html! { Ok(html! {
(DOCTYPE) (DOCTYPE)
html lang=[self.language().get()] dir=[self.direction().get()] { html lang=[self.language().get()] dir=[self.direction().get()] {
(head) (head)
@ -220,10 +219,9 @@ impl Page {
pub fn render_region(&mut self, region: &str) -> Markup { pub fn render_region(&mut self, region: &str) -> Markup {
match self.regions.get_mut(region) { match self.regions.get_mut(region) {
Some(components) => components.render(&mut self.context), Some(components) => components.render(&mut self.context),
None => html! {} None => html! {},
} }
} }
// Page EXTRAS. // Page EXTRAS.
} }

View file

@ -1,5 +1,5 @@
use crate::core::hook::{HookTrait, AnyHook};
use super::Page; use super::Page;
use crate::core::hook::{AnyHook, HookTrait};
pub const HOOK_BEFORE_RENDER_PAGE: &str = "pagetop::hook::before_render_page"; pub const HOOK_BEFORE_RENDER_PAGE: &str = "pagetop::hook::before_render_page";

View file

@ -1,2 +1,2 @@
pub use tracing::{Level, event, span};
pub use tracing::{debug, error, info, trace, warn}; pub use tracing::{debug, error, info, trace, warn};
pub use tracing::{event, span, Level};

View file

@ -23,15 +23,11 @@ macro_rules! theme_static_files {
( $cfg:ident, $dir:expr ) => {{ ( $cfg:ident, $dir:expr ) => {{
let static_files = &$crate::config::SETTINGS.dev.static_files; let static_files = &$crate::config::SETTINGS.dev.static_files;
if static_files.is_empty() { if static_files.is_empty() {
$cfg.service(actix_web_static_files::ResourceFiles::new( $cfg.service(actix_web_static_files::ResourceFiles::new($dir, generate()));
$dir,
generate()
));
} else { } else {
$cfg.service(actix_files::Files::new( $cfg.service(
$dir, actix_files::Files::new($dir, &[static_files, $dir].join("")).show_files_listing(),
&[static_files, $dir].join("") );
).show_files_listing());
} }
}}; }};
} }

View file

@ -2,8 +2,7 @@ use pagetop::prelude::*;
struct PageTopWebSite; struct PageTopWebSite;
impl AppTrait for PageTopWebSite { impl AppTrait for PageTopWebSite {}
}
#[actix_web::main] #[actix_web::main]
async fn main() -> std::io::Result<()> { async fn main() -> std::io::Result<()> {