Display R help page in shiny app

Dear RStudio community,

I'd like the users of my shiny applications having the possibility to browse through R's help page to e.g. view the used R packages, authors and licenses.

I'm able to display the help page in an iframe just fine (please see the reprex below), however this works, only on the localhost due to the fact that:

This function starts the internal HTTP server, which runs on the loopback interface (127.0.0.1).

see ?startDynamicHelp.

Does anyone know how to host this help page for the local network (without using the internet - like the host = "0.0.0.0" argument for a shiny app).

I know that I could use addResourcePath() to host the files directly via shiny - but my attempts with this approach only worked partly.

library(shiny)

helpPort <- tools::startDynamicHelp(NA)
helpURL <- paste0("http://127.0.0.1:", helpPort, "/doc/html/index.html")

ui <- fluidPage(
  tags$style(type = "text/css", "#helpPageIframe {height: calc(100vh - 80px) !important;}"),
  htmlOutput("helpPageIframe")
)

server <- function(input, output, session) {
  output$helpPageIframe <- renderUI({
    tags$iframe(src=helpURL, height="100%", width="100%", frameborder="0", scrolling="yes")
  })
}

shinyApp(ui, server)
# app <- shinyApp(ui, server)
# runApp(app, host = "0.0.0.0", port = 3838, launch.browser = TRUE)

The package.html file in the local R installation can be updated via make.packages.html(temp = FALSE)

I've found some related links:

https://r.789695.n4.nabble.com/Use-of-tools-httpdPort-in-a-package-for-CRAN-td4700440.html

It's going to be quite difficult to do this — you'll need to effectively proxy the requests coming into the shiny app to the sever that startDynamicHelp() creates. I would suggest thinking about another way to attack your problem.

This topic was automatically closed 54 days after the last reply. New replies are no longer allowed.

@hadley thank you very much for taking the time to answer here.

By now I managed to find a solution, which seems to solve the problem for my usecase.

I modified the function startDynamicHelp to accept a host argument.
Furthermore, I'm rendering the iframe based on session$clientData$url_hostname, which is giving me the networkinterface used to access the app.

So far my tests were working fine in the local network. Please check the following reprex and let me know if you have any comments.

Cheers!

library(shiny)

startDynamicHelpHost <- function (start = NA, host = "127.0.0.1")
{
  if (nzchar(Sys.getenv("R_DISABLE_HTTPD"))) {
    tools:::httpdPort(-1L)
    warning("httpd server disabled by R_DISABLE_HTTPD", 
            immediate. = TRUE)
    utils::flush.console()
    return(invisible(tools:::httpdPort()))
  }
  port <- tools:::httpdPort()
  if (is.na(start)) {
    if (port <= 0L) 
      return(startDynamicHelpHost(start = TRUE, host))
    return(invisible(port))
  }
  if (start && port) {
    if (port > 0L) 
      stop("server already running")
    else stop("server could not be started on an earlier attempt")
  }
  if (!start && (port <= 0L)) 
    stop("no running server to stop")
  if (start) {
    utils::flush.console()
    OK <- FALSE
    ports <- getOption("help.ports")
    if (is.null(ports)) {
      ports <- 10000 + 22000 * ((stats::runif(10) + unclass(Sys.time())/300)%%1)
    }
    ports <- as.integer(ports)
    if (all(ports == 0)) 
      return(invisible(0))
    message("starting httpd help server ...", appendLF = FALSE)
    for (i in seq_along(ports)) {
      status <- .Call(tools:::C_startHTTPD, host, 
                      ports[i])
      if (status == 0L) {
        OK <- TRUE
        tools:::httpdPort(ports[i])
        break
      }
      if (status != -2L) 
        break
    }
    if (OK) {
      message(" done")
      utils::flush.console()
    }
    else {
      warning("failed to start the httpd server", 
              immediate. = TRUE)
      utils::flush.console()
      tools:::httpdPort(-1L)
    }
  }
  else {
    .Call(C_stopHTTPD)
    tools:::httpdPort(0L)
  }
  invisible(tools:::httpdPort())
}

# try to shut down the dynamic help server if it is running
tryCatch({
  tools::startDynamicHelp(start = FALSE)
}, error = function(e){return(e)})

helpPort <- startDynamicHelpHost(start = NA, host = "0.0.0.0")

ui <- fluidPage(
  tags$style(type = "text/css", "#helpPageIframe {height: calc(100vh - 80px) !important;}"),
  htmlOutput("helpPageIframe")
)

server <- function(input, output, session) {
  helpURL <- reactive({
    if (helpPort > 0L) {
      paste0("http://", session$clientData$url_hostname, ":", helpPort, "/doc/html/index.html")
    } else { NULL }
  })
  
  output$helpPageIframe <- renderUI({
    tags$iframe(src=helpURL(), height="100%", width="100%", frameborder="0", scrolling="yes")
  })
}

app <- shinyApp(ui, server)
runApp(app, host = "0.0.0.0", port = 3838, launch.browser = TRUE)