Spaces:
Sleeping
Sleeping
if(!"rlang" %in% installed.packages()){ | |
if(!interactive()) { stop("The package \"rlang\" is required.") } | |
cat("The package \"rlang\" is required.\n✖ Would you like to install it?\n\n1: Yes\n2: No\n\nSelection:") | |
if (readLines(n = 1) == "1"){ | |
install.packages("rlang") | |
} | |
} | |
rlang::check_installed("remotes") | |
rlang::check_installed("ambhtmx", action = \(pkg, ... ) remotes::install_github("devOpifex/ambhtmx")) | |
rlang::check_installed("ambiorix", action = \(pkg, ... ) remotes::install_github("devOpifex/ambiorix")) | |
rlang::check_installed("scilis", action = \(pkg, ... ) remotes::install_github("devOpifex/scilis")) | |
rlang::check_installed("signaculum", action = \(pkg, ... ) remotes::install_github("devOpifex/signaculum")) | |
rlang::check_installed("tidyverse") | |
rlang::check_installed("zeallot") | |
rlang::check_installed("glue") | |
rlang::check_installed("htmltools") | |
rlang::check_installed("this.path") | |
options( | |
'ambiorix.host'=Sys.getenv('AMBHTMX_HOST'), | |
'ambiorix.port'=Sys.getenv('AMBHTMX_PORT') | |
) | |
# remotes::install_github("jrosell/ambhtmx", force = TRUE) | |
library(ambhtmx) | |
library(zeallot) | |
page_title <- "Password protected CRUD (Create, Read, Update, and Delete) example with ambhtmx" | |
render_index <- \() { | |
main <- NULL | |
tryCatch({ | |
index <- p("Add your first item.") | |
item_rows <- items$read_rows() | |
if(nrow(item_rows) > 0) { | |
index <- item_rows |> | |
rowwise() |> | |
group_split() |> | |
map(\(item) { | |
tags$li( | |
tags$a( | |
item$name, | |
href = glue("/items/{item$id}"), | |
`hx-get`= glue("/items/{item$id}"), | |
`hx-target` = "#main", | |
`hx-swap` = "innerHTML" | |
) | |
) | |
}) | |
} | |
main <- div(id = "page", style = "margin: 50px", | |
div(style ="float:right", id = "logout", button("Logout", onclick = "void(location.href='/logout')")), | |
h1(page_title), | |
div(id = "main", style = "margin-top: 20px", tagList( | |
h2("Index of items"), | |
index, | |
button( | |
"New item", | |
style = "margin-top:20px", | |
`hx-get` = "/items/new", | |
`hx-target` = "#main", | |
`hx-swap` = "innerHTML" | |
) | |
)) | |
) | |
}, | |
error = \(e) print(e) | |
) | |
return(main) | |
} | |
render_new <- \(req, res) { | |
errors <- process_error_get(req, res) | |
render_tags(tagList( | |
h2("New item"), | |
div(label("Name", p(input(name = "name")))), | |
div(label("Content", p(textarea(name = "content")))), | |
a( | |
"Go back", | |
href = "/", | |
style = "margin-right:20px", | |
`hx-confirm` = "Are you sure you want to go back?", | |
`hx-get` = "/items", | |
`hx-target` = "#page", | |
`hx-swap` = "outerHTML", | |
`hx-encoding` = "multipart/form-data" | |
), | |
button( | |
"Create", | |
style = "margin-top:20px", | |
`hx-post` = "/items", | |
`hx-target` = "#page", | |
`hx-swap` = "outerHTML", | |
`hx-include` = "[name='name'], [name='content']", | |
), | |
errors | |
)) | |
} | |
render_row <- \(item) { | |
tags$div( | |
tags$strong(item$name), | |
tags$br(), | |
HTML(item$content) | |
) | |
} | |
#' Starting the app | |
counter <- 0 | |
c(app, context, items) %<-% | |
ambhtmx_app( | |
dbname = getOption("ambiorix.dbname") %||% "items.sqlite", | |
host = getOption("ambiorix.host") %||% "127.0.0.1", | |
port = getOption("ambiorix.port")%||% "3000", | |
value = tibble( | |
id = character(1), | |
name = character(1), | |
content = character(1) | |
), | |
render_index = render_index, | |
render_row = render_row | |
) | |
#' Authentication feature with secret cookies and .Renviron variables | |
app$get("/login", \(req, res) { | |
process_login_get(req, res) | |
}) | |
app$post("/login", \(req, res) { | |
process_login_post(req, res) | |
}) | |
app$get("/logout", \(req, res) { | |
process_logout_get(req, res) | |
}) | |
app$use(\(req, res){ | |
process_loggedin_middleware(req, res) | |
}) | |
#' Some CRUD operations examples | |
cat("\nBe sure is initially empty:\n") | |
walk(items$read_rows()$id, \(x) items$delete_row(id = x)) | |
items$read_rows() |> print() | |
cat("\nAdd some items:\n") | |
tibble(name = "Elis elis", content = "Putxinelis.",) |> | |
items$add_row() -> first_id | |
tibble(name = "Que bombolles", content = "T\'empatolles.") |> | |
items$add_row() -> some_id | |
tibble(name = "Holi", content = "Guapi.") |> | |
items$add_row() -> last_id | |
items$read_rows() |> print() | |
cat("\nDelete last item:\n") | |
items$delete_row(id = last_id) | |
items$read_rows() |> print() | |
cat("\nUpdate first items:\n") | |
tibble(name = "First", content = "Hello in <span style='color:red'>red</span>.") |> | |
items$update_row(id = first_id) | |
items$read_rows() |> print() | |
cat("\nRender the first item:\n") | |
items$read_row(id = first_id) |> | |
items$read_row() |> | |
as.character() |> | |
cat() | |
cat("\nAdd an item with id 1:\n") | |
tibble(id = "1", name = "Quines postres", content = "Tant bones.") |> | |
items$add_row() | |
items$read_rows() |> print() | |
#' The main page | |
app$get("/", \(req, res){ | |
if (!req$loggedin) { | |
return(res$redirect("/login", status = 302L)) | |
} | |
html <- "" | |
tryCatch({ | |
html <- render_page( | |
page_title = page_title, | |
main = items$render_index() | |
) | |
}, | |
error = \(e) print(e) | |
) | |
res$send(html) | |
}) | |
#' Read the index of the items | |
app$get("/items", \(req, res){ | |
if (!req$loggedin) { | |
return(res$redirect("/login", status = 302L)) | |
} | |
res$send(items$render_index()) | |
}) | |
#' New item form | |
app$get("/items/new", \(req, res){ | |
if (!req$loggedin) { | |
return(res$redirect("/login", status = 302L)) | |
} | |
tryCatch({ | |
html <- render_new(req, res) | |
}, | |
error = \(e) print(e) | |
) | |
res$send(html) | |
}) | |
#' Show an existing item | |
app$get("/items/:id", \(req, res){ | |
if (!req$loggedin) { | |
return(res$redirect("/login", status = 302L)) | |
} | |
item_id <- req$params$id %||% "" | |
item <- items$read_row(id = item_id) | |
html <- render_tags(tagList( | |
h2("Show item details"), | |
items$render_row(item), | |
a( | |
"Go back", | |
href = "/", | |
style = "margin-right:20px", | |
`hx-get` = "/items", | |
`hx-target` = "#page", | |
`hx-swap` = "outerHTML", | |
), | |
a( | |
"Delete", | |
href = "/", | |
style = "color: red; margin-right:20px", | |
`hx-confirm` = "Are you sure you want to delete the item?", | |
`hx-delete` = glue("/items/{item$id}"), | |
`hx-target` = "#page", | |
`hx-swap` = "outerHTML", | |
`hx-encoding` = "multipart/form-data" | |
), | |
button( | |
"Edit", | |
style = "margin-top:20px", | |
`hx-get` = glue("/items/{item_id}/edit"), | |
`hx-target` = "#main", | |
`hx-swap` = "innerHTML" | |
) | |
)) | |
res$send(html) | |
}) | |
#' Edit item form | |
app$get("/items/:id/edit", \(req, res){ | |
if (!req$loggedin) { | |
return(res$redirect("/login", status = 302L)) | |
} | |
item_id <- req$params$id %||% "" | |
item <- items$read_row(id = item_id) | |
html <- render_tags(tagList( | |
h2("Edit item"), | |
input(type = "hidden", name = "id", value = item$id), | |
div(label("Name", p(input(name = "name", value = item$name)))), | |
div(HTML(glue('<textarea rows=5 name="content">{item$content}</textarea>'))), | |
a( | |
"Go back", | |
href = "/", | |
style = "margin-right:20px", | |
`hx-confirm` = "Are you sure you want to go back?", | |
`hx-get` = "/items", | |
`hx-target` = "#page", | |
`hx-swap` = "outerHTML", | |
`hx-encoding` = "multipart/form-data" | |
), | |
button( | |
"Update", | |
style = "margin-top:20px", | |
`hx-put` = glue("/items/{item$id}"), | |
`hx-target` = "#page", | |
`hx-swap` = "outerHTML", | |
`hx-include` = "[name='name'], [name='content']", | |
) | |
)) | |
res$send(html) | |
}) | |
#' Create a new item | |
app$post("/items", \(req, res){ | |
if (!req$loggedin) { | |
return(res$redirect("/login", status = 302L)) | |
} | |
params <- parse_multipart(req) | |
if (is.null(params[["name"]])) { | |
error_message <- "Name is required." | |
res$cookie( | |
name = "errors", | |
value = error_message | |
) | |
res$header("HX-Retarget", "#main") | |
res$header("HX-Reswap", "innerHTML") | |
print("Retarget amb error") | |
return(res$send(render_new(req, res))) | |
} | |
if (is.null(params[["content"]])) { | |
params[["content"]] = "" | |
} | |
tryCatch({ | |
params |> | |
as_tibble() |> | |
items$add_row() | |
}, | |
error = \(e) print(e) | |
) | |
res$send(items$render_index()) | |
}) | |
#' Update an existing item | |
app$put("/items/:id", \(req, res){ | |
if (!req$loggedin) { | |
return(res$redirect("/login", status = 302L)) | |
} | |
item_id <- req$params$id %||% "" | |
params <- parse_multipart(req) |> | |
as_tibble() |> | |
mutate(id = item_id) | |
item <- items$read_row(id = item_id) | |
tryCatch({ | |
item |> | |
dplyr::rows_upsert(params, by = "id") |> | |
items$update_row() | |
}, | |
error = \(e) print(e) | |
) | |
res$send(items$render_index()) | |
}) | |
#' Delete an existing item | |
app$delete("/items/:id", \(req, res){ | |
if (!req$loggedin) { | |
return(res$redirect("/login", status = 302L)) | |
} | |
item_id <- req$params$id %||% "" | |
items$delete_row(id = item_id) | |
res$send(items$render_index()) | |
}) | |
#' Start the app with all the previous defined routes | |
app$start() |