How do I create a file download link in ActiveAdmin to download an uploaded file?
I tried something like this:
action_item :only=> :show do
link_to('Download file', [#user, :upload_file])
end
The action_item will establish the button in the taskbar, but it does not create the actual route or the controller's action method.
You can achieve the download functionality by creating a custom member_action in user.rb
# In app/admin/users.rb
action_item only: [:show] do
link_to('Download File', download_admin_user_path(resource)) if resource.upload_file.present?
end
member_action :download, method: :get do
user = User.find(params[:id])
send_file user.upload_file
end
or, my preference, just leverage the RESTful show action in upload_file.rb
# In app/admin/users.rb
action_item only: [:show] do
# NOTE: remove period from file extension
file_ext = resource.upload_file.file_extension.gsub('.', '')
link_to('Download File', download_admin_upload_file_path(resource.upload_file, format: file_ext)) if resource.upload_file.present?
end
# In app/admin/upload_files.rb
controller do
def show
if params[:format] == 'txt' # example of a known mimetype, e.g. text file
send_data resource.path, type: 'text/plain'
elsif params[:format] == resource.file_extension # example of unknown mimetype
send_file resource.path
# let ActiveAdmin perform default behavior
else
super
end
end
end
cite: http://activeadmin.info/docs/8-custom-actions.html
I don't know the complete code that you are using but that should work -
routes.rb -
resources :users do
collection do
get :upload_file
end
end
controller -
def upload_file
send_file #user.upload_file.path, :type => 'application/pdf', :filename => #user.permalink
end
View -
<%= link_to 'Download file', upload_file_users_path(#user) %>
Related
I have following model and controller setup on my app
attr_accessible :upload
has_attached_file :upload,
:url => "/files/docs/:basename.:extension"
:path => "/files/docs/:basename.:extension"
include Rails.application.routes.url_helpers
def to_jq_upload
{
"name" => read_attribute(:upload_file_name),
"size" => read_attribute(:upload_file_size),
"url" => upload.url(:original),
"delete_url" => upload_path(self),
"delete_type" => "DELETE"
}
end
and controller
def create
#upload = Upload.new(params[:upload])
respond_to do |format|
if #upload.save
format.html {
render :json => [#upload.to_jq_upload].to_json,
:content_type => 'text/html',
:layout => false
}
format.json { render json: {files: [#upload.to_jq_upload],param:params}, status: :created, location: #upload }
else
format.html { render action: "new" }
format.json { render json: #upload.errors, status: :unprocessable_entity }
end
end
end
now I want to upload my files to different folders like docs, images etc, so I need to make /docs dynamic in path
In file upload form I have added a hidden field with name folder and set value "docs" but when I use it in model to make path dynamic it gives me error following is the code I tried
has_attached_file :upload,
:url => "/files/#{params[:folder]}/:basename.:extension"
:path => "/files/#{params[:folder]}/:basename.:extension"
when I check I can see folder name in params but I am not able to use it in model.
I also tried interpolation
Paperclip.interpolates :folder do |attachment, style|
attachment.instance.params[:folder]
end
but no result.
Is there any way to make path dynamic using params ?
You need to do some changes to get your desired result.
Add the field folder to your model so that it gets saved when you save your form.
Update your model and remove the path and url options form has_attached_file:
has_attached_file: :upload
Update your environment (e.g. config/environments/development.rb) configuration of paperclip to use the interpolation and a dynamic path:
# Paperclip defaults
config.paperclip_defaults = {
# other paperclip options for storage type e.g. file or S3
# important part
path: '/files/:dynamic_path'
}
Implement the interpolation to create your dynamic path within your config/initializers/paperclip.rb initializer
Paperclip.interpolates :dynamic_path do |attachment, style|
file_name = attachment.original_filename.gsub(/#{File.extname(attachment.original_filename)}$/, '')
folder = attachment.instance.folder
"#{folder}/#{file_name}"
end
I have a simple static website written in rails 3.
The site has one controller called pages and each static page is served as view. Such as pages/home, pages/about, pages/prices, etc. This all works great.
I've now run into a problem where I need to add a simple contactus feature but I'm struggling to get my head round the model/controller/views for this.
I already have a pages controller with a contactus view, that view has details addresses etc. Now I somehow need to get a message model into the contactus view so I can populate the model attirbutes and send the email.
Can I / Should I just create a new message model from within the Pages Controller as in ,
class PagesController < ApplicationController
def contact
def new
#message = Message.new
end
def create
#message = Message.new(params[:message])
if #message.valid?
# TO DO send message here using OS mail program.
redirect_to root_url, notice: "Message sent! Thank you for contacting us."
else
render "new"
end
end
end
def about
end
def products
end
def portfolio
end
def services
end
end
Or should I take out the contactus view from the pages controller and make new controller called messages ?
Thanks.
I would have a separate controller called contact for example with new and create actions
def new
#message = Message.new
end
def create
#message = Message.new(params[:message])
if #message.valid?
NotificationsMailer.new_message(#message).deliver
redirect_to(root_path, :notice => "Message was successfully sent.")
else
flash.now.alert = "Please fill all fields."
render :new
end
end
end
Then a separate model to handle your messages
class Message
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name, :email, :subject, :body, :file
validates :name, :email, :subject, :body, :presence => true
validates :email, :format => { :with => %r{.+#.+\..+} }, :allow_blank => true
end
your attributes can be anything you like, obviously this is just an example of what you can do
My controller sends a ZIP file:
def index
respond_to do |format|
format.html { render :text => open("tmp/test1.zip", "rb").read }
end
end
PROBLEM: the ZIP is received as text shown in the browser.
I would like it to come as a download.
Note: I wrote format.html because when I write format.zip I get uninitialized constant Mime::ZIP. That is probably part of the problem.
You can register your own mime type:
Mime::Type.register "application/zip", :zip
def index
respond_to do |format|
format.html { ... } #do whatever you need for html
format.csv { ... } #do whatever you need for csv
format.zip { send_file 'your_file.zip' }
end
end
have a look here:
http://weblog.rubyonrails.org/2006/12/19/using-custom-mime-types
You should probably use ActionController::DataStreaming#send_file Take a look here:
http://api.rubyonrails.org/classes/ActionController/DataStreaming.html#method-i-send_file
You can skip the respond_to stuff and manually set the content type:
def index
render :file => '/full/path/to/tmp/test1.zip', :content_type => 'application/zip', :status => :ok
end
See the Layouts and Rendering in Rails guide for more information.
If you want to support .csv as well, then you could try looking at the params[:format]:
def index
if params[:format] == 'zip'
# send back the zip as above.
elsif params[:format] == 'csv'
# send back the CSV
else
# ...
end
end
And have a look at send_file as Marian Theisen suggests.
a simple solution for the user to download a file located in the application
def index
send_data File.read('/full/path/to/tmp/test1.zip'), filename: "test1.zip"
end
send_data will read your file located here /full/path/to/tmp/test1.zip and then send it as a response as a file
and your user download a file with filename test1.zip
I'm looking for a best practice/standard pattern for generating feeds in Rails 3. Is http://railscasts.com/episodes/87-generating-rss-feeds still valid?
First of all, nowadays I recommend using an ATOM feed instead of RSS.
The specification of ATOM feed offers more value than the RSS one with internationalization, content types and other things and every modern feed reader supports it.
More info about ATOM vs RSS can be found at:
the Wikipedia ATOM entry
PRO Blogger and Free Marketing Zone blog posts about the subject
On to the coding:
This example assumes:
a model called NewsItem with the following attributes:
title
content
author_name
a controller for that model (news_items_controller.rb), to which you'll add the feed action
We'll use a builder template for this and the Ruby on Rails atom_feed helper which is of great use.
1. Add the action to the controller
Go to app/controllers/news_items_controller.rb and add:
def feed
# this will be the name of the feed displayed on the feed reader
#title = "FEED title"
# the news items
#news_items = NewsItem.order("updated_at desc")
# this will be our Feed's update timestamp
#updated = #news_items.first.updated_at unless #news_items.empty?
respond_to do |format|
format.atom { render :layout => false }
# we want the RSS feed to redirect permanently to the ATOM feed
format.rss { redirect_to feed_path(:format => :atom), :status => :moved_permanently }
end
end
2. Setup your builder template
Now let's add the template to build the feed.
Go to app/views/news_items/feed.atom.builder and add:
atom_feed :language => 'en-US' do |feed|
feed.title #title
feed.updated #updated
#news_items.each do |item|
next if item.updated_at.blank?
feed.entry( item ) do |entry|
entry.url news_item_url(item)
entry.title item.title
entry.content item.content, :type => 'html'
# the strftime is needed to work with Google Reader.
entry.updated(item.updated_at.strftime("%Y-%m-%dT%H:%M:%SZ"))
entry.author do |author|
author.name entry.author_name
end
end
end
end
3. Wire it up with a route
Let's make the feeds available at http://domain.example/feed
This will call the action with the ATOM format by default and redirect /feed.rss to /feed.atom.
Go to config/routes.rb and add:
resources :news_items
match '/feed' => 'news_items#feed',
:as => :feed,
:defaults => { :format => 'atom' }
4. Add the link to ATOM and RSS feeds on the layout
Finally, all that is left is to add the feed to the layout.
Go to app/views/layouts/application.html.erb and add this your <head></head> section:
<%= auto_discovery_link_tag :atom, "/feed" %>
<%= auto_discovery_link_tag :rss, "/feed.rss" %>
I did something similar but without creating a new action.
index.atom.builder
atom_feed :language => 'en-US' do |feed|
feed.title "Articles"
feed.updated Time.now
#articles.each do |item|
next if item.published_at.blank?
feed.entry( item ) do |entry|
entry.url article_url(item)
entry.title item.title
entry.content item.content, :type => 'html'
# the strftime is needed to work with Google Reader.
entry.updated(item.published_at.strftime("%Y-%m-%dT%H:%M:%SZ"))
entry.author item.user.handle
end
end
end
You don't need to do anything special in the controller unless you have some special code like i did. For example I'm using the will_paginate gem and for the atom feed I don't want it to paginate so I did this to avoid that.
controller
def index
if current_user && current_user.admin?
#articles = Article.paginate :page => params[:page], :order => 'created_at DESC'
else
respond_to do |format|
format.html { #articles = Article.published.paginate :page => params[:page], :order => 'published_at DESC' }
format.atom { #articles = Article.published }
end
end
end
I have in my User view Index page a button_to tag as follows:
<%= button_to "Make Admin", :action => :make_admin :user => user %>
In the User controller i have:
def make_admin
#user = User.find(params[:id])
#changed_user.role = 3
#changed_user.save
end
I get a message about bad routing, but since I'm not interested in changing the view until after the action I don't know how to route this action. Where have i gone wrong?
You need to name the path in your routes:
# routes.rb
get 'your_path' => 'user#make_admin, :as => 'make_admin' # can use post too
# controller
def make_admin
# logic to make an admin
redirect_to(some_other_path, :notice => 'User was made an admin')
end
then, in your view,
button_to 'make admin', make_admin_path
You might also want to make the call remotely, but you'll need to post another question with more information in that sense