rstudio / blogdown

Create Blogs and Websites with R Markdown
https://pkgs.rstudio.com/blogdown/
1.73k stars 333 forks source link

Insert external image addin #269

Closed lcolladotor closed 6 years ago

lcolladotor commented 6 years ago

Hi Yihui,

I was about write an updated version of http://lcolladotor.github.io/2018/02/17/r-markdown-blog-template using the new changes from https://github.com/rstudio/blogdown/pull/263. I then realized that inserting external (non R images) takes enough work that it might scare off some users. This has been discussed in https://github.com/rstudio/blogdown/issues/239. I know that another option is to use knitr::insert_graphics() but then the user has to organize the pictures someway that avoids creating files at blogpath/content/post. So I made an addin for inserting pictures that copies the selected file to blogpath/static/postdir/ and inserts the HTML code for showing a picture. I chose HTML since that should work with .md, .Rmd and .Rmarkdown blog posts + gives control on the width/height.

What's the best option for submitting a PR? Should I delete my first fork at https://github.com/lcolladotor/blogdown and remake one? Some of the solutions at https://stackoverflow.com/questions/28119733/how-can-i-make-a-second-fork-of-a-github-project (a new account, forking to an organization) don't seem like the best to me.

Addin demo

Select addin from the addins menu

screen shot 2018-03-05 at 11 22 12 pm

If the user didn't have a file selected (for example they had the console selected), end in an error with a useful message.

screen shot 2018-03-05 at 11 24 00 pm

Addin after filling some options

screen shot 2018-03-05 at 11 24 34 pm

Text is inserted, console prints the location of where the image was copied to. Useful if new users want to learn what is going on.

screen shot 2018-03-05 at 11 24 43 pm

Addin code

Contents of inst/scripts/insert_image.R. It's based on inst/scripts/new_post.R

local({
  txt_input = function(..., width = '100%') shiny::textInput(..., width = width)

  blogfile = rstudioapi::getActiveDocumentContext()
  if(blogfile$path == '') stop('Please select the blog post source file before using this addin', call. = FALSE)

  imgdir = file.path(
    gsub('/content/.*', '', normalizePath(blogfile$path)),
    'static',
    dirname(gsub('.*content/', '', normalizePath(blogfile$path))),
    paste0(xfun::sans_ext(basename(blogfile$path)), '_files')
  )

  shiny::runGadget(
    miniUI::miniPage(miniUI::miniContentPanel(
      shiny::fillRow(
        shiny::fileInput('newimg', 'Image', placeholder = 'Select external image'),
        height = '100px'
      ),
      shiny::fillRow(
        txt_input('imgwidth', 'Image width', '', '(optional) Example: 400 for 400px'),
        txt_input('imgheight', 'Image height', '', '(optional) Example: 200 for 200px'),
        height = '70px'),
      shiny::fillRow(
        txt_input('imgalt', 'Image alternative text', '', '(optional) Example: awesome screenshot'),
        height = '70px'
      ),
      miniUI::gadgetTitleBar(NULL)
    )),
    server = function(input, output, session) {
      shiny::observeEvent(input$done, {
        if (is.null(input$newimg)) return(
          warning('You have to choose an image!', call. = FALSE)
        )
        dir.create(imgdir, showWarnings = FALSE)
        file.copy(input$newimg$datapath, file.path(imgdir, input$newimg$name))
        if(file.exists(file.path(imgdir, input$newimg$name))) message(paste('successfully copied the image to', file.path(imgdir, input$newimg$name)))

        imgsrc = paste0(
          "/",
          basename(dirname(imgdir)),
          "/",
          basename(imgdir),
          "/",
          input$newimg$name
        )

        image_text = if(input$imgwidth == '' & input$imgheight == '') shiny::img(src = imgsrc) else if (input$imgwidth == '') shiny::img(src = imgsrc, height = input$imgheight) else if(input$imgheight == '') shiny::img(src = imgsrc, width = input$imgwidth) else shiny::img(src = imgsrc, width = input$imgwidth, height = input$imgheight)

        rstudioapi::insertText(as.character(image_text))
        shiny::stopApp()
      })
      shiny::observeEvent(input$cancel, {
        shiny::stopApp()
      })
    },
    stopOnCancel = FALSE, viewer = shiny::dialogViewer('Add external image to a blogdown post', height = 270)
  )
})

Changes to R/utils.R (around line 325)

insert_image_addin = function() {
  sys.source(pkg_file('scripts', 'insert_image.R'))
}

Changes at the end of inst/rstudio/addins.dcf

Name: Insert image
Description: Insert an external image into a blogdown post
Binding: insert_image_addin
Interactive: true

Best, Leo

lcolladotor commented 6 years ago

This also should be easier than what I used to do at https://github.com/lcolladotor/markdown-redcarpet.tmbundle/commit/f043c056ff620299843e9d8ea34144f478aa7965

yihui commented 6 years ago

Yes! Yes!! Yes!!! That is absolutely the thing we need in blogdown, and WordPress can RIP after your PR!

The last time you used your master branch to create the PR, which is good for a one-time PR, but not good for future PRs. In general, you should use different branches for PRs, and keep the master branch in sync with the upstream master branch here. Since you have already used master, the easiest option for you is to delete your blogdown repo, fork it from here again, make changes in a new branch, push the branch, and submit a PR.

BTW, it looks like the "Great Hacker Award" worked? 😜 Did you receive the blogdown stickers?

lcolladotor commented 6 years ago

Hi Yihui,

I'm glad that you are super excited about the insert image addin idea =)

I followed your instructions (deleted repo, re-forked, made a branch, sent PR from branch) so hopefully it's all good. Compared to the code above, I simply reduced 10px the file chooser part of the addin.

Best, Leo

PS I haven't received the stickers yet, I'll let you know when I do ^^. And yes, the great hacker award was motivating hehe =D

lcolladotor commented 6 years ago

I just received the blogdown stickers, thanks Yihui!

img_4306

yihui commented 6 years ago

Excellent! I sent you two: one with my signature, and obviously the other one was for your laptop (which is already on your laptop).

lcolladotor commented 6 years ago

Hehe yup, I did get both =) and took an image of the signed one. Thank for the note!

Actually, that's not my laptop. It's the bookstand next to my monitor at work ^^