Shiny: conditionally build UI - r

I'm building a Shiny dashboard to show a large amount of data. People access the dashboard through a separate login page (non-Shiny) that sits in front, at which point a JWT is generated and put in a cookie. I've managed to read in the cookie in Shiny and parse the data, saving the token in a variable called userPermissions. Now, however, I'd like to show/hide tabs in the dashboard depending on the user permissions.
For example: I have data on both managers and assistants. I want to show the manager data to any user with userPermissions == 'manager', and the assistant data to anyone with userPermissions == assistant.
I thought the best way would be to use conditionalPanel() – here's a (simplified) reproducible example:
library(shiny)
# UI file
ui <- fluidPage(
# JS to read cookie -- simplified to just return value!
tags$head(tags$script(
HTML('
Shiny.addCustomMessageHandler("goReadTheCookie", function (message) {
Shiny.onInputChange("cookie", "manager");
})
')
)
# Title
,titlePanel('Test')
# Navbar
,navbarPage(
id="navbar"
,conditionalPanel(condition = "userPermissions() == 'manager'",
mainPanel(textOutput('Manager data')))
,conditionalPanel(condition = "userPermissions() == 'assistant'",
mainPanel(textOutput('Assistant data')))
)
))
# Server file
server <- shinyServer(function(input, output,session){
## Grab permissions from Cookie
# Prepare output
userPermissions <- reactiveVal("")
# Tell JS to return cookie
session$sendCustomMessage(type="goReadTheCookie", message=list(name="cookie_name"))
# Listen for cookie
observeEvent(input$cookie,{
## -- Bunch of code omitted for sake of brevity -- ##
userPermissions("manager")
})
})
# Run app
shinyApp(ui=ui, server=server)
The problem with this code is that, in a browser console, I get the error Can't find variable: userPermissions.
I guess what's going on here is that the entire ui is executed, before JS can grab and return the cookie. What would be the best way to solve this?
Or, maybe I'm going about this the wrong way. I obviously need to verify the cookie server-side (i.e., in R) not to divulge the secret; and preferably this check, and the hiding/showing is completed at the very start of the Shiny application (userPermissions won't change during the session). Maybe there's a different (& better) solution to get to that point?
Any help would be very much appreciated!

In the end I found that the function appendTab does exactly what I was looking for. This needs to be run in server.R though, within the function to look for the cookie (otherwise userPermissions indeed doesn't exist). I could then do:
appendTab("navbar" # the id of the navigation bar created in ui.R
,tabPanel("tab name"
,mainPanel(...))
)
where tabPanel(...) could be anything you'd normally put in ui.R.
The added benefit here is that hidden tabs are also not available in the HTML source, as they're never even passed from the server to the client!

Related

how to connect to R Shiny website using programming

We have a R shiny based website where we input some parameters and get some results. We can do it manually, but considering many actions are repeat, we want write some R code to automatically pull out the results. Look looks we cannot use some simple REST request, because the HTTP request used some information like "nonce" and "session". Also I am not familiar with the JavaScript. Could someone help me to understand how to do it?
There is a functionality session$registerDataObj which allows you to add an API endpoint for a specific shiny session. This will make the provided data available at http://{host}/session/{session_id}/dataobj/{name}.
The minimal app below invokes the produced endpoint via browseURL(). See ?shiny::session for more details.
shinyApp(
ui = fluidPage(),
server = function(input, output, session) {
name <- "iris"
session$registerDataObj(
name = name,
data = iris,
filterFunc = function(data, req) {
httpResponse(content = paste('nrow: ', nrow(data)))
}
)
uri <- paste0("http://", session$request$HTTP_HOST, "/session/",
session$token, "/dataobj/", name)
browseURL(print(uri))
}
)
#> Listening on http://127.0.0.1:5130
#> [1] "http://127.0.0.1:5130/session/fd47ff29025bbe4d9dbf922b935186b3/dataobj/iris"
This should open up a new browser tab that shows: "nrow: 150". The return value of session$registerDataObj() can also be used to create the uri with additional query parameters.
But I personally think it is better to use a database in 99% of all usecases. This would mean that there is an export button in your ui which causes the app server to write all relevant results into the database and the database is then accessed by another process that needs the app data.

how should I access shiny user's session info?

I'm wondering if there's any way I can access user log for my Shiny App.
Currently, I use the code below to get who logged in and when did this person log off.
However, I'd prefer to know the time the user logged in so that I'm able to know how long does the user using the app.
session$onSessionEnded(function(){
UserInfo <- data.frame(
LoginName = session$user,
Time = as.character(Sys.time())
)
Plus, I understand that Google Analytics can easily access this kind of info, but I do prefer a 'shiny' way to solve it.
I've also tried to use `session$clientData' as the document says it's used for "Getting Non-Input Data From the Client", but I don't know how could I get the login time.
Does anyone have any idea on how could I achieve this? Thanks in advance!
Per the shiny scoping rules, everything inside server <- function(input, output, session) is per-session.
server <- function(input, output, session) {
# everything in here is run once per-session, so it should run as soon as
# a user starts using the app
started <- Sys.time()
# ... reactives here ...
session$onSessionEnded(function() {
UserInfo <- data.frame(
LoginName = session$user,
Time = as.character(Sys.time())
)
# ... do something with UserInfo ...
})
In fact, the scoping rules suggest exactly this, but they named it startTime instead. (Hard things: cache invalidation and variable naming.)

Limiting the number of users in a locally hosted R shiny app

I'd like to limit the number of users of my locally hosted r shiny app to one user at any one time.
So ideally when a second user attempted to run the app at the same time (users access the app by typing the local IP into the address field) the app would display a default message and stop any further progress. Nullifying any other user commands may not matter if the only thing shown upon entry is this denial message.
The content of the app doesn't matter so we can use this app as an example: http://shiny.rstudio.com/gallery/tabsets.html
Thanks for any help or info you can give.
I wouldn't recommend doing this, I think it's very dangerous, but there are ways you could hack this together. Here's one solution (as I said, it's hacky and I wouldn't do it myself). The basic idea is to have a global variable that keeps track of whether or not someone is using the app. If nobody is using the app, show the app and turn on the flag and make sure to turn off the flag when the user exits.
shinyBusy <- FALSE
runApp(shinyApp(
ui = fluidPage(
shinyjs::useShinyjs(),
shinyjs::hidden(
h1(id = "busyMsg", "App is busy")
),
shinyjs::hidden(
div(
id = "app",
p("Hello!")
)
)
),
server = function(input, output, session) {
if (shinyBusy) {
shinyjs::show("busyMsg")
} else {
shinyBusy <<- TRUE
session$onSessionEnded(function() shinyBusy <<- FALSE)
shinyjs::show("app")
}
}
),
launch.browser = TRUE)
Note: in order to show/hide app elements, I'm using a package that I wrote shinyjs

Shiny Issue - Tracking User Settings in Multiuser Environment

I asked this question on the Shiny user group, but haven't been able to get a response, so I'm posting it here as well.
I have an app that needs to track a user's preference, with the possibility that several users may be using the app simultaneously. For simplicity, let's say I have a list to contain a user's settings stored within the shiny server function. It's a long list (1000 elements) that contains attributes based on the user's interaction with the app. The user can in effect change any index of this list to one of hundreds of possible settings. My initial solution was something like this:
shinyServer(function(input, output, session) {
settings <<- rep("A",1000)
observe({
input$changeSettingsButton
settings[input$changeIndex] <<- input$newSetting
})
}
Which works great unless you have multiple people using the app at the same time, because the <<- creates a global, shared variable across sessions. Is there a good way to do this?
You can use reactiveValues to store persistent user info. Something like
shinyServer(function(input, output, session) {
myReactives <- reactiveValues(settings = rep("A",1000))
observe({
input$changeSettingsButton
isolate(myReactives$settings[input$changeIndex] <- input$newSetting)
})
}
may work for you.

Execute a function in background when clicking a button with no output being changed (Shiny)

I want to be able to click a button within my Shiny App. So far so easy.
But when doing so, a function that loads, writes and then saves an excel sheet should be executed "behind the scenes" with no output changed in the App itself.
How can I do this?
Kind regards,
Martin
Yes. Try to use an Observer.
in server.R:
shinyServer(function(input, output, session) {
# other code ...
observe({
# do_something_button is your actionButton id.
if (input$do_something_button > 0) {
# perform behind the scenes analysis here.
# if you want to send a message to the client side, you can try
# something like:
message <- list(type="completed", excel_file=saved_file)
session$sendCustomMesage("background_task", message)
}
})
# other code ...
})
then write some custom javascript snippet and load it via ui.R:
Shiny.addCustomMessageHandler("background_task", function(message) {
console.log("Finished processing: " + message.excel_file);
});
In this way when the server completes the background processing, or should an error occur, you can get some feedback on the client side. Maybe you want to notify the user about an error or something.
The reference for using observe is here

Resources