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?) %>
Admin | |||
<%= link_to user.email, user_path(user) %> | <% if user.admin? %> Yes <% end %> | <% if current_user.try(:admin?) %><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %> | <% else %><% end %> |
<%= 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:<% 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.