Custom Settings Form in ActiveAdmin - ruby-on-rails

I'm using rails-settings by Squeegy from https://github.com/Squeegy/rails-settings as well as Activeadmin. What I'm trying to accomplish is making a form in ActiveAdmin that I can let the site admin change the settings for the site, which take a command line syntax of:
Setting.foo = "bar"
Setting.site_title = "My Awesome Site!"
Setting.max_users = 35
I really don't think I've got too far, but I'm already stuck. I'm up to the point of having a custom ActiveAdmin form made:
ActiveAdmin.register_page "Settings" do
action_item do
link_to "View Site", "/"
end
content do
form do |f|
#Inputs for Settings
end
end
end
But I don't even know how to begin laying out the form to directly access the Settings model, or how to make a custom controller to handle the input. I suppose if I could get the input sent to a controller that I could make, I'd be just fine.

This is very simple to do with ActiveAdmin.
Lets say your settings class is Settings :
ActiveAdmin.register_page "Settings" do
content do
table :class => 'settings' do
thead do
th 'Setting'
th 'Value'
th ''
end
Settings.all.each do |key, val|
tr do
td strong key
td val
td do
link_to "delete", admin_settings_delete_path( :key => key ), :method => :post
end
end
end
tr do
form :action => admin_settings_create_path, :method => :post do
td do
input :name => 'key'
end
td do
input :name => 'val'
end
td do
input :type => 'submit', :value => 'Add'
end
end
end
end
end
page_action :create, :method => :post do
Settings[params[:key]] = params[:val]
redirect_to :back, :notice => "#{params[:key]} added"
end
page_action :delete, :method => :post do
Settings.destroy params[:key]
redirect_to :back, :notice => "#{params[:key]} deleted"
end
end
Of course you'll need to add some CSS and maybe some validations but you have your settings page.
Edit:
Note that I wrote this for rails-settings-cached, not rails-settings, but my quick search led here so I guess this could still help someone.

I don't think you want your site's form to directly change the settings in ActiveAdmin, I would ...
Create a new table, eg. adminsettings and add fields for each of the settings you want to store for instance site_title, alternatively you could use each row for a setting which means you can add new settings in the future without changing the database
Put together a form in Activeadmin to maintain your settings
Add some functions to your model to grab the settings so you can do something like ..
Setting.site_title = Adminsetting.getsitetitle
You could be clever with your model method and use the method_missing facility so you need the least amount of code to get a setting ...
class << self
def method_missing(method, *args, &block)
setting = Adminsetting.where(:code => method.to_s).first
if setting
return setting.content
else
return super(method, *args, &block)
end
end
Perhaps you could package this into a Gem as it could be a useful thing for others.

Related

Rails linking to another controller in different view

I this view is currently in the views/projects/show.html.erb file however I want it to use the website controller for deleting this file:
<%= link_to 'Delete', #website, :controller => 'website', :action => 'delete', method: :delete, data: {confirm: "Are you sure you want to delete this asset?"}%>
It returns the error 'Could not find action destroy in the ProjectsController'. Also i don't have #website defined in the projects controller so should I be using something else? Or am I still able to access it because it is defined in the websites controller.
#controllers/websites_controller.rb
class WebsitesController < ApplicationController
def new
#project = Project.find(params[:project_id])
#website = #project.assets.build(:type => 'Website', :project_id => Project.find(params[:project_id]), :asset_number => #project.assets.size + 1)
end
def create
#website = current_user.assets.build(website_params)
#website.update_attributes(:project_id => #project)
if #website.save
flash[:notice] = "Asset successfully added."
redirect_to(:controller => 'projects', :action => 'show', :id => #website.project_id)
else
render(:action => 'new')
end
end
def delete
#website = Asset.find(params[:id])
end
def destroy
asset = Asset.find(params[:id]).destroy
flash[:notice] = "The asset '#{asset.title}' has been destroyed Successfully."
redirect_to(:controller => 'projects', :action => 'index')
end
private
def website_params
params.require(:website).permit(:id, :project_id, :asset_number, :title, :type, :url, :page_rank, :rev_company ,:social_pages)
end
end
If you are using this link on the show page for projects then #website will not be available unless it is defined in the projects controller.
That said, if there is some relationship between the project and the website, you could use that as opposed to defining #website in your projects controller.
Also, as far as your link_to is concerned, I do not believe that you can specify controller and action in the link_to like that. Instead, you should use the path to #website. Which should make your link_to look something more like this:
<%= link_to "Delete", website_path(#website), method: :delete, data: {confirm: "Are you sure you want to delete this asset?" %>
However, the model that your websites_controller appears to handle is actually an Asset. Without seeing your routes it is hard to guess how you have set them up, but assuming that you do something like
map.resources :assets, :controller => 'websites'
in your routes. Then in your link_to instead of using website_path(#website) you would likely use asset_path(#website).
Generally speaking, it is rarely a good idea to defy rails convention by naming things inconsistently from your model in ruby. If your Asset model uses single table inheritance or you are implying something like single table inheritance and are using controllers to separate responsibilities, then this may perhaps be an exception, but you will still need to be careful to ensure you are mapping to the correct place in your routes.
You may want to read up on the rails guide for routing, as it is a very good resource and explains pretty well how destroy gets mapped, which in turn explains why the link_to for it looks the way that it does.
#website available in the show action is the the one defined in the projects controller because it is he one rendering the current html page.
Therefore the one you wish to delete is not available at the moment.

Rails - how to change <select> to be just a hidden field when called with an id?

I have a form with a <select> element for the Group.
[The application stores bookmark links and groups for them]
When doing a 'new', everything work correctly. The new form works, the <select> dropdown has the list of groups and has their ID's.
The problem now is that I want to call the 'new link' from a different place that will already know the group. In this case I don't want an option to select the group, I just want to use the group id passed to it.
The form itself has:
= f.select :group_id, #groups
My routes include:
resources :groups do
resources :links # Added so that I can do group/:id/link/new ...
collection do
post 'order_links'
end
end
match 'search' => 'links#index'
match 'advanced_search' => 'links#advanced_search'
resources :links do
collection do
get 'groups'
end
end
The controller that's showing the form for this "new link" has:
def new
#link = Link.new
#groups = Group.all.collect { |g| [g.group_name, g.id] }
#group_name =
if params[:group_id]
'for the '+Group.find(params[:group_id]).group_name + ' group.'
else
''
end
respond_to do |format|
format.html
end
end
I tried changing the view to have this:
-if params[:group_id]
= f.hidden_field :group_id, :value => params[:group_id]
-else
= f.select :group_id, #groups
but it didn't work, I still got the <select> element, defaulting to its first <option>.
I actually had this working as some point in the past but has broken since, so hopefully I'm fairly close.
The indentation in your HAML template is wrong. It should be:
- if params[:group_id]
= f.hidden_field :group_id, :value => params[:group_id]
- else
= f.select :group_id, #groups
(If that was just the way it came out when you entered the question, I will delete this answer.)

How to Override Form Heading or Title in ActiveScaffold

Is there an easy way to override the title on an ActiveScaffold page? I know I can customize the name of the model part, but what if I want to say "Let's make a new widget, shall we?" instead of "Create Widget"? It seems overkill to override the whole template or partial just to get a new heading.
The ActiveScaffold default _create_form_html.erb is
<% form_action ||= :create %>
<%= render :partial => "base_form", :locals => {:xhr => xhr ||= nil,
:form_action => form_action,
:method => method ||= :post,
:cancel_link => cancel_link ||= true,
:headline => headline ||= active_scaffold_config.... } %>
Is there a way to set that headline variable from inside the controller?
Inside the scaffold config write:
conf.list.label = 'The list label'
conf.create.label = 'Let's make a new widget, shall we?'

Question about URL friendly links

I am trying to get my urls to look like this:
example.com/posts/id_of_post/title_of_post
I have this in my controller:
match ':controller/:id/:link', :controller => 'posts', :action => 'show'
Say I have a list of posts.. how can I link to them?
<%= link_to 'Show', post %>
Just gives the usual /posts/id
On another note, at the minute I am making a url-friendly link when a post is created and storing it in the database. Would it be better to create on the fly? Is that possible/better?
I saw this in an answer to another question:
def to_param
normalized_name = title.gsub(' ', '-').gsub(/[^a-zA-Z0-9\_\-\.]/, '')
"#{self.id}-#{normalized_name}"
end
That would work if I could change the - to a /. Possible?
I recommend just doing this instead of the gsub stuff:
def to_param
"#{self.id}-#{title.parameterize}"
end
Downside is that if the title changes, the URL changes. Which is a downer.
So a lot of implementations will do
before_create :permanize
def permanize
permalink = title.parameterize
end
def to_param
"#{self.id}-#{permalink}"
end
This is what I did:
I added this to my post#create:
#post.link = (#post.title.parameterize)
I will give the user the option to edit the title for up to 5 mins after posting.
My route:
match "/posts/:id/:link" => "posts#show", :as => "story"
and my index view for posts
<%= link_to 'Show', story_url(post, post.link) %>

Rails: Custom template for email "deliver_" method?

I'm building an email system that stores my different emails in the database and calls the appropriate "deliver_" method via method_missing (since I can't explicitly declare methods since they're user-generated).
My problem is that my rails app still tries to render the template for whatever the generated email is, though those templates don't exist. I want to force all emails to use the same template (views/test_email.html.haml), which will be setup to draw their formatting from my database records.
How can I accomplish this? I tried adding render :template => 'test_email' in the test_email method in emailer_controller with no luck.
models/emailer.rb:
class Emailer < ActionMailer::Base
def method_missing(method, *args)
# not been implemented yet
logger.info "method missing was called!!"
end
end
controller/emailer_controller.rb:
class EmailerController < ApplicationController
def test_email
#email = Email.find(params[:id])
Emailer.send("deliver_#{#email.name}")
end
end
views/emails/index.html.haml:
%h1 Listing emails
%table{ :cellspacing => 0 }
%tr
%th Name
%th Subject
- #emails.each do |email|
%tr
%td=h email.name
%td=h email.subject
%td= link_to 'Show', email
%td= link_to 'Edit', edit_email_path(email)
%td= link_to 'Send Test Message', :controller => 'emailer', :action => 'test_email', :params => { :id => email.id }
%td= link_to 'Destroy', email, :confirm => 'Are you sure?', :method => :delete
%p= link_to 'New email', new_email_path
Error I'm getting with the above:
Template is missing
Missing template
emailer/name_of_email_in_database.erb in view
path app/views
Try multipart may that work
def test_email
#email = Email.find(params[:id])
Emailer.send("deliver_#{#email.name}")
part :content_type => 'multipart/alternative' do |copy|
copy.part :content_type => 'text/plain' do |plain|
plain.body = render( :file => "conta.text.plain.erb", :email=>#email )
end
copy.part :content_type => 'text/html' do |html|
html.body = render( :file => "conta.text.html.erb", :email => #email )
end
end
end
Aph, I feel silly. I figured it out:
models/emailer.rb:
class Emailer < ActionMailer::Base
def method_missing(method, *args)
logger.info "method missing was called!!"
recipients "Test <test#test.com>"
body "#{ Email.find_by_name(method.to_s).body }"
end
end
Since the incoming method is basically the name of the record, I can pull the body content stored in the database directly and pass that in as the body of the email, bypassing the templates altogether.
I'd go "up a level"
In other words, use the same view for everyone's email. But in the view, render different text depending on the user_id.
Remember that the view can call the render method.
In my case, I let users upload email templates using the liquid template system. (Google for rails liquid templates.) Using liquid is good since it is safe--users can't include arbitrary ruby code in their templates.
Then my email view renders the liquid template and the customized email is thereby generated.

Resources