1.globals$options <- list() 2 3#' @param name Name of an option to get. 4#' @param default Value to be returned if the option is not currently set. 5#' @rdname shinyOptions 6#' @export 7getShinyOption <- function(name, default = NULL) { 8 # Make sure to use named (not numeric) indexing 9 name <- as.character(name) 10 11 # Check if there's a current session 12 session <- getDefaultReactiveDomain() 13 if (!is.null(session)) { 14 if (name %in% names(session$options)) { 15 return(session$options[[name]]) 16 } else { 17 return(default) 18 } 19 } 20 21 # Check if there's a current app 22 app_state <- getCurrentAppState() 23 if (!is.null(app_state)) { 24 if (name %in% names(app_state$options)) { 25 return(app_state$options[[name]]) 26 } else { 27 return(default) 28 } 29 } 30 31 # If we got here, look in global options 32 if (name %in% names(.globals$options)) { 33 return(.globals$options[[name]]) 34 } else { 35 return(default) 36 } 37} 38 39#' Get or set Shiny options 40#' 41#' @description 42#' 43#' There are two mechanisms for working with options for Shiny. One is the 44#' [options()] function, which is part of base R, and the other is the 45#' `shinyOptions()` function, which is in the Shiny package. The reason for 46#' these two mechanisms is has to do with legacy code and scoping. 47#' 48#' The [options()] function sets options globally, for the duration of the R 49#' process. The [getOption()] function retrieves the value of an option. All 50#' shiny related options of this type are prefixed with `"shiny."`. 51#' 52#' The `shinyOptions()` function sets the value of a shiny option, but unlike 53#' `options()`, it is not always global in scope; the options may be scoped 54#' globally, to an application, or to a user session in an application, 55#' depending on the context. The `getShinyOption()` function retrieves a value 56#' of a shiny option. Currently, the options set via `shinyOptions` are for 57#' internal use only. 58#' 59#' @section Options with `options()`: 60#' 61#' \describe{ 62#' \item{shiny.autoreload (defaults to `FALSE`)}{If `TRUE` when a Shiny app is launched, the 63#' app directory will be continually monitored for changes to files that 64#' have the extensions: r, htm, html, js, css, png, jpg, jpeg, gif. If any 65#' changes are detected, all connected Shiny sessions are reloaded. This 66#' allows for fast feedback loops when tweaking Shiny UI. 67#' 68#' Since monitoring for changes is expensive (we simply poll for last 69#' modified times), this feature is intended only for development. 70#' 71#' You can customize the file patterns Shiny will monitor by setting the 72#' shiny.autoreload.pattern option. For example, to monitor only ui.R: 73#' `options(shiny.autoreload.pattern = glob2rx("ui.R"))` 74#' 75#' The default polling interval is 500 milliseconds. You can change this 76#' by setting e.g. `options(shiny.autoreload.interval = 2000)` (every 77#' two seconds).} 78#' \item{shiny.deprecation.messages (defaults to `TRUE`)}{This controls whether messages for 79#' deprecated functions in Shiny will be printed. See 80#' [shinyDeprecated()] for more information.} 81#' \item{shiny.error (defaults to `NULL`)}{This can be a function which is called when an error 82#' occurs. For example, `options(shiny.error=recover)` will result a 83#' the debugger prompt when an error occurs.} 84#' \item{shiny.fullstacktrace (defaults to `FALSE`)}{Controls whether "pretty" (`FALSE`) or full 85#' stack traces (`TRUE`) are dumped to the console when errors occur during Shiny app execution. 86#' Pretty stack traces attempt to only show user-supplied code, but this pruning can't always 87#' be done 100% correctly.} 88#' \item{shiny.host (defaults to `"127.0.0.1"`)}{The IP address that Shiny should listen on. See 89#' [runApp()] for more information.} 90#' \item{shiny.jquery.version (defaults to `3`)}{The major version of jQuery to use. 91#' Currently only values of `3` or `1` are supported. If `1`, then jQuery 1.12.4 is used. If `3`, 92#' then jQuery `r version_jquery` is used.} 93#' \item{shiny.json.digits (defaults to `16`)}{The number of digits to use when converting 94#' numbers to JSON format to send to the client web browser.} 95#' \item{shiny.launch.browser (defaults to `interactive()`)}{A boolean which controls the default behavior 96#' when an app is run. See [runApp()] for more information.} 97#' \item{shiny.maxRequestSize (defaults to 5MB)}{This is a number which specifies the maximum 98#' web request size, which serves as a size limit for file uploads.} 99#' \item{shiny.minified (defaults to `TRUE`)}{By default 100#' Whether or not to include Shiny's JavaScript as a minified (`shiny.min.js`) 101#' or un-minified (`shiny.js`) file. The un-minified version is larger, 102#' but can be helpful for development and debugging.} 103#' \item{shiny.port (defaults to a random open port)}{A port number that Shiny will listen on. See 104#' [runApp()] for more information.} 105#' \item{shiny.reactlog (defaults to `FALSE`)}{If `TRUE`, enable logging of reactive events, 106#' which can be viewed later with the [reactlogShow()] function. 107#' This incurs a substantial performance penalty and should not be used in 108#' production.} 109#' \item{shiny.sanitize.errors (defaults to `FALSE`)}{If `TRUE`, then normal errors (i.e. 110#' errors not wrapped in `safeError`) won't show up in the app; a simple 111#' generic error message is printed instead (the error and strack trace printed 112#' to the console remain unchanged). If you want to sanitize errors in general, but you DO want a 113#' particular error `e` to get displayed to the user, then set this option 114#' to `TRUE` and use `stop(safeError(e))` for errors you want the 115#' user to see.} 116#' \item{shiny.stacktraceoffset (defaults to `TRUE`)}{If `TRUE`, then Shiny's printed stack 117#' traces will display srcrefs one line above their usual location. This is 118#' an arguably more intuitive arrangement for casual R users, as the name 119#' of a function appears next to the srcref where it is defined, rather than 120#' where it is currently being called from.} 121#' \item{shiny.suppressMissingContextError (defaults to `FALSE`)}{Normally, invoking a reactive 122#' outside of a reactive context (or [isolate()]) results in 123#' an error. If this is `TRUE`, don't error in these cases. This 124#' should only be used for debugging or demonstrations of reactivity at the 125#' console.} 126#' \item{shiny.testmode (defaults to `FALSE`)}{If `TRUE`, then various features for testing Shiny 127#' applications are enabled.} 128#' \item{shiny.trace (defaults to `FALSE`)}{Print messages sent between the R server and the web 129#' browser client to the R console. This is useful for debugging. Possible 130#' values are `"send"` (only print messages sent to the client), 131#' `"recv"` (only print messages received by the server), `TRUE` 132#' (print all messages), or `FALSE` (default; don't print any of these 133#' messages).} 134#' \item{shiny.autoload.r (defaults to `TRUE`)}{If `TRUE`, then the R/ 135#' of a shiny app will automatically be sourced.} 136#' \item{shiny.usecairo (defaults to `TRUE`)}{This is used to disable graphical rendering by the 137#' Cairo package, if it is installed. See [plotPNG()] for more 138#' information.} 139#' \item{shiny.devmode (defaults to `NULL`)}{Option to enable Shiny Developer Mode. When set, 140#' different default `getOption(key)` values will be returned. See [devmode()] for more details.} 141### Not documenting as 'shiny.devmode.verbose' is for niche use only 142# ' \item{shiny.devmode.verbose (defaults to `TRUE`)}{If `TRUE`, will display messages printed 143# ' about which options are being set. See [devmode()] for more details. } 144### (end not documenting 'shiny.devmode.verbose') 145#' } 146#' 147#' 148#' @section Scoping for `shinyOptions()`: 149#' 150#' There are three levels of scoping for `shinyOptions()`: global, 151#' application, and session. 152#' 153#' The global option set is available by default. Any calls to 154#' `shinyOptions()` and `getShinyOption()` outside of an app will access the 155#' global option set. 156#' 157#' When a Shiny application is run with [runApp()], the global option set is 158#' duplicated and the new option set is available at the application level. If 159#' options are set from `global.R`, `app.R`, `ui.R`, or `server.R` (but 160#' outside of the server function), then the application-level options will be 161#' modified. 162#' 163#' Each time a user session is started, the application-level option set is 164#' duplicated, for that session. If the options are set from inside the server 165#' function, then they will be scoped to the session. 166#' 167#' @section Options with `shinyOptions()`: 168#' 169#' There are a number of global options that affect Shiny's behavior. These 170#' can be set globally with `options()` or locally (for a single app) with 171#' `shinyOptions()`. 172#' 173#' \describe{ \item{cache}{A caching object that will be used by 174#' [renderCachedPlot()]. If not specified, a [cachem::cache_mem()] will be 175#' used.} } 176#' 177#' @param ... Options to set, with the form `name = value`. 178#' @aliases shiny-options 179#' @export 180shinyOptions <- function(...) { 181 newOpts <- list2(...) 182 183 if (length(newOpts) > 0) { 184 # If we're within a session, modify at the session level. 185 session <- getDefaultReactiveDomain() 186 if (!is.null(session)) { 187 # Modify session-level-options 188 session$options <- dropNulls(mergeVectors(session$options, newOpts)) 189 return(invisible(session$options)) 190 } 191 192 # If not in a session, but we have a currently running app, modify options 193 # at the app level. 194 app_state <- getCurrentAppState() 195 if (!is.null(app_state)) { 196 # Modify app-level options 197 app_state$options <- dropNulls(mergeVectors(app_state$options, newOpts)) 198 return(invisible(app_state$options)) 199 } 200 201 # If no currently running app, modify global options and return them. 202 .globals$options <- dropNulls(mergeVectors(.globals$options, newOpts)) 203 return(invisible(.globals$options)) 204 } 205 206 # If not setting any options, just return current option set, visibly. 207 208 session <- getDefaultReactiveDomain() 209 if (!is.null(session)) { 210 return(session$options) 211 } 212 213 app_state <- getCurrentAppState() 214 if (!is.null(app_state)) { 215 return(app_state$options) 216 } 217 218 return(.globals$options) 219} 220 221 222# Get specific shiny options and put them in a list, reset those shiny options, 223# and then return the options list. This should be during the creation of a 224# shiny app object. This function "consumes" the options when the shinyApp 225# object is created, so the options won't affect another app that is created 226# later. 227# 228# ==== Example ==== 229# shinyOptions(bookmarkStore = 1234) 230# # This now returns 1234. 231# getShinyOption("bookmarkStore") 232# 233# # Creating the app captures the bookmarkStore option and clears it. 234# s <- shinyApp( 235# fluidPage(verbatimTextOutput("txt")), 236# function(input, output) { 237# output$txt <- renderText(getShinyOption("bookmarkStore")) 238# } 239# ) 240# 241# # This now returns NULL. 242# getShinyOption("bookmarkStore") 243# 244# When running the app, the app will display "1234" 245# runApp(s) 246# 247# # After quitting the app, this still returns NULL. 248# getShinyOption("bookmarkStore") 249# ================== 250# 251# If another app had been created after s was created, but before s was run, 252# then it would capture the value of "bookmarkStore" at the time of creation. 253captureAppOptions <- function() { 254 options <- list( 255 appDir = getwd(), 256 bookmarkStore = getShinyOption("bookmarkStore") 257 ) 258 259 shinyOptions(appDir = NULL, bookmarkStore = NULL) 260 261 options 262} 263 264# Do the inverse of captureAppOptions. This should be called once the app is 265# started. 266applyCapturedAppOptions <- function(options) { 267 if (!is.null(options)) { 268 do.call(shinyOptions, options) 269 } 270} 271