Continuing on from the [first part]({{< relref "/dev/rails-blog-part-1.md" >}}) of the Rails Blog tutorial we're going to flesh out our blog concept even further. Now we want to create an administrator role who has permissions to edit/delete other's posts in case they contain unsuitable material or are outdated and the connected account is deleted. There's a few possible ways of doing this, but what we're going to do is add a boolean attribute to the user model that signifies whether a user is an administrator. Firstly, run `rake db:reset` to reset the database, which we'll need to do because we're going to set the first registered user of the blog/new-site to be an administrator and they can promote other admins afterwards. Next run `rails g migration AddAdminToUsers admin:boolean` then open the newly created migration, it should be in db/migrate/YYYYMMDDHHMMSS_add_admin_to_users.rb with it's own timestamp in the title. Change the `change` method to look like the below (which gives a default value of false for administrator): def change add_column :users, :admin, :boolean, :default => false end Then run `rake db:migrate` to assign the changes to the schema and database. Next, go into app/controllers/posts_controller.rb and in the `index` method add the following before the `end` keyword: if current_user == User.first && !current_user.try(:admin?) && User.count == 1 current_user.update_attribute :admin, true end This checks if the user is the first user in the database, that they're not already an admin and that they're the only user in the database and if all three conditions are met (ie: the blog has just been set up) it sets the current user as an admin. Now we can test for if the current user is an admin with this: `current_user.try(:admin?)`, which returns true if the user is an admin and false if the current user is not an admin or there is no current user. If you're certain that a value contains a user, you can also user `@user.admin?`. So now we need to add some logic to the controller to allow the admins to do their work: # GET /posts/1/edit def edit if author_exists = User.where(:id => @post.user_id).first if current_user == author_exists || current_user.try(:admin?) else render :show end else if current_user.try(:admin?) else render :show end end end # PATCH/PUT /posts/1 # PATCH/PUT /posts/1.json def update if author_exists = User.where(:id => @post.user_id).first if current_user == author_exists || current_user.try(:admin?) respond_to do |format| if @post.update(post_params) format.html { redirect_to @post, notice: 'Post was successfully updated.' } format.json { render :show, status: :ok, location: @post } else format.html { render :edit } format.json { render json: @post.errors, status: :unprocessable_entity } end end else render :show end else if current_user.try(:admin?) if @post.update(post_params) redirect_to @post, notice: 'Post was successfully updated.' else render :edit end else render :show end end end # DELETE /posts/1 # DELETE /posts/1.json def destroy if author_exists = User.where(:id => @post.user_id).first if current_user == author_exists || current_user.try(:admin?) @post.destroy respond_to do |format| format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' } format.json { head :no_content } end else render :show end else if current_user.try(:admin?) @post.destroy redirect_to posts_url, notice: 'Post was successfully destroyed.' else render :show end end end For each method, it checks if the author of the post exists and if so, performs an action if the current user is the author OR the current user is an admin; in the case that there is no existing author an admin can still perform the appropriate actions on the post. Next we'll need to open up app/views/posts/index.html and edit the section with the Edit/Destroy links to look like so: <% if (current_user == post.user && post.user != nil) || current_user.try(:admin?) %> <%= link_to 'Edit', edit_post_path(post) %> <%= link_to 'Destroy', post, method: :delete, data: { confirm: 'Are you sure?' } %> <% end %> This makes it so if the current user is the post owner OR an administrator they can edit and destroy the posts through the view. We'll also need a way of promoting new admins and to do this we'll need to create a User controller. Now Devise performs some controls and gives some routes, but we need to create our own controller to add some actions. So run `rails g controller users`, then open up config/routes.rb and add `resources :users` after the other resource routes. Next open up the new app/controllers/users_controller.rb and make it look like: class UsersController < ApplicationController before_filter :authenticate_user! def index @users = User.all end def show @user = User.find(params[:id]) end def update @user = User.find(params[:id]) if current_user.try(:admin?) if current_user == @user && params[:user][:admin] == '0' && User.where(:admin => true).count == 1 render :show else if @user.update(user_params) redirect_to user_path(@user), notice: 'User was successfully updated.' else render :show end end else render :show end end def destroy if current_user.try(:admin?) @user = User.find(params[:id]) if current_user == @user && User.where(:admin => true).count == 1 render :show else @user.destroy redirect_to users_path end else render :show end end private def user_params params.require(:user).permit(:admin) end end The obvious classes (`index`, `show`) simply show all or a select User. `update` checks if the current_user is an admin and if they are, updates the selected user **unless** they happen to be the selected user, they're disabling admin and there's only one admin currently available (to prevent a "no admin on blog" situation occurring). `destroy` checks if the user is an admin and if so, deletes the selected user **unless** they are the selected user and there's only one admin on the board. Next we'll need to create some views to see this, so create app/views/users/index.html.erb and fill it with:

Listing users

<% @users.each do |user| %> <% if current_user.try(:admin?) %> <% else %> <% end %> <% end %>
Email Admin
<%= link_to user.email, user_path(user) %> <% if user.admin? %> Yes <% end %> <%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %>

<%= link_to 'Index', posts_path %>

This iterates through the users and displays them all, also showing if they're admins and allowing admins to delete users (under the constraints set out in the users controller). Also create app/views/users/show.html.erb and set it's contents to be:

<%= @user.email %>

<% if current_user.try(:admin?) %> <%= form_for @user, url: user_path(@user) do |f| %>

<%= f.label :admin %> <%= f.check_box(:admin) %>

<%= f.submit %> <% end %> <% end %>

<%= link_to 'Users List', users_path %>

This is where an admin can mark other users as admins or not, simple really. Lastly we'll need to open up app/views/posts/index.html.erb and change the last few lines to the following:

<%= link_to 'New Post', new_post_path %>

<%= link_to 'Users', users_path %>

This gives us a link to the users section that people can use to view current users and admins can use to admin! Now you should have a working administrator model with protections/capabilities all set out. You can apply the same ideas here to things such as the Rails Forum Skeleton project ([Part 1]({{< relref "/dev/rails-forum-skeleton-part-1.md" >}}), [Part 2]({{< relref "/dev/rails-forum-skeleton-part-2.md" >}}), [Part 3]({{< relref "/dev/rails-forum-skeleton-part-3.md" >}}), [Thoughts]({{< relref "/dev/rails-forum-skeleton-thoughts.md" >}})) and also expand it to create other roles including moderator or banned or whatever else you need.