2424# ' @inheritParams teal::module
2525# ' @inheritParams shared_params
2626# '
27- # ' @param rmd_file (`character`) Path to the R Markdown file to be rendered.
28- # ' The file must be accessible from the Shiny app environment .
27+ # ' @param rmd_content (`character`) Content of the R Markdown file to be rendered.
28+ # ' This can be the value of `readLines("path/to/file.Rmd")` .
2929# ' @param allow_download (`logical`) whether to allow downloading of the R Markdown file.
3030# ' Defaults to `TRUE`.
3131# ' @param extra_transform (`list`) of [teal::teal_transform_module()] that will be added in the module's UI.
5555# ' modules = modules(
5656# ' tm_rmarkdown(
5757# ' label = "RMarkdown Module",
58- # ' rmd_file = system.file(file.path("sample_files", "test.Rmd"), package = "teal.modules.general")
58+ # ' rmd_content = readLines( system.file(file.path("sample_files", "test.Rmd"), package = "teal.modules.general") )
5959# ' )
6060# ' )
6161# ' )
9292# ' modules = modules(
9393# ' tm_rmarkdown(
9494# ' label = "RMarkdown Module",
95- # ' rmd_file = system.file(file.path("sample_files", "test.Rmd"), package = "teal.modules.general"),
95+ # ' rmd_content = readLines( system.file(file.path("sample_files", "test.Rmd"), package = "teal.modules.general") ),
9696# ' allow_download = FALSE,
9797# ' extra_transform = list(nrow_transform)
9898# ' )
101101# ' @export
102102# '
103103tm_rmarkdown <- function (label = " RMarkdown Module" ,
104- rmd_file ,
104+ rmd_content ,
105105 datanames = " all" ,
106106 allow_download = TRUE ,
107107 pre_output = NULL ,
@@ -113,7 +113,7 @@ tm_rmarkdown <- function(label = "RMarkdown Module",
113113 # Start of assertions
114114
115115 checkmate :: assert_string(label )
116- checkmate :: assert_file( rmd_file , access = " r " )
116+ checkmate :: assert_character( rmd_content )
117117 checkmate :: assert_flag(allow_download )
118118
119119 checkmate :: assert_multi_class(pre_output , c(" shiny.tag" , " shiny.tag.list" , " html" ), null.ok = TRUE )
@@ -127,32 +127,29 @@ tm_rmarkdown <- function(label = "RMarkdown Module",
127127 ans <- module(
128128 label = label ,
129129 server = srv_rmarkdown ,
130- server_args = list (rmd_file = rmd_file , allow_download = allow_download , extra_transform = extra_transform ),
130+ server_args = list (rmd_content = rmd_content , allow_download = allow_download , extra_transform = extra_transform ),
131131 ui = ui_rmarkdown ,
132132 ui_args = args ,
133133 transformators = transformators ,
134134 datanames = datanames
135135 )
136136 # attr(ans, "teal_bookmarkable") <- TRUE
137- ans
137+ disable_src( ans )
138138}
139139
140140# UI function for the rmarkdown module
141- ui_rmarkdown <- function (id , rmd_file , allow_download , extra_transform , ... ) {
141+ ui_rmarkdown <- function (id , rmd_content , allow_download , extra_transform , ... ) {
142142 args <- list (... )
143143 ns <- NS(id )
144144
145145 teal.widgets :: standard_layout(
146146 output = teal.widgets :: white_small_well(
147147 tags $ div(
148- tags $ h4(
149- " Rendered report from: " ,
150- tags $ code(basename(rmd_file ))
151- ),
148+ tags $ h4(" Rendered report from Rmd" ),
152149 if (allow_download ) {
153150 downloadButton(
154151 ns(" download_rmd" ),
155- sprintf(" Download '%s' " , basename( rmd_file ) ),
152+ sprintf(" Download R Markdown file " ),
156153 class = " btn-primary btn-sm"
157154 )
158155 },
@@ -168,7 +165,7 @@ ui_rmarkdown <- function(id, rmd_file, allow_download, extra_transform, ...) {
168165}
169166
170167# Server function for the rmarkdown module
171- srv_rmarkdown <- function (id , data , rmd_file , allow_download , extra_transform ) {
168+ srv_rmarkdown <- function (id , data , rmd_content , allow_download , extra_transform ) {
172169 checkmate :: assert_class(data , " reactive" )
173170 checkmate :: assert_class(isolate(data()), " teal_data" )
174171 moduleServer(id , function (input , output , session ) {
@@ -189,36 +186,52 @@ srv_rmarkdown <- function(id, data, rmd_file, allow_download, extra_transform) {
189186
190187 if (allow_download ) {
191188 output $ download_rmd <- downloadHandler(
192- filename = function () basename( rmd_file ),
189+ filename = function () " teal_module.Rmd " , # TODO: find a better name
193190 content = function (file ) {
194- lines <- readLines(rmd_file )
195-
196191 # find the end of the YAML header or start of the file
197192 # and insert the contents of teal.code::get_code(q_r())
198- yaml_end <- which(lines == " ---" )[2 ]
193+ yaml_end <- which(rmd_content == " ---" )[2 ]
199194 insert_pos <- if (! is.na(yaml_end )) yaml_end else 0
200195 note_lines <- c(
196+ " " ,
197+ " ### Pre-processing data" ,
198+ " " ,
199+ " The following code chunk was automatically added by the teal markdown module." ,
200+ " It shows how to generate the data used in this report." ,
201201 " " ,
202202 " ```{r}" ,
203- " # The following code chunk was automatically added by the teal markdown module" ,
204- " # It shows how to generate the data used in this report" ,
205203 teal.code :: get_code(q_r()),
206204 " ```" ,
207205 " "
208206 )
209- lines <- append(lines , note_lines , after = insert_pos )
210- writeLines(lines , con = file )
207+ rmd_content <- append(rmd_content , note_lines , after = insert_pos )
208+ writeLines(rmd_content , con = file )
211209 },
212210 contentType = " text/plain"
213211 )
214212 }
215213
216- temp_dir <- tempdir()
217- temp_rmd <- tempfile(tmpdir = temp_dir , fileext = " .Rmd" )
218- file.copy(rmd_file , temp_rmd ) # Use a copy of the Rmd file to avoid modifying the original
214+ clean_up_r <- shiny :: reactiveVal(list ())
215+ # Can only clean on sessionEnded as temporary files are needed for the reporter
216+ # during session
217+ onSessionEnded(function () {
218+ logger :: log_debug(" srv_rmarkdown: cleaning up temporary folders." )
219+ lapply(shiny :: isolate(clean_up_r()), function (f ) f())
220+ }, session )
219221
220222 rendered_path_r <- reactive({
221223 datasets <- req(q_r()) # Ensure data is available
224+
225+ temp_dir <- tempfile(pattern = " rmd_" )
226+ dir.create(temp_dir , showWarnings = FALSE , recursive = TRUE )
227+ temp_rmd <- tempfile(pattern = " rmarkdown_module-" , tmpdir = temp_dir , fileext = " .Rmd" )
228+ # Schedule cleanup of temp files when reactive is re-executed
229+ shiny :: isolate({
230+ old_clean_up <- clean_up_r()
231+ clean_up_r(c(old_clean_up , function () unlink(temp_dir , recursive = TRUE )))
232+ })
233+ writeLines(rmd_content , con = temp_rmd )
234+
222235 tryCatch(
223236 {
224237 rmarkdown :: render(
@@ -252,20 +265,7 @@ srv_rmarkdown <- function(id, data, rmd_file, allow_download, extra_transform) {
252265
253266 reactive({
254267 out_data <- q_r()
255-
256- if (allow_download ) {
257- out_data <- eval_code(
258- q_r(),
259- paste(
260- sep = " \n " ,
261- sprintf(" ## R Markdown contents are generated from file, please download it from the module UI." ),
262- sprintf(" # rmarkdown::render(%s, params = params)" , shQuote(basename(rmd_file ), type = " cmd" ))
263- )
264- )
265- out_data @ verified <- FALSE # manual change verified status as code is being injected
266- }
267-
268- report_doc <- .markdown_internal(rendered_path_r(), temp_dir , rendered_html_r())
268+ report_doc <- .markdown_internal(rendered_path_r(), rendered_html_r())
269269 teal.reporter :: teal_card(out_data ) <- c(
270270 teal.reporter :: teal_card(out_data ), report_doc
271271 )
@@ -275,7 +275,7 @@ srv_rmarkdown <- function(id, data, rmd_file, allow_download, extra_transform) {
275275}
276276
277277# ' @exportS3Method tools::toHTML
278- toHTML.markdown_teal_internal <- function (block , ... ) {
278+ toHTML.markdown_internal <- function (block , ... ) {
279279 cached_html <- attr(block , " cached_html" , exact = TRUE )
280280 if (! is.null(cached_html )) {
281281 return (cached_html )
@@ -284,50 +284,30 @@ toHTML.markdown_teal_internal <- function(block, ...) {
284284}
285285
286286# ' @method to_rmd markdown_internal
287- to_rmd.markdown_teal_internal <- function (block , figures_dir = " figures" , include_chunk_output = TRUE , ... ) {
288- images_base64 <- attr(block , " images_base64" , exact = TRUE )
289- for (img_path in names(images_base64 )) {
290- img_data <- sub(" ^data:.*;base64," , " " , images_base64 [[img_path ]])
291- img_tag_pattern <- paste0(" !\\ [.*?\\ ]\\ (" , img_path , " \\ )" )
292- dir.create(figures_dir , showWarnings = FALSE , recursive = TRUE )
293- path <- file.path(
294- figures_dir ,
295- sprintf(
296- " markdown_img_%s.%s" ,
297- substr(rlang :: hash(img_data ), 1 , 6 ),
298- sprintf(" %s" , tools :: file_ext(img_path ))
299- )
300- )
301- writeBin(base64enc :: base64decode(img_data ), path )
302- replacement_tag <- sprintf(" " , path )
303- block <- gsub(img_tag_pattern , replacement_tag , block , fixed = FALSE )
304- }
287+ # ' @exportS3Method teal.reporter::to_rmd
288+ to_rmd.markdown_internal <- function (block , figures_dir = " figures" , include_chunk_output = TRUE , ... ) {
289+ old_base_path <- attr(block , " old_base_path" , exact = TRUE )
290+ parent_path <- attr(block , " parent_path" , exact = TRUE )
291+ new_base_path <- file.path(figures_dir , old_base_path )
292+
293+ # Copy figures from old path to new location
294+ dir.create(figures_dir , showWarnings = FALSE , recursive = TRUE )
295+ file.copy(file.path(parent_path , old_base_path ), figures_dir , recursive = TRUE )
296+
297+ # Change the image paths in the markdown content
298+ block <- gsub(pattern = old_base_path , replacement = new_base_path , x = block , fixed = TRUE )
305299 NextMethod(unclass(block ), ... )
306300}
307301
308- .markdown_internal <- function (markdown_file , temp_dir , rendered_html ) {
309- # Read the markdown file
310- lines <- readLines(markdown_file )
311- images_base64 <- list ()
312-
313- # Extract images based on pattern 
314- img_pattern <- " !\\ [.*?\\ ]\\ ((.*?)\\ )"
315- img_tags <- unlist(regmatches(lines , gregexpr(img_pattern , lines )))
316- for (ix in seq_along(img_tags )) {
317- img_tag <- img_tags [[ix ]]
318- img_path <- gsub(" !\\ [.*?\\ ]\\ ((.*?)\\ )" , " \\ 1" , img_tag )
319- full_img_path <- file.path(temp_dir , img_path )
320- if (file.exists(full_img_path )) {
321- img_data <- knitr :: image_uri(full_img_path )
322- images_base64 [[img_path ]] <- img_data
323- }
324- }
302+ .markdown_internal <- function (markdown_file , rendered_html ) {
303+ base_file <- basename(markdown_file )
325304
326305 # Create new custom structure with contents and images in base64 as attribute
327306 structure(
328- lines ,
329- class = c(" markdown_teal_internal" , " character" ),
330- images_base64 = images_base64 ,
307+ readLines(markdown_file ),
308+ class = c(" markdown_internal" , " character" ),
309+ parent_path = dirname(markdown_file ),
310+ old_base_path = sprintf(" %s_files/" , tools :: file_path_sans_ext(base_file )),
331311 cached_html = rendered_html
332312 )
333313}
0 commit comments