HOW TO EMBED

YOUR DOCUMENTS WITH BOXVIEW

TO START, YOU NEED

  • Paperclip and AWS S3 installed and configured
  • And, of course, figaro implemented
  • A Document model with a name
  • A Document controller with an Index, New, Create and Show method
  • A resources :documents in your routes

MODIFY YOUR PAPERCLIP

You need to authorize certain types of document.

So add some MIME Types in your model.

class Document < ActiveRecord::Base
  validates :name, presence: true

  has_attached_file :doc

  validates_attachment_content_type :doc, content_type: [
    "application/pdf",
    "application/msword",
    "application/vnd.oasis.opendocument.text",
    "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
    "application/vnd.ms-powerpoint",
    "application/vnd.openxmlformats-officedocument.presentationml.presentation",
    "text/css",
    "application/javascript",
  ]
end

GET AN API KEY

First, you need to create an account : https://app.box.com/developers/services

 

Create a Box Application

Select Box View

Copy the Key

Paste it in your application.yml

LET'S BOX NOW !

BOX STEPS

  1. Get a document_id from Box
  2. Open a session in Box
  3. When ready, it will return an url that you'll insert into an iframe

BUT WHERE ARE WE GOING TO STORE THAT URL ?

IN THE MODEL !

UPGRADE THE MODEL

We need to add a string for :box_view_url

rails generate migration AddBoxViewUrlToDocuments box_view_url:string

and let's place the iframe in our show.html.slim

  .embed-responsive.embed-responsive-4by3
    iframe src=@document.box_view_url class="embed-responsive-item" frameborder="0" allowfullscreen="allowfullscreen"

I. DOCUMENT_ID

CHAT WITH BOX

We're going to use HTTParty to discuss with BoxView

In your gemfile, add :

gem 'httparty'

And of course, run

bundle install

WHAT BOX NEEDS 

curl https://view-api.box.com/1/documents \
-H "Authorization: Token YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "http://www.example.com/MyFile.pdf"}' \
-X POST
def generate_box_document
  HTTParty.post('https://view-api.box.com/1/documents',
    headers: {
      'Authorization' => "Token #{ENV['BOX_VIEW_API_KEY']}",
      'Content-Type' => 'application/json',
    },
    body:
    {
      url: doc.url,
      name: doc.name
    }.to_json
  )
end

SO, LET'S CREATE A METHOD

Why do we need the .to_json ?

Let's see Box's answer !

def create
  @document = Document.new(document_params)

  if @document.save
    @document.generate_box_document
    redirect_to document_path(@document)
  else
    render :new
  end
end

AND CALL IT...

in the Documents Controller (for now)

II. CREATE A SESSION

WHAT BOX NEEDS

curl https://view-api.box.com/1/sessions \
-H "Authorization: Token YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"document_id": "ABC123"}' \
-X POST
def generate_box_session(document_id)
  session = HTTParty.post('https://view-api.box.com/1/sessions',
    headers: {
      'Authorization' => "Token #{ENV['BOX_VIEW_API_KEY']}",
      'Content-Type' => 'application/json',
    },
    body: {
      document_id: document_id,
      expires_at: 90.days.from_now,
      is_downloadable: true,
    }.to_json
  )
end

THE METHOD WILL BE

Let's see Box's answer !

CODE 202 !

201

The session was successfully created.

202

The document is not ready for viewing. A Retry-After header will be included 
in the response indicating the time to wait before trying again.

400

An error occurred while converting the document, or the document does not exist.

if 201

Easy, let's update our model with the url that Box gave us:

if 202

Let's run again the method:

if session.response.code == '201'
  self.update_column(:box_view_url, session['urls']['view'])
if session.response.code == '202'
  generate_box_session(document_id)

AND IT DOESN'T WORK

[3] pry(main)> Document.last.box_view_url
  Document Load (0.4ms)  SELECT  "documents".* FROM "documents"  ORDER BY "documents"."id" DESC LIMIT 1
=> nil

LET'S THINK DIFFERENTLY

For now, when the doc is saved in our base,  we call:

@document.generate_box_document

And with the document_id generated,

we're looking to retrieve an url for our iframe.

And if we don't get that url, what happens? Nothing.

WHY DON'T WE CREATE A BOX_VIEWER METHOD THAT WILL BE CALLED IF NOTHING HAPPENED?

View

Model

- if @document.box_view_url.present?
  .embed-responsive.embed-responsive-4by3
    iframe src=@document.box_view_url class="embed-responsive-item" frameborder="0" allowfullscreen="allowfullscreen"
- else
  - @document.box_viewer
  p Your document is loading...
  def box_viewer
    box_document = generate_box_document
    generate_box_session(box_document['id'])
  end

And get rid of @document.generate_box_document
in the controller

Let's add a little twist...

III. ADD A LITTLE JAVASCRIPT

LET'S AUTOREFRESH !

Put a loader in your assets, add a class in the view and define it in your stylesheets

.box_loader {
  background-image: image-url('loader.gif');
  width: 64px;
  height: 64px;
  margin-top: 100px;
  margin-left: 33.5em;
}

AND FOR THE JS...

Add a boxloader.js file and in it:

$(window).load(function(){
  if($('.box_loader').length > 0){
    setTimeout(function() {
      location.reload();
    }, 4000)
  }
})

TADAAAA !

Thanks you !

And if you want to practise, here is the repo where you can start from this demo:

https://github.com/soniaprevost/lbyt-embedwithbox

Fork it !

Special thanks to

  • Julien, Lead Developer @agorize
  • Adrien, FrontEnd Developer @agorize
  • The Wagon Team (Cécile, Sébastien, Miruna,...)

Box View Rails

By Sonia Prévost

Box View Rails

  • 1,186