Allowing users to choose custom theme in Rails - ruby-on-rails

I want to give my users the ability to choose how their public page is displayed from 5 different layouts. I assume I'll need 5 different css files according to layout and then need to pass that into stylesheet_link_tag
I know only how to do it using if then statements. I don't suppose that is the best way. Any help...also could it be done?
Thanks

You should store the layout that the user has chosen in the session variable (easiest, but lost when the user clears cookies or uses a different computer), or in your database.
Lets say the stylesheets have five names, each corresponding to a color:
blue_stylesheet.css
green_stylesheet.css
red_stylesheet.css
orange_stylesheet.css
white_stylesheet.css
Place these files inside of public/stylesheets.
Store the user's choice of stylesheet into the session[:style] variable like so:
session[:style] = 'green'
This value will persist for as long as the user does not clear their cookies.
Create an application.erb file in your layouts if one does not already exist. The code in this file will be rendered for every template on your site. It should contain a line like <%= yield %>. In this file place the following:
<%=stylesheet_link_tag session[:style]+'_stylesheet'%>
That's it!
Good luck!

First, try to add 'theme' field to user's model (using migrations).
Then add some links in a view (user's settings):
link_to 'Change to green theme', :controller => "user", :action => "set_theme", :id => "green"
Controller:
def set_theme
# don't forget to check, is there a theme with such params
current_user.update_attributes :theme => params[:id]
end
Public profile's controller:
def public_profile
#theme = 'default'
user = User.find(params[:user_id]) # profile's owner
#theme ||= user.theme # overriding default theme to custom one
end
layout:
<%=stylesheet_link_tag #theme %>

Related

Personalised views for a specific Rails controller

The goal is to generate views for given class that are personalised to a given user.
Model and Controller logic remain the same, but the layout and record presentation would be subject to user preferences.
Given the unicity of User.id it is readily available to create application_controller logic to use a layout with that id.
layout = #user.layout
The layout would be able to invoke its personalised CSS.
But what of the views? and the routing? Given a class Product how can the
resources :products
invoke a subdirectory of app/views/products to use that of the user's id, say
app/views/products/10
Thus leading to a structure of files
app/views/products/10/show.html.erb
app/views/products/10/edit.html.erb
app/views/products/10/index.html.erb
app/views/products/11/show.html.erb
app/views/products/11/edit.html.erb
app/views/products/11/index.html.erb
app/views/products/12/show.html.erb
app/views/products/12/edit.html.erb
app/views/products/12/index.html.erb
Answering your question you have call render in each method in the controller.
def show
...
render "user/show/#{params[:id]}.html.erb"
end
You have more info here: https://guides.rubyonrails.org/layouts_and_rendering.html#using-render
But, unless they are TOTALLY different it would be better using the same view for every record and keeping a field for each one with options to manipulate the view.
#user.layout = {header: true, footer: false, ...}
And in the view:
<% if #user.layout[:header] %>
<div>....</div>
<% end %>

Differentiate between multiple registration pages for same user model

I have a fairly vanilla registration system for my Rails 4 app, and I'd like to add a strange feature. I'd like to provide multiple registration forms, and secretly set a value on the new User object, based on which page the data is being submitted from.
My current way of doing it is bad for security reasons. It works fine, but a savvy user could use a browser's dev tools to change the value of the hidden field and indicate that they came in from a different page.
routes.rb
get '/signup', to: 'users#new'
get '/red-signup', to: 'users#new', color: 'red'
get '/blue-signup', to: 'users#new', color: 'blue'
users_controller.rb
def new
#user = User.new
#color = params[:color] || 'grey' # default of grey
end
users/new.html.erb
<%= form_for(#user) do |f| %>
...
<%= f.hidden_field :color, value: #color %> # VULNERABLE TO TAMPERING
<%= f.submit "Create my account" %>
<% end %>
So, I'd like a tamper-proof way to differentiate between new registrations that came from different pages. I assume that means keeping any signifying tokens out of the form data.
Any suggestions for an approach to explore?
If your aim is to pass the #color value safely between new and create action, you can use the session for this purpose. Inside your new action you set the color:
def new
#user = User.new
session[:color] = params[:color] || 'grey' # default of grey
end
Then in the create action you can retrieve it:
def create
#color = session[:color]
end
Rails session data is stored in Cookies by default and is cryptographically signed to make it tamper-proof. It is also encrypted so anyone with access to it can't read its contents.
What is the final purpose?
If it is to know which registration forms has a better conversion rate, you are reinventing the wheel and you should have a look on A/B testing and those gems for doing it right.

Dynamic routes on runtime in Rails

I'm developing a site using refinery. Now for one specific page that is created in the back-end of refinery, i want to use my own controller and views. All the User can do with this page is to set the menu-position, title, meta-info etc. The URL for this page has to look the same as all the other pages.
So for example, the menu structure looks like:
menux
menu1
menu2
specific page
menux
And the URL for "specific page" looks like "locale/menu1/menu2/specific page"
The site is available in multiple languages, so i have to create these routes for all languages.
Currently i'm creating the routes like this:
specific_page_id = 1
Refinery::I18n.frontend_locales.each do |lang|
slugs = []
page = Refinery::Page.find_by_path_or_id(nil, specific_page_id)
# get slug for page in current language
slugs << page.translations.select { |p| p.locale == lang }.first.slug
# get all slugs from parrent pages
while !page.parent_id.blank?
page = Refinery::Page.find_by_path_or_id(nil, page.parent_id)
slugs << page.translations.select { |p| p.locale == lang }.first.slug
end
match "/:locale/#{slugs.reverse.join("/")}" => "controller#action", :via => :get, :constraints => { :locale => /#{lang}/ }
end
With this, i'm getting a route to the specified page in every language like described above.
But the problem is, when the user changes the name of the page or the position in the menu, the routes have to be generated again, which isn't done too often.
Now my question is, how can i do this more dynamically on run-time? I've read a bit about constraints but i don't know if this is what i need.
Thanks for your help!
I needed to figure out building routes off a database model myself in a Rails 4 application (which is called "ComingSoon" in the examples below. I wanted pages that could be edited on the back-end and given a user-friendly name, which is stored in the Page#name field. So "About Us" titled page typically becomes "about_us" name, which leads to "http://localhost:3000/about_us" The following is the technique I came up with:
Create a new model in app/models/dynamic_router.rb
class DynamicRouter
def self.load
ComingSoon::Application.routes.draw do
Page.all.each do |pg|
get "/#{pg.name}", :to => "pages#show", defaults: { id: pg.id }, as: "pages_#{pg.name}"
end
end
end
def self.reload
ComingSoon::Application.routes_reloader.reload!
end
end
The key above is that I pass the page's id as one of the parameters, so look up is still on the Page#id field, which is, IMHO, a lot better than using the friendly plugin or lookups on slugerized values.
Add the following line to your config/routes.rb
ComingSoon::Application.routes.draw do
# ...
DynamicRouter.load
end
Finally, when the Page is updated, we need to reload the routes, so add an after_safe callback on the Page model:
class Page < ActiveRecord::Base
after_save :reload_routes
def reload_routes
DynamicRouter.reload
end
end
I plan to refine this further to only reload routes if the name attribute is changed and perhaps simply edit the existing route rather than reloading everything if performance proves to be an issue (which at the moment, its not).

Rendering polymorphic object in Rails

For people that only want the question, here it is :
Is there a way to specify the folder to look in when you call render on an object? I don't want to specify the view, only the folder to look in.
And for people that want context :
I am working on an activity stream system (something that looks like google+/facebook).
I have "Activities", which are exactly like google+ feeds (or facebook, or whatever!). So, I have a simple loop that display each activities, which are bound to one of the following object (polymorphic) : User, Group, Comment, Note.
In my view that render an activity (views/activities/_activity.html.erb), I have
<%= render activity.object %>
where activity.object is a reference to the bound object (User, Group, Note, Comment). If it's a user, it goes to views/users/_user.html.erb and renders it. For a group, views/groups/_group.html.erb.
That works just fine. However, I come to the point where the rendering of a group in my activities should not be the same rendering as in the group list page. Is there a way to specify the folder to look in when you call render on an object? So that my :
<%= render activity.object %>
would become :
<%= render activity.object, :folder => 'views/activities/' %>
Note that I don't want to specify which view directly, as I don't want to do a case for each of the possible types of objects (User, Group, Note, Comment) in the activity. I want to to have the same behaviour as of right now, which means if it finds a views/activities/_user.html.erb, it would load any user in the activities with that view instead of the one in the views/users/_user.html.erb.
Thanks
I'm not aware of any folder type option, but when I do this I usually do:
<%= render "activities/#{activity.object.class.name.underscore}" %>
That would give you similar behaviour.
EDIT A good point below by Dominic, if your classes are nested in namespaces, you will have to include the appropriate structure.
i.e.
module Foo
class Bar < ActiveRecord::Base
end
end
# class.name is Foo::Bar, underscored is 'foo/bar'
<%= render "activities/#{activity.object_type.underscore}" %>
# will be in
activities/foo/_bar.html
In current Rails (3.2.9), you can define a to_partial_path method on your model to tell it where to look.
For example, you can do:
class User
def to_partial_path; "user"; end
end
Rails will then look for _user.html.erb relative to the view from which you're calling render.
My own approach, which I extracted in polymorphic_render gem - to add suffixes for special partials, but store that partial in resource folders.
In your case views/users/_user.html.erb will have very common user representation (which probably used in users list rendering), but views/users/_user_activity.html.erb will render special partial for user activity.
And inserting that partials is very simple, just <%= polymorphic_render activity.object, :activity %>

Rails: Create a custom UI for a single specific object

I've run into a problem I'm completely unsure how to approach.
I have an app for sharing architectural photos. Users have_many Photos, and users can create Collections which also have_many Photos.
Now I have one customer who is a big name in the industry who would like to work with me to create a totally customized Collection with a very different look and feel from "regular" collections, but essentially the same functionality underneath. I'd like to accommodate this request, but I really have no idea how to do it.
Given that I already have a functioning Collection model and CollectionsController, plus all the views, I'd like to re-use as much of that as possible. So, for instance, the custom Collection needs to override the user facing :show view, but not the admin :edit view.
How would you approach something like this?
I'm trying to understand the most efficient, DRY method for creating a completely custom UI for a single record in the database. I'd be very appreciative of suggestions, including links to articles / books etc, as I haven't been able to find much in this area.
I would allow the creation of Liquid view templates associated with a User and/or Collection (if you want both - per-user templates with per-collection variations - use a polymorphic association) and of course fall back to your default view (also built with Liquid for consistency and reference) for all cases where no custom template is found.
Edit to add suggested details:
Any custom templates should be stored in the database (I would add a test/preview function so the user entering a custom template has the chance to verify their template before publishing it):
# Table name custom_templates
# id :integer
# templatable_type :string
# templatable_id :integer
# contents :text
class CustomTemplate < ActiveRecord::Base
belongs_to :templatable, :polymorphic => true
end
class User
has_one :custom_template, :as => :templatable
end
class Collection
has_one :custom_template, :as => :templatable
end
In your controller action, look for a custom template:
custom_template = #collection.custom_template
custom_template ||= #user.custom_template
#custom_template = Liquid::Template.parse(custom_template.contents) if custom_template
In your view, either render the custom template or your default template partial:
<% if #custom_template -%>
<%= #custom_template.render(_hash_of_objects_to_pass_to_liquid_template_) %>
<% else -%>
<%= render :partial => 'default' %>
<% end -%>

Resources