Embedded PDF In File View with Plone

A quick and easy modification to the file_view template in Plone, which when viewing a PDF, will display the PDF embedded on the page, not just a download link.

A quick note before we get started: This method, is very much plugin dependent, if the users browser doesn’t come with the ability to view PDFs, or they have not got some form of in-browser PDF viewer like Adobe Reader, they will not see the PDF at all. We will attempt to make this degradation as graceful as possible.

How do I find the template name?

  • Make a note of what content type you are looking to change the template for (is it a Page? A File? etc.)
  • Go to the Zope Management Interface
  • Click on portal_types
  • Find the object we’re looking to update the template for (in this case, File) and click on it
  • You will see a “Available view methods” heading, and a box next to it containing references to various templates
  • Make note of the template that was listed (in this case file_view) and go back to the root of the Zope Management Interface

What if I can’t find it?

Most of the view templates for Plone content types can be found in the Zope Management Interface under plone_skins inside of plone_content however if you’re looking for anything in Plone you can do the following:

  • Go to the Zope Management Interface
  • Click the Find tab located at the very top of the page
  • Paste or type the name of your view into the with ids field and hit the Find button
  • You will see the template in the results

Adding the functionality

The default template for file_view looks like this:

<html xmlns=”http://www.w3.org/1999/xhtml” xml:lang=”en”

      xmlns:tal=”http://xml.zope.org/namespaces/tal”

      xmlns:metal=”http://xml.zope.org/namespaces/metal”

      xmlns:i18n=”http://xml.zope.org/namespaces/i18n”

      lang=”en”

      metal:use-macro=”context/main_template/macros/master”

      i18n:domain=”plone”>

<body>

    <metal:content-core fill-slot=”content-core”>

        <metal:block define-macro=”content-core”

                     tal:define=”content_type context/get_content_type|context/Format”>

            <p>

                <metal:field use-macro=”python:context.widget(‘file’, mode=’view’)”>

                File

                </metal:field>

            </p>

            <div tal:condition=”python: content_type.startswith(‘text’) and context.get_size() > 0″>

                <h2 i18n:translate=”heading_file_contents”>File contents</h2>

                <pre tal:content=”context/get_data|context/data|nothing”>

                </pre>

            </div>

        </metal:block>

    </metal:content-core>

</body>

</html>

We are going to add an object tag which will enable us to embed PDF content types directly into the page, luckily we can make use of a lot of information that already exists and we only need to add a small amount of our own HTML. That’s handy!

Just above the opening <p> tag is where we will make our changes, this will result in the PDF displayed above with a link to the file underneath, this looks a bit neater when it’s rendered so that’s what we’re going to do.

The code we add & explanations:

First we need to define a variable using TALES (Template Attribute Language) which is what you can see above (all Plone page templates are written in this language). This variable means we can avoid typing large amount of repetitive information. The variable we are defining is for the location of the PDF.

Our definition is as follows:

tal:define=”location context.absolute_url()”

We will add the previous line to the <metal:block define-macro=”content-core” define property (variables that have been defined are only available within the scope of that tag, so I define it here incase we need to make use of it throughout the template). This will get an absolute URL of the object, and assign it to the word location so we only have to type location each time we need the link!

Now we need to take our HTML:

<object data=”Location of the PDF” type=”application/pdf”>

<a href=”Location of the DF”>

  Oh no! It appears you are missing a plugin, click here to download the file.

</a>

<embed src=”Location of the PDF” type=”application/pdf”></embed>

</object>

And turn it into something that Plone can make use of.

First things first, we need a condition. This will prevent the object tag rendering for objects that are not PDFs. This condition was lifted from the last <div> on the page, you can see they are checking if the content type starts with ‘text’.

We altered it slightly to: python:content_type.endswith(‘pdf’) and added that to the <object> element. The reason we used endswith() instead of startswith() is that plain text content types are: text/plain, HTML files are text/html, PDFs on the other hand, are application/pdf. Hence we need to check the end of that information, not the start.

Then we need Plone to update the data attribute on the <object> tag to point to whichever PDF the user is looking at (remember this template is used for any file you upload to the site, so this attribute will change from file to file). We do this by adding the line tal:attributes=”data location” to the <object> element. You will see we’ve made use of our location variable here.

I understand that some browsers do not support the <object> element so we will add a link which allows the user to download the PDF directly, we will also provide an <embed> tag just in case their browser supports this instead.

The below code shows our <object> tag which will now be dynamically updated depending on which PDF file we view, and will also not appear if the file uploaded is not a PDF.

<object tal:condition=”python:content_type.endswith(‘pdf’)”

   class=”viewembededfile” type=”application/pdf”

   data=”” tal:attributes=”data location”>

    <a href=”” tal:attributes=”href location”>

      Please click here to download the PDF.

    </a>

   <embed src=”” class=”viewembededfile”

      type=”application/pdf” tal:attributes=”src location”>

   </embed>

</object>

Our changes completely made:

<html xmlns=”http://www.w3.org/1999/xhtml” xml:lang=”en”

xmlns:tal=”http://xml.zope.org/namespaces/tal”

xmlns:metal=”http://xml.zope.org/namespaces/metal”

xmlns:i18n=”http://xml.zope.org/namespaces/i18n”

lang=”en”

metal:use-macro=”context/main_template/macros/master”

i18n:domain=”plone”>

<body>

<metal:content-core fill-slot=”content-core”>

  <metal:block define-macro=”content-core”

   tal:define=”content_type context/get_content_type|context/Format;

   location python:context.absolute_url()”>

    <object tal:condition=”python:content_type.endswith(‘pdf’)” class=”viewembededfile”

     type=”application/pdf” data=”” tal:attributes=”data location”>

      <a href=”” tal:attributes=”href location”>Please click here to download the PDF.</a>

      <embed src=”” class=”viewembededfile” type=”application/pdf”

       tal:attributes=”src location”>

      </embed>

    </object>

   <p>

    <metal:field use-macro=”python:context.widget(‘file’, mode=’view’)”>

     File

    </metal:field>

   </p>

   <div tal:condition=”python: content_type.startswith(‘text’) and context.get_size() > 0″>

    <h2 i18n:translate=”heading_file_contents”>File contents</h2>

    <pre tal:content=”context/get_data|context/data|nothing”></pre>

   </div>

  </metal:block>

</metal:content-core>

</body>

</html>

NOTE: This is a template from Plone 4.2.4, I’m not sure if this is any different from other Plone versions but you may want to check before any copy and paste action ensues!

Find out more about us. Visit our Managed IT Support page.