I need help on creating these cookies for my Search options. I've read the Rails Guide and API but still don't understand how to make cookies correctly. I have two checkboxes that filter search results. They start out as already checked but what I need it to do is save the settings in a cookie if one of them is unchecked for the next searches. Here is a picture for better understanding:
Answer:
class SearchController < ApplicationController
def index
#title = "Search"
#page_title = "Search"
# Checkboxes are already checked.
params[:online_search_checked] = true
params[:offline_search_checked] = true
restore_cookie # if user enters this page in the future we restore checkboxes state from the cookies. For the first visit checkbox value will be true.
end
def results
update_cookie # each time user submits search we need to update cookies.
restore_cookie # each time user submits search we need to show search page with correctly checked check boxes.
#title = "Search"
#page_title = "Search"
#search = Product.search do |q|
q.fulltext params[:search]
q.with(:coordinates, params[:coordinates]).near(latitude, longitude, :precision => 3) if params[:coordinates].present?
q.with(:online_search, false) if params[:online_search].nil? # search user prices that are online automatically if checkbox is checked.
q.with(:offline_search, true) if params[:offline_search].nil? # search user prices that are offline automatically if checkbox is checked.
q.paginate(:per_page => 20, :page => params[:page])
q.order_by(:purchase_date, :desc)
q.order_by(:price,:asc)
end
#products = #search.results
end
def update_cookie
update_cookie_with_param(:online_search, :online_search_checked)
update_cookie_with_param(:offline_search, :offline_search_checked)
end
def restore_cookie
restore_param_from_cookie(:online_search_checked)
restore_param_from_cookie(:offline_search_checked)
end
def update_cookie_with_param(value_param_name, checked_param_name)
checked = params[value_param_name].nil? ? "false" : "true"
cookies[checked_param_name] = { :value => checked, :expires => 2.weeks.from_now }
end
def restore_param_from_cookie(checked_param_name)
if cookies[checked_param_name]
params[checked_param_name] = (cookies[checked_param_name] == "true")
end
end
end
# On index and result page in partial
<%= form_tag results_search_index_path, :method => 'get' do %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
<%= label_tag :online_search, 'Online' %>
<%= check_box_tag :online_search, 'online_search_value', params[:online_search_checked] %>
<%= label_tag :offline_search, 'Offline' %>
<%= check_box_tag :offline_search, 'offline_search_value', params[:offline_search_checked] %>
<% end %>
Cookie is a message given to a Web browser by a Web server. The
browser stores the message in a text file. The message is then sent
back to the server each time the browser requests a page from the
server.
In rails you can use cookies, cookies.permanent and cookies.signed to write cookies:
cookies - simple session cookies
cookies.permanent - create cookies
with expiration date 20 years in the future
cookies.signed - generate
signed representation of cookie to pvervent tampering of its value by
the end user.
To explain cookies usage I created two session cookies for your application: 'online_search_checked' and 'offline_search_checked'. They contain "true" or "false" values and represent whether appropriate checkbox is checked or not.
check_box_tag has following parameters: checkbox_name, checkbox_value, is_checked. We need to modify third parameter depending on the value received from the cookie. The second value may be a constant. If checkbox is checked, then params[:checkbox_name] == checkbox_value. If checkbox is unchecked, then params[:checkbox_name] == nil.
Here is the algorithm:
The first time when user hits 'search/index' he does not have
'online_search_checked' and 'offline_search_checked' cookies set.
Then you would like to show him both check boxes checked by default.
If this is not the first time then user already has cookies set and
we need to restore state of checkboxes from these cookies.
When user hits 'search/results' we update cookies with the current state of checkboxes
and restore state of checkboxes from these cookies.
Here is the code with my comments:
class SearchController < ApplicationController
def index
params[:online_search_checked] = true
params[:offline_search_checked] = true
restore_cookie # if user enters this page in the future we restore checkboxes state from the cookies. For the first visit checkbox value will be true.
end
def results
update_cookie # each time user submits search we need to update cookies
restore_cookie # each time user submits search we need to show search page with correctly checked check boxes
# Using Sunspot here.
#search = Product.search do |q|
q.fulltext params[:search]
q.with(:online_search, params[:online_search] == 1) if params[:online_search].nil?
q.with(:offline_search, params[:offline_search] == 0) if params[:offline_search].nil?
end
#products = #search.results # Sunspot rendering results.
end
def update_cookie
update_cookie_with_param(:online_search, :online_search_checked)
update_cookie_with_param(:offline_search, :offline_search_checked)
end
def restore_cookie
restore_param_from_cookie(:online_search_checked)
restore_param_from_cookie(:offline_search_checked)
end
def update_cookie_with_param(value_param_name, checked_param_name)
checked = params[value_param_name].nil? ? "false" : "true"
cookies[checked_param_name] = { :value => checked, :expires => 2.weeks.from_now }
end
def restore_param_from_cookie(checked_param_name)
if cookies[checked_param_name]
params[checked_param_name] = (cookies[checked_param_name] == "true")
end
end
end
# On index and result page in partial
<%= form_tag 'results', :method => 'get' do %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
<%= check_box_tag :online_search, 'online_search_value', params[:online_search_checked] %>
<%= check_box_tag :offline_search, 'offline_search_value', params[:offline_search_checked] %>
<% end %>
Hope this helps!
UPDATE
I do not quite understood what do you want to do here:
q.with(:online_search, params[:online_search] == 1) if params[:online_search].nil?
q.with(:offline_search, params[:offline_search] == 0) if params[:offline_search].nil?
In your code params[:online_search] == 1 and params[:offline_search] == 0 expressions will always be false because params[:online_search] and params[:offline_search] are nil according to if condition.
Related
I have a problem with Rails 4 and deleting multiple items in a database.
I am working on creating an email inbox and I would like to move an item into the "trash folder" and after that I want to redirect the user to the trash page, where he can delete the item from the database.
My code:
routes.rb
namespace :admin do
resources :inboxes do
collection do
match 'destroy_multiple' => 'inboxes#destroy_multiple', via: ['post','delete']
get 'sent'
get 'trash'
end
end
end
controller :
def destroy_multiple
if params[:action] == 'trash'
#del = Inbox.where(:id => params[:delete]).destroy_all
else
#del = Inbox.where(:id => params[:delete]).update_all(:folder =>'trash')
end
redirect_to admin_inboxes_path
end
And finally, my form (slim) :
= form_tag destroy_multiple_admin_inboxes_path, method: :delete, class: "multiple_delete" do
input type="hidden" name="action" value = controller.action_name
- #inboxes.each do |msg|
- #username = msg.email.gsub(/([^.]+)#.+/, '\1').gsub(/[^0-9A-Za-z]/, ' ').split.map(&:capitalize).join(' ')
tr class=(msg.read == 1 ? nil : 'unread')
td.inbox-small-cells
label.checkbox-custom.check-success
= check_box_tag "delete[]", msg.id, false, class: "for_del", id: "delete_#{msg.id}"
label for="delete_#{msg.id}"
td.inbox-small-cells
i.fa.fa-star.inbox-started
td
a.avatar href="/fr/admin/inboxes/#{msg.id}"
span.bg-primary =#username[0]
td.view-message.dont-show = #username
td.view-message = msg.subject
td.view-message.inbox-small-cells
td.view-message.text-right = msg.created_at.strftime("%d/%m")
I can with this code successfully move mail to the "trash", but I cannot delete them from the database.
Thank you!
You need to add the authenticity_token to your form. Try:
form_tag destroy_multiple_admin_inboxes_path, method: :delete, class: "multiple_delete", authenticity_token: true do
If you're working with a specific active record object, use form_for instead of form_tag, and the csrf field will automatically be built into the form by rails. However, this is a good time to be using form_tag.
The alternative solution is to change the config config.action_view.embed_authenticity_token_in_remote_forms to false, but this will make your site less secure, and it's better to just add authenticity_token: true to your form_tag.
Here is the documentation on form_tag
I'll start off with a bit of context to my question. I have a group of offices which each have reviewers associated with them. A reviewer can only be associated with one office. I want to create two select boxes. One lists all of the reviewers that are associated with the office I am viewing, the other lists all of the reviewers that are available (which is basically all of the reviewers that aren't already assigned to this office).
The goal of the current reviewer's listbox is to set their office to nil when they are selected. The goal of the available reviewers listbox is to set their office to this office's id when they are selected. I'm not sure how to change only the reviewers' office_id when using a select box.
Code-wise, what I have so far is this:
office_controller.rb
def edit
#office = Group.find params[:id] if params[:id]
#current_reviewers = Reviewer.find_all_by_group_id(#office.id)
#available_reviewers = Reviewer.where('group_id <> ?',[#office.id])
end
def update
?
end
office/edit.html.erb
<% form_for(#office, :url => {:controller => :office, :action => :update, :id => #office.id}, :html => {}) do |f| %>
...
<%= select_tag 'removedReviewers', options_from_collection_for_select(#current_reviewers, "id", "display_name"), :multiple => true %>
<%= select_tag 'chosenReviewers', options_from_collection_for_select(#available_reviewers, "id", "display_name"), :multiple => true %>
...
<% end %>
Any suggestions would be greatly appreciated. Thanks!
I solved this by adding the ability to get a select_tag to get the selected objects as a collection via the [] modifier of the select_tag name. So, in this example it was:
<%= select_tag 'removedReviewers[]', options_from_collection_for_select(#current_reviewers, "id", "display_name"), :multiple => true %>
And for the controller code to handle it, I did this:
#removedReviewers = params[:removedReviewers]
if !#removedReviewers.nil?
#removedReviewers.each do |reviewer|
#reviewer = Reviewer.find(reviewer)
#reviewer.group_id = nil
#reviewer.save
end
end
And the equivalent for the chosen/available reviewers.
I'm new to Rails and am fixing a Rails 2 site. I have a form that lets the user add information for the starting location (:start) EITHER with an input OR with a dropdown field. However, I have found that when I include both options, only the dropdown (which comes last) submits data, while the input is ignored. What's the right way to include both options?
MY VIEW
<% form_for #newsavedmap, :html=>{:id=>'createaMap'} do |f| %>
<%= f.error_messages %>
<p>Enter a street address, city, and state:
<%= f.text_field :start, {:id=>"startinput", :size=>50}%></p>
<p>Or, select a location from the list:
<%= f.select :start, options_for_select(#itinerary.locations), {:include_blank => true }, {:id=>"startdrop"} %>
<input type="submit" id="savethismap" value="Save Map">
<% end %>
One way to achieve this is by using virtual attributes. Since both fields map to same attribute, you are going to have to pick which one to use.
# app/models/newsavedmap.rb
class Newsavedmap < ActiveRecord::Base
...
attr_accessible :start_text, :start_select
...
def start_text=(value)
#start_text = value if value
prepare_start
end
def start_select=(value)
#start_select = value if value
prepare_start
end
# start_text will fall back to self.start if #start_text is not set
def start_text
#start_text || self.start
end
# start_select will fall back to self.start if #start_select is not set
def start_select
#start_select || self.start
end
private
def prepare_start
# Pick one of the following or use however you see fit.
self.start = start_text if start_text
self.start = start_select if start_select
end
end
Then your form needs to use the virtual attributes:
<%= f.text_field :start_text, {:id=>"startinput", :size=>50}%></p>
<p>Or, select a location from the list:
<%= f.select :start_select, options_for_select(#itinerary.locations), {:include_blank => true }, {:id=>"startdrop"} %>
Other options are:
Use text_field as the primary and update it's value with selected option if user selects an option.
Add a hidden field in your form and use JavaScript to update the hidden field's value when text_field text gets updated or select option changes
I have a user model that contains 2 booleans in the database - admin, and readonly. Basically this allows me to have 3 levels of access (readonly, editable, and admin).
I have a screen that allows admins to edit other users permissions. Instead of having 2 checkboxes for the boolean values of admin and readonly, i created a dropdown using f.select and i created a virtual attribute called "permission".
Everything works fine when saving a user as far as permissions go, the only thing is that when you go to the edit page for a particiular user, it does not load the page with the user's actual permission in the dropdown. It just loads the first value in the dropdown as the default value.
How can i make it so this dropdown shows the users actual permission when first loading the user edit page?
Here's my virtual attribute code for my User model:
# get the permission
def permission
if self.read_only
#permission = 'readonly'
elsif self.admin
#permission = 'admin'
else
#permission = 'editable'
end
end
# Set the permission.
def permission=(value)
p "VALUE = #{value}"
if value == 'readonly'
self.read_only = true
self.admin = false
#permission = 'readonly'
elsif value == 'admin'
p "INSIDE admin"
self.read_only = false
p "before #{self.admin}"
self.admin = true
p "after #{self.admin}"
#permission = 'admin'
elsif value == 'editable'
self.read_only = false
self.admin = false
#permission = 'editable'
end
end
and here's my f.select in my form view:
<%= f.select :permission, options_for_select([['Admin', 'admin'], ['Read Only', 'readonly'], ['Editable', 'editable']], {:disabled => #permissions_disabled}) %>
I tried using collection_select, but couldn't figure out how to populate the list.
as stated in Rails f.select trying to disable a dropdown from being changed, you dont need the options_for_select. If you remove it and pass the fourth parameter a selected option, you should be set.
<%= f.select :permission, [['admin', 'Admin'], ['readonly', 'Read Only'], ['editable', 'Editable']], { selected: 'readonly' }, { disabled: #permissions_disabled } %>
but given the code above, even without the selected option, as long as f.object.permission returns the right value, you should be fine.
Try adding this to your last options hash: :selected => #user.permission
For example:
<%= f.select :permission, options_for_select([['Admin', 'admin'], ['Read Only', 'readonly'], ['Editable', 'editable']], {:disabled => #permissions_disabled, :selected => #user.permission}) %>
Hello I have the followong struggle in my head. I want a text-field in which the use can type in some parameters, which will be used as filter-criteria for the :conditions hash in my find method.
I have created a helper, with takes an option and merge the hash to the options:
In my controller:
#bills = adminbill_filter(:limit=>params[:limit] || 50,:offset=>params[:offset] || 0, :conditions=>params[:options])
In my helper:
def link_to_with_current(text, link, condition, *args)
options = args.first || {}
options[:class] = condition ? 'current' : nil
link_to text, link, options
end
In my view:
<%= text_field :filter ,:criteria, :class=>'roundRect',:id=>'name', :value=>12009%>
<%= button_to_with_filter 'Start Filter', 'index', :filter_condition=>true, :options=>{:id=>81}%>
Is it somehow possible to pass the value of text_field into the :option=>{...} of the button_to_with_filter? I find this solution (if it is working) quite unhandy. Your comments are as always very helpful.
Greetings
Matthias
It seems kind of terrifying to put in the contents of user-submitted params without vetting them in any capacity. You're probably going to run into all kinds of exceptions if the data doesn't come in as expected, or is formulated to be malicious.
I've found it's often easier to use a chained scopes approach:
def index
bills_scope = Bill
# Use an example Bill.with_id scope
if (params[:with_id])
bills_scope = bills_scope.with_id(params[:with_id])
end
# Repeat as required
# Finally, use the scope to retrieve matching records
#bills = bills_scope.paginated
end
Using something like will_paginate can help with your offset and limit values.
If the text field and button were encapsulated in a form, and the button was the submit button, the text field's value would automatically be brought into the params hash. Then you wouldn't have to deal with it. I can't recall at the moment the exact Rails helpers that will do this for you, but you want the resulting form to probably be something like this:
<% form_for :options, :url => {:action => :index}, :html => { :method => :get } do |f| %>
<%= f.text_field :filter ,:criteria, :class=>'roundRect',:id=>'name', :value=>12009%>
<%= f.submit 'Start Filter' %>
<% end %>
Which may change some, since I don't know the underlying code behind your methods.
Otherwise, the only thing I can think of is using a Javascript event on the button that grabs the value of the text field before it submits.
Thanks for your help, I came across named_scope and solved the problem with the following code:
Bill model:
class Bill < ActiveRecord::Base
# named_scope werden fuer Filterfunktionen bei Adminbill benoetigt
named_scope :standard, :order => "created_at DESC"
named_scope :limit, lambda {|*args| {:limit=>(args.first)|| 50}}
named_scope :offset, lambda {|*args| {:offset=>(args.first || 10)}}
named_scope :paid, :conditions=>"paid IS NOT NULL"
named_scope :not_paid, :conditions=>{:paid=>nil}
named_scope :test_bill, :conditions => {:test_bill=>true}
named_scope :no_test_bill, :conditions => {:test_bill=>false}
named_scope :find_via_bill_id, lambda {|*args|{:conditions=>{:id=>(args.first || 210)}}}
named_scope :find_via_email, lambda {|*args| {:conditions=>{:buyer_id=>args.first}}}
controller:
def index
logger.debug "The object is #{current_user}"
if params[:filterInput] != nil && !params[:filterInput].empty?
filter_array = params[:filterInput].split('&')
bill_scope = Bill.scoped({})
bill_scope = bill_scope.standard
# Filtere via Regexp-Matching die Zahlen der Eingabe heraus
filter_array.each do |el|
if el =~ /limit\([0-9]+\)/
number =
bill_scope = bill_scope.limit(el.scan(/\d+/)[0])
elsif el =~ /offset\([0-9]+\)/
bill_scope = bill_scope.offset(el.scan(/\d+/)[0])
elsif el == 'paid'
bill_scope = bill_scope.paid
elsif el == 'not_paid'
bill_scope = bill_scope.not_paid
elsif el == 'test_bill'
bill_scope = bill_scope.test_bill
elsif el =~ /find_via_bill_id\([0-9]+\)/
bill_scope = bill_scope.find_via_bill_id(el.scan(/\d+/)[0])
elsif el =~ /find_via_email\([A-Za-z0-9.#-]+\)/
email = el.scan(/\([A-Za-z0-9.#-]+\)/)[0]
# TODO geht bestimmt auch eleganter durch besseres Matching
email = email.gsub("(", "")
email = email.gsub(")", "")
user = User.find_by_email(email) unless User.find_by_email(email).blank?
bill_scope = bill_scope.find_via_email(user.id)
end
end
#bills = bill_scope
else
#bills = Bill.standard.limit.offset
end
And in the view:
<% form_tag(:action => 'index') do %>
<%= text_field_tag 'filterInput', nil, :size => 40 %>
<%= submit_tag 'Start Filter'%>
<% end %>
Now you can pass in the tex-field e.g.the following valid expression: paid&limits(20)
I know that the controller solution isn't very elegant but for me it was the fastest way to solve this problem.