| Title: | Add the 'Privatus Analytics' Tracking Script to 'shiny' Apps and Documents |
|---|---|
| Description: | Adds the cookieless 'Privatus Analytics' <https://privatusanalytics.com> tracking script to 'shiny' apps, 'R Markdown' and 'Quarto' documents, built with 'htmltools'. Also adds the HTML data attributes the script reads for custom events, click maps and page properties. The package only prints HTML. It never sends web requests or events from R and sets no cookies. |
| Authors: | James Andrews [aut, cre], Two Phase LLC [cph, fnd] |
| Maintainer: | James Andrews <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 1.0.0 |
| Built: | 2026-10-25 11:51:44 UTC |
| Source: | https://github.com/Privatus-Analytics/r |
Returns the tracking script as an htmltools::tagList(). Put it in the
<head> of your page: tags$head(privatus_analytics()) in a 'shiny' app,
or see use_privatus_header() for R Markdown and 'Quarto'.
privatus_analytics( site_id = getOption("privatus.site_id"), props = NULL, request = NULL, nonce = getOption("privatus.nonce"), modules = getOption("privatus.modules"), excluded_paths = getOption("privatus.excluded_paths"), domains = getOption("privatus.domains"), spa = getOption("privatus.spa"), manual = getOption("privatus.manual", FALSE), enabled = getOption("privatus.enabled"), extra_attributes = getOption("privatus.extra_attributes"), queue_stub = getOption("privatus.queue_stub", FALSE), script_host = getOption("privatus.script_host") )privatus_analytics( site_id = getOption("privatus.site_id"), props = NULL, request = NULL, nonce = getOption("privatus.nonce"), modules = getOption("privatus.modules"), excluded_paths = getOption("privatus.excluded_paths"), domains = getOption("privatus.domains"), spa = getOption("privatus.spa"), manual = getOption("privatus.manual", FALSE), enabled = getOption("privatus.enabled"), extra_attributes = getOption("privatus.extra_attributes"), queue_stub = getOption("privatus.queue_stub", FALSE), script_host = getOption("privatus.script_host") )
site_id |
Your site id. It starts with |
props |
Page props for this page, as a named list. They are sent with
every pageview and event on the page. They are merged over the
|
request |
Passed to a |
nonce |
A Content Security Policy nonce for the script tags, if your site uses one. |
modules |
Tracker modules to load, for example |
excluded_paths |
Path patterns the tracker never counts, for example
|
domains |
Hostnames to count. |
spa |
Single-page app mode: |
manual |
|
enabled |
|
extra_attributes |
A named list of any other |
queue_stub |
|
script_host |
Where |
When there is no site id, or when the tag is off (see
privatus_enabled()), it returns only the queue stub when queue_stub is
TRUE, and otherwise an empty tag list. Every argument defaults to an R option of
the same name with a privatus. prefix, so you can set them once with
options() and still change any of them for one page.
When there are page props, a small script is printed before the tracker. It
sets window.privatusPageProps, and the tracker tag gets
data-props="privatusPageProps". With queue_stub = TRUE the official
queue stub is printed first.
privatus_analytics(site_id = "pa_YOURSITEID", enabled = TRUE) privatus_analytics( site_id = "pa_YOURSITEID", enabled = TRUE, modules = c("engage", "auto"), props = list(plan = "pro", role = "admin") ) # In a Shiny app: # ui <- fluidPage(tags$head(privatus_analytics()), ...)privatus_analytics(site_id = "pa_YOURSITEID", enabled = TRUE) privatus_analytics( site_id = "pa_YOURSITEID", enabled = TRUE, modules = c("engage", "auto"), props = list(plan = "pro", role = "admin") ) # In a Shiny app: # ui <- fluidPage(tags$head(privatus_analytics()), ...)
A click map shows where people click on a page. Give the important parts a name so they stay easy to find, even when the page changes.
privatus_click(tag, name) privatus_click_data(name) privatus_click_attr(name)privatus_click(tag, name) privatus_click_data(name) privatus_click_attr(name)
tag |
An 'htmltools' tag, for example |
name |
The click map name, for example |
A name may use letters, digits and _ . : -, up to 100 characters.
Anything else gives nothing. Outside production (see
privatus_enabled()) you also get a warning, so you can fix the name.
privatus_click() adds data-privatus-click to an 'htmltools' tag.
privatus_click_data() returns it as a named list.
privatus_click_attr() returns it as an HTML string.
privatus_click() returns the tag, with the attribute added when
the name is valid. privatus_click_data() returns a named list (empty
for an invalid name). privatus_click_attr() returns an
htmltools::HTML() string (empty for an invalid name).
library(htmltools) privatus_click(tags$a(href = "/pricing", "See plans"), "pricing.cta") privatus_click_attr("pricing.cta")library(htmltools) privatus_click(tags$a(href = "/pricing", "See plans"), "pricing.cta") privatus_click_attr("pricing.cta")
The tag is on when enabled is TRUE and off when it is FALSE. When
enabled is NULL (the default), the tag is on only when the
PRIVATUS_ENV or R_CONFIG_ACTIVE environment variable is
"production". So your own clicking around while you build is never
counted.
privatus_enabled(enabled = getOption("privatus.enabled"))privatus_enabled(enabled = getOption("privatus.enabled"))
enabled |
|
R has no single setting for "this is the live site". If you deploy to
shinyapps.io or 'Posit Connect', set PRIVATUS_ENV=production in the
app's environment variables (or R_CONFIG_ACTIVE=production if you use
the 'config' package), or set options(privatus.enabled = TRUE).
TRUE or FALSE.
privatus_enabled() privatus_enabled(TRUE)privatus_enabled() privatus_enabled(TRUE)
The tracker reads these data- attributes in the browser and sends a
custom event when the element is clicked. On a <form> the event is sent
when the form is submitted. Your R code sends nothing.
privatus_event(tag, name, ..., .props = list()) privatus_event_data(name, ..., .props = list()) privatus_event_attrs(name, ..., .props = list())privatus_event(tag, name, ..., .props = list()) privatus_event_data(name, ..., .props = list()) privatus_event_attrs(name, ..., .props = list())
tag |
An 'htmltools' tag, for example |
name |
The event name. Use a short "Noun verb" name like
|
... |
Event props as |
.props |
A named list of more props. Props in |
privatus_event() adds the attributes to an 'htmltools' tag, such as
tags$button() or shiny::actionButton().
privatus_event_data() returns them as a named list, so you can splice
them into a tag with !!! or do.call().
privatus_event_attrs() returns them as an HTML string to put inside a
tag you write by hand, for example in an R Markdown document.
The name is trimmed and cut to 120 characters. An empty name gives
nothing. Prop keys are made lowercase, and anything that is not a letter, a
digit or _ becomes _, so "Report Type" becomes report_type. Keys
are cut to 64 characters. At most 30 props are kept. Values become text:
TRUE is "true", numbers are written out, and vectors are joined with
commas. NULL and NA values are left out. Values are cut to 500
characters. Everything is escaped for HTML.
The attributes print even when the tracker tag is off. They do nothing without the tracker, so your pages look the same in development and production.
Never put names, emails, user ids or anything that points to one person in event props.
privatus_event() returns the tag with the attributes added.
privatus_event_data() returns a named list (empty for an empty name).
privatus_event_attrs() returns an htmltools::HTML() string (empty for
an empty name).
library(htmltools) privatus_event(tags$button("Export"), "Report exported", format = "csv") tags$button("Export", !!!privatus_event_data("Report exported", format = "csv")) privatus_event_attrs("Report exported", format = "csv", source = "toolbar")library(htmltools) privatus_event(tags$button("Export"), "Report exported", format = "csv") tags$button("Export", !!!privatus_event_data("Report exported", format = "csv")) privatus_event_attrs("Report exported", format = "csv", source = "toolbar")
privatus_shiny() adds a tiny script to your 'shiny' app. It listens for
messages from your server function and passes them to the tracker in the
browser with privatus.track(). privatus_track() sends such a message.
privatus_shiny() privatus_track(name, ..., .props = list(), session = NULL)privatus_shiny() privatus_track(name, ..., .props = list(), session = NULL)
name |
The event name. Use a short "Noun verb" name like
|
... |
Event props as |
.props |
A named list of more props. |
session |
The 'shiny' session. Defaults to the current one. |
Your R server never talks to Privatus. The message goes to the visitor's own browser over the 'shiny' connection, and the browser's tracker sends the event, just like a click would. When the tracker is not on the page (for example in development) nothing happens.
Props follow the page props rules: keys are cleaned, numbers and
TRUE/FALSE keep their type, and at most 30 props are kept. Never put
names, emails, user ids or anything that points to one person in props.
privatus_shiny() returns an htmltools::tagList() holding the
script as an htmltools::htmlDependency(). privatus_track() returns
TRUE invisibly when a message was sent, and FALSE for an empty name.
if (interactive() && requireNamespace("shiny", quietly = TRUE)) { library(shiny) ui <- fluidPage( tags$head(privatus_analytics(), privatus_shiny()), downloadButton("export", "Export") ) server <- function(input, output, session) { output$export <- downloadHandler("data.csv", function(file) { privatus_track("Report exported", format = "csv") utils::write.csv(mtcars, file) }) } shinyApp(ui, server) }if (interactive() && requireNamespace("shiny", quietly = TRUE)) { library(shiny) ui <- fluidPage( tags$head(privatus_analytics(), privatus_shiny()), downloadButton("export", "Export") ) server <- function(input, output, session) { output$export <- downloadHandler("data.csv", function(file) { privatus_track("Report exported", format = "csv") utils::write.csv(mtcars, file) }) } shinyApp(ui, server) }
R Markdown and 'Quarto' can add an HTML file to the <head> of every page
they build. This writes the tag (from privatus_analytics()) to such a
file. Point your document at it:
use_privatus_header(path = "_privatus.html", ..., quiet = FALSE)use_privatus_header(path = "_privatus.html", ..., quiet = FALSE)
path |
Where to write the file. |
... |
Passed to |
quiet |
|
R Markdown (html_document and most HTML formats):
output:
html_document:
includes:
in_header: _privatus.html
'Quarto' (_quarto.yml or the document header):
format:
html:
include-in-header: _privatus.html
Write the file before you build, for example in a build script or as a
'Quarto' pre-render script, so it follows your settings each time. When the
tag is off (see privatus_enabled()) or there is no site id, the file is
written empty (or with only the queue stub when queue_stub = TRUE), so
the build still works.
The path, invisibly.
path <- tempfile(fileext = ".html") use_privatus_header(path, site_id = "pa_YOURSITEID", enabled = TRUE) readLines(path)path <- tempfile(fileext = ".html") use_privatus_header(path, site_id = "pa_YOURSITEID", enabled = TRUE) readLines(path)