Navigation Menu

Skip to content

Commit

Permalink
Updated the PageAttachment#public_filename to allow choice of friendl…
Browse files Browse the repository at this point in the history
…y or direct (fast) URL and attachment:url and attachment:link tags to expose that choice to the user. The attachment:url tag defaults to friendly URLs and attachment:link defaults to fast URLs.
  • Loading branch information
jgarber committed Jun 9, 2009
1 parent 899cf56 commit 0bcfecd
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 28 deletions.
53 changes: 30 additions & 23 deletions README
@@ -1,32 +1,39 @@
= Page Attachments Xsendfile

Created by Jason Garber, October 2008.

Works with the page_attachments extension to make attached files available under the page they're attached to, so you can link to files at sane URLs. Since it uses the X-Sendfile header, delivery of the attached items doesn't block the Rails process.

Ordinarily the page_attachments extension locates pages at URLs like /page_attachments/0000/0001/my_image.jpg, regardless of where the attachment is attached. This may be fine for images, where the user never sees the URL, but what about PDFs or other files where the URL is important?

With the page_attachments_xsendfile extension, if you attach report.pdf to the page with the slug /president/board/, then the attachment is available at /president/board/report.pdf.

== PERFORMANCE WARNING
(June 2009) This extension was a good idea, but it results in higher server loads because it has to run all the attachments embedded in a page through Radiant. If the page has several image attachments referenced in itself, the browser requests them all at once and Radiant has to find_by_url for each one. Radiant and a fast machine can handle it, but you need more processes than you otherwise would.

== Development Status

Because of this performance hit, I don't think it's worth it anymore, so I'm no longer maintaining this extension. Feel free to fork it or ask me for commit rights if you want to maintain it. At some point in the future I may figure out something different, like virtual urls only for non-images and the /page_attachments/0000/0001 type for images.
= Page Attachments X-Sendfile

Created by Jason Garber, October 2008. Updated June 2009.

This extension works with the page_attachments extension to make attached
files available under the page they're attached to so you can link to files at
friendly URLs. Since it uses the X-Sendfile header, delivery of the attached
items doesn't block the Rails process, though it does require a couple
database queries to find the page and the attachment.

Ordinarily the page_attachments extension locates pages at URLs like
/page_attachments/0000/0001/my_image.jpg and the attachment is served directly
by the webserver. This is great for images, where the user never sees the URL,
but what about PDFs and other files where the URL is important? With this
extension installed, if you attach report.pdf to the page at
/president/board/, then the attachment is available at
/president/board/report.pdf.

The attachment:link tag uses this friendly URL format by default unless you
specify format="fast". The attachment:url tag uses the direct attachment URL
(/page_attachments/...) by default unless you specify format="friendly". The
attachment image tags always use the direct URL and you can't specify
otherwise. Loading a page full of images attached at friendly URLs is a great
way to bog down your server while Radiant does a find_by_url on each of them,
so it's not even an option.

== Installation
=== Easy way:
=== Automated install:
./script/extension install page_attachments_xsendfile

=== Harder way:
=== Manual install:

1) Unpack/checkout/export the extension into vendor/extensions of your
project.

git clone git://github.com/jgarber/radiant-page_attachments_xsendfile-extension.git vendor/extensions/page_attachments_xsendfile

2) Run the extension update task.

$ rake production radiant:extensions:page_attachments_xsendfile:update

3) Restart your server
2) Restart your server

10 changes: 9 additions & 1 deletion lib/page_attachments_xsendfile/page_attachment_extensions.rb
@@ -1,6 +1,14 @@
module PageAttachmentsXsendfile::PageAttachmentExtensions
def self.included(base)
base.send :alias_method_chain, :public_filename, :xsendfile
base.send :alias_method_chain, :public_filename, :friendly_url_switching
end

def public_filename_with_friendly_url_switching(thumbnail = nil, friendly_url = false)
if friendly_url
public_filename_with_xsendfile(thumbnail)
else
public_filename_without_friendly_url_switching(thumbnail)
end
end

# Gets the public path to the file, which is a child of the page the
Expand Down
52 changes: 52 additions & 0 deletions lib/page_attachments_xsendfile/page_attachment_tags_extensions.rb
@@ -0,0 +1,52 @@
module PageAttachmentsXsendfile::PageAttachmentTagsExtensions
include Radiant::Taggable

desc %{
Renders the url or public filename of the attachment for use in links, stylesheets, etc.
The 'name' attribute is required on this tag or the parent tag. The optional 'size' attribute
applies only to images.
The optional 'format' attribute specifies whether the URL will be the
one at /page_attachments/0000/0001/file.jpg (fast--best for images) or the friendly URL that
includes the URL of the page it's attached to. The friendly format takes a little longer to find
but makes nicer URLs for things like documents. Fast is the default.
*Usage*:
<pre><code><r:attachment:url name="file.jpg" [size="icon"] [format="fast|friendly"]/></code></pre>
}
tag "attachment:url" do |tag|
raise TagError, "'name' attribute required" unless name = tag.attr['name'] or tag.locals.attachment
page = tag.locals.page
size = tag.attr['size'] || nil
format = (tag.attr['format'] == "friendly") || nil
attachment = tag.locals.attachment || page.attachment(name)
attachment.public_filename(size, format)
end

desc %{
Renders a hyperlink to the attachment. The 'name' attribute is required on this tag or the parent tag.
You can use the 'label' attribute to specify the textual contents of the tag. The format attribute
specifies whether the link goes directly to the attachment or via a friendly (but slower) alias (which is the
default for links). Any other attributes will be added as HTML attributes to the rendered tag. This tag
works as both a singleton and a container. Any contained content will be rendered inside the resulting link.
The optional 'size' attribute applies only to images.
*Usage*:
<pre><code><r:attachment:link name="file.jpg" [size="thumbnail"] [format="friendly|fast"]/></code></pre>
<pre><code><r:attachment:link name="file.jpg" [size="thumbnail"] [format="friendly|fast"]> Some text in the link </r:attachment:link></code></pre>
}
tag "attachment:link" do |tag|
raise TagError, "'name' attribute required" unless name = tag.attr.delete('name') or tag.locals.attachment
page = tag.locals.page
attachment = tag.locals.attachment || page.attachment(name)
label = tag.attr.delete('label') || attachment.filename
size = tag.attr.delete('size') || nil
format = tag.attr.delete('format') != "fast"
filename = attachment.public_filename(size, format) rescue ""
attributes = tag.attr.inject([]){ |a,(k,v)| a << %{#{k}="#{v}"} }.join(" ").strip
output = %{<a href="#{filename}"#{" " + attributes unless attributes.empty?}>}
output << (tag.double? ? tag.expand : label)
output << "</a>"
end
end
5 changes: 4 additions & 1 deletion page_attachments_xsendfile_extension.rb
Expand Up @@ -7,7 +7,10 @@ class PageAttachmentsXsendfileExtension < Radiant::Extension


def activate
Page.send :include, PageAttachmentsXsendfile::PageExtensions
Page.class_eval {
include PageAttachmentsXsendfile::PageExtensions
include PageAttachmentsXsendfile::PageAttachmentTagsExtensions
}
PageAttachment.send :include, PageAttachmentsXsendfile::PageAttachmentExtensions
SiteController.send :include, PageAttachmentsXsendfile::SiteControllerExtensions

Expand Down
34 changes: 34 additions & 0 deletions spec/models/page_attachment_tags_spec.rb
@@ -0,0 +1,34 @@
require File.dirname(__FILE__) + '/../spec_helper'

describe PageAttachmentTags do
dataset :pages, :page_attachments

FAST_URL_PATTERN = /\/page_attachments\/\d{4}\/\d{4}/

before do
@page = pages(:first)
@attachment = page_attachments(:first)
end

it "should render attachment url in normal format by default" do
@page.should render('<r:attachment:url name="rails.png" />').matching(FAST_URL_PATTERN)
@page.should render('<r:attachment:url name="rails.png" format="fast" />').matching(FAST_URL_PATTERN)
end
it "should render attachment url in friendly format when option is set" do
@page.should render('<r:attachment:url name="rails.png" format="friendly" />').as(File.join(@page.url, @attachment.filename))
end

it "should always use the fast URL format for images" do
expected = %{<img src="#{@attachment.public_filename}" />}
@page.should render('<r:attachment:image name="rails.png" />').as(expected)
end

it "should link to a url in the friendly format by default" do
expected = %Q{<a href="#{File.join @page.url, @attachment.filename}">#{@attachment.filename}</a>}
@page.should render('<r:attachment:link name="rails.png" />').as(expected)
@page.should render('<r:attachment:link name="rails.png" format="friendly" />').as(expected)
end
it "should link to a url in the normal format when option is set to 'fast'" do
@page.should render('<r:attachment:link name="rails.png" format="fast" />').matching(FAST_URL_PATTERN)
end
end
11 changes: 8 additions & 3 deletions spec/models/page_attachment_with_xsendfile_spec.rb
Expand Up @@ -11,11 +11,16 @@
describe "#public_filename" do
it "should be extended by xsendfile" do
@attachment.should respond_to(:public_filename_with_xsendfile)
@attachment.should respond_to(:public_filename_without_xsendfile)
@attachment.should respond_to(:public_filename_with_friendly_url_switching)
@attachment.should respond_to(:public_filename_without_friendly_url_switching)
end

it "should be under the page it is attached to" do
@attachment.public_filename.should == File.join(@page.url, @attachment.filename)
it "should be under the page it is attached to when second param is true" do
@attachment.public_filename(nil, true).should == File.join(@page.url, @attachment.filename)
end

it "should be at the normal URL by default" do
@attachment.public_filename.should =~ /\/page_attachments\/\d{4}\/\d{4}/
end
end

Expand Down

0 comments on commit 0bcfecd

Please sign in to comment.