Skip to content

Articles

amonfog edited this page Jun 10, 2013 · 17 revisions

Articles are the main engine on the website. The articles are updated by the different users in the system and can be watched after an admin's approval more info on the admins roles can be found in the Dashboard article.

##Article Model The article makes use of a model that gives it access to the articles database tables. It also defines the connection to other models.

###The Article model

class Article < ActiveRecord::Base
  attr_accessible :content, :picture, :tags, :title, :user_id
  belongs_to :user
  has_many :favorites
  has_many :reviews, :order => "created_at DESC", :dependent => :destroy
  mount_uploader :picture, PictureUploader
end

The article model is wrapped in a class. The class defines this model in to Articles range and the active record tells the system that it's dealing with base (in this case a base-model)

Let's break this model down to a smaller level.

####accessible

  attr_accessible :content, :picture, :tags, :title

the first line of code int he class allows the rails app to access and change the columns content, picture, tags and title in the articles table.

####relations

  belongs_to :user

every article belongs to a user. User id's are linked to each article trough the user_id column in the articles database-table. users can have more than one article, but the article can only have one user. more info on the linked user model can be found in the Users article.

  has_many :favorites

articles can be Favorited by many users the favorites are stored in their own table. more info on the favorites table and the connected model can be found in the Favorites article.

  has_many :reviews, :order => "created_at DESC", :dependent => :destroy

users can add their comments, reviews and responses to each article. the reviews table stores these. when the reviews are called in this model they are automaticly sorted to show the newest on top. the dependent allows controll over the destroy controller in the reviews controller. More info on reviews can be found in the Reviews article.

####Mounts

  mount_uploader :picture, PictureUploader

with this small line of code the Picture Uploader is added to the picture column of the database. The more specific file that is mounted here is picture_uploader more info on the picture can be found in the pictures and Avatars article.

##The Articles controller The controller is the next big thing on our list. This file defines the existance of the diffrent pages that are presented to visitors and registered users. It also defines certain actions like deleting artiles, updating them, etc.

###The Controller

class ArticlesController < ApplicationController
end

The controller is wrapped in a class called ArticlesController (conveniently) this class is part of the applications controller. In the Application Controller basic functions are set and the anti-forgery things are stored there as well. more info on the Applications core functions and Application Controller can be found in the Application article.

####Before filters

before_filter :authenticate_user!, only: [:new, :create, :edit, :save]

after defining the class the controller is started with a before filter. this filter is here to protect the articles from interference and changes of non-registered users

Let's break this controller down to it's methods level.

####Index def index @title = "Inspiratieplanet - Alle artikelen" @subtitle = "Alle artikelen" @articles = Article.find(:all, :conditions => { :accepted => true }) respond_to do |format| format.html # index.html.erb format.json { render json: @articles.to_json( :include => [

          :reviews => {:include => { :user => {:except => [:admin, :email, :updated_at, :created_at]  } }},
          :user => {:except => [:admin, :email, :updated_at, :created_at]}
        ]
      )}
    end
  end

the index allows users to view all or newest articles. from the database.

#####variables

@title = "Inspiratieplanet - Alle artikelen"
@subtitle = "Alle artikelen"
@articles = Article.find(:all, :conditions => { :accepted => true })

the value of the @title variable defines the title of the page in the top of the browser the value of @subtitle defines the content of the <h1> tag bellow the header of each page. the @title and @subtitle are handled in the Application Helper. The functions are called in line 4 and 138 of application.html.erb. More info on the title and subtitle can be found in the Application article.

#####Responses

respond_to do |format|
  format.html # show.html.erb
  format.json { render json: @article.to_json(
    :include => [
      
      :reviews, 
      :user => {:except => [:admin, :email, :updated_at, :created_at]}
    ]

  )}
end

each server response is stored in a local format variable. The index method will present the visitor with a index.html.erb file (fetched from views) generated in format.html) and an articles.json file from format.json { render json: @article.to_json(~). This file is basically a big array of articles with including reviews and the user information of the article. there is an except command in the user part of the includes. this part is here to make sure the admin-status, email-address and metadata are not pushed to files and thus contributing to the privacy of the user.

The articles index page is seen as a source in the routs file. it can be accessed with host-name/articles in the browser.

####Show

def show
  @article = Article.find(params[:id])
  @subtitle = @article.title
  @title = "Inspiratieplanet - " + @article.title
  @userName = @article.user.name

  if user_signed_in?
    @showheart = Favorite.exists?(:user_id => current_user.id, :article_id => @article.id)
  end
  respond_to do |format|
    format.html # show.html.erb
    format.json { render json: @article.to_json(
      :include => [
        
        :reviews, 
        :user => {:except => [:admin, :email, :updated_at, :created_at]}
      ]

    )}
  end
end

the show-method allows users to show a specific page (defined in routes as adress: /articles/[article_id]/)

#####variables

@article = Article.find(params[:id])
@subtitle = @article.title
@title = "Inspiratieplanet - " + @article.title
@userName = @article.user.name
if user_signed_in?
  @showheart = Favorite.exists?(:user_id => current_user.id, :article_id => @article.id)
end

the @article calls the article with the id of the address-parameter (given in the adressbar). The @subtitle and @title are again part of the naming process of the page. The title and subtitle call the name of the selected @article. the @userName is called to handle the @article.user.name. This is somehow needed in the articles page. After checking if the user is logged in by stating user_signed_in' from the devise [Gem](https://github.com/plataformatec/devise) with a statement of userthe @showheart is defined. as a boolean statement. if the current_user (the user with the session) has added an article as favorite@showheartis defined astrue. in all other situations @showheart` is defined as false.

#####responses

respond_to do |format|
  format.html # show.html.erb
  format.json { render json: @article.to_json(
    :include => [
      
      :reviews, 
      :user => {:except => [:admin, :email, :updated_at, :created_at]}
    ]

  )}
end

In this case html is generated from format.html and a json file from format.json that includes @article with reviews and users (having the saveguards for privacy in place again). the json is used by diffrent javascript files throughout the code.

####New

def new
  @article = Article.new
  @title = "Inspiratieplanet - Nieuw artikel"
  @subtitle = "nieuw artikel"
  @categories = Category.find(:all)
  
  respond_to do |format|
    format.html # new.html.erb
    format.json { render json: @article }
  end
end

the new method allows users to make new articles.

#####variables

@article = Article.new
@title = "Inspiratieplanet - Nieuw artikel"
@subtitle = "nieuw artikel"
@categories = Category.find(:all)

when @article is called for the first time it generates a new article. @title and @subtitle are set again with the name "Nieuw artikel" this is the dutch translation for "New article" @categories are called to get all generated categories from the database more on categories can be found in the Categories article.

#####Responses

respond_to do |format|
  format.html # new.html.erb
  format.json { render json: @article }
end

a html is rendered from new.html.erb and a json file with the contents of @article is generated in articles/[number].json.

####Edit

def edit
  @article = Article.find(params[:id])
  if current_user.id == @article.user_id
  elsif current_user.admin?
  else
    @article = Article.find(params[:id])
    redirect_to @article, notice: 'U kunt dit artikel niet bewerken.'
  end
end

The edit pages are in place to allow users to edit their articles.

#####Variables

@article = Article.find(params[:id])

The only variable called for here is the @article variable which finds selects the right Article from the database based on the article_id given in the adress of the page articles/[article_id]

#####Tests

if current_user.id == @article.user_id
elsif current_user.admin?
else
  redirect_to @article, notice: 'U kunt dit artikel niet bewerken.'
end

This "test" checks if the current_user that edits the article matches the user that made the article in the first place. the second part of the tests if the current_user is admin if those are true the page continues to load if those are false the 3rd part of the test starts. This part will redirect the user to the article and flash a notice that the user is not allowed to edit the page.

#####Responses

There are no responses defined. this means that the page will automaticly search for a html.erb page with it's name no Json file is generated.

####Create

def create
  @article = Article.new(params[:article])
  @article.user_id = current_user.id

  
  respond_to do |format|
    if @article.save
      format.html { redirect_to @article, notice: 'Article was successfully created.' }
      format.json { render json: @article, status: :created, location: @article }
    else
      format.html { render action: "new" }
      format.json { render json: @article.errors, status: :unprocessable_entity }
    end
  end
end

Create handles the actions performed by the server after posting an article from the new method. It extracts the submitted parameters (the strings from the fields in the form of the new method) and fills a database row with these. It then redirects the user to the right places.

#####Variables

@article = Article.new(params[:article])
@article.user_id = current_user.id

@article extracts the parameters from the incomming stream and pulls them to the active record and saves them from active record to the database.

@article.user_id get's the id from the current logged in user (which is also used and stored in the database.

#####Responses

respond_to do |format|
  if @article.save
    format.html { redirect_to @article, notice: 'Article was successfully created.' }
    format.json { render json: @article, status: :created, location: @article }
  else
    format.html { render action: "new" }
    format.json { render json: @article.errors, status: :unprocessable_entity }
  end
end

The respond_to has a test in this case. If the article can be saved (all the requirements are met) it redirects the user to the newly created article page and notices the user that the article was successfully created. it then renders a json file where the article, the created_at data are stored. if the save fails for some reason it re-renders the new method and renders the errors from the article.

####Update

def update
  @article = Article.find(params[:id])

  respond_to do |format|
    if @article.update_attributes(params[:article])
      format.html { redirect_to @article, notice: 'Article was successfully updated.' }
      format.json { head :no_content }
    else
      format.html { render action: "edit" }
      format.json { render json: @article.errors, status: :unprocessable_entity }
    end
  end
end

The update method is triggered after the rendered edit method is submitted back to the server. The server reads the filled-out values and writes them to the correct table-row in the Articles table.

#####Variables

@article = Article.find(params[:id])

The @article variable is set to the current loaded article (by extracting the id found in the route). The loaded article get's edited not specific others or non-existing articles.

#####Responses

respond_to do |format|
  if @article.update_attributes(params[:article])
    format.html { redirect_to @article, notice: 'Article was successfully updated.' }
    format.json { head :no_content }
  else
    format.html { render action: "edit" }
    format.json { render json: @article.errors, status: :unprocessable_entity }
end

Like the create method this response is populated with a test. If the article can be saved the user is redirected to the article's show page and noticed that the article was updated. If requirements can't be met the edit method is rendered again and the errors are flash as error-notices.

####Destroy

def destroy
  @article = Article.find(params[:id])

  if current_user.id == @article.user_id
    # Remove favorites linked to the article
      if @article.favorites.count > 0
      @article.favorites.each do |favorite|
        favorite.destroy
      end
    end

    # remove reviews linked to the article
    if @article.reviews.count > 0
      @article.reviews.each do |review|
        review.destroy
      end
    end

    # Remove article
    @article.destroy
  
  respond_to do |format|
      format.html { redirect_to articles_url }
      format.json { head :no_content }
    end

  elsif current_user.admin?
    # Remove favorites linked to the article
      if @article.favorites.count > 0
      @article.favorites.each do |favorite|
        favorite.destroy
      end
    end

    # remove reviews linked to the article
    if @article.reviews.count > 0
      @article.reviews.each do |review|
        review.destroy
      end
    end

    # Remove article
    @article.destroy

    respond_to do |format|
      format.html { redirect_to articles_url }
      format.json { head :no_content }
    end
  
  else
    respond_to do |format|
      format.html { redirect_to @article, notice: 'U hebt geen toegang dit artikel te verwijderen' }
      format.json { head :no_content }
    end
  end
end

This rather long method handles the things that need to be done when a user decides to destroy an article. It's long since it needs to check if the logged-in user is allowed to destroy the article (only a user that created an article can destroy it apart from admins) and after that remove not only the article, but also the reviews, favorites and data "owned" by the article from the model. if current_user.id == @article.user_id checks if the logged-in user-id matches the user_id linked to the article. elsif current_user.admin? checks if the logged-in user has it's admin property set to "true". Note that the extra data (favorites and reviews) are deleted before the article. This is needed since favorites and reviews are unlinked from the article if it get's deleted first. removing this data would be much harder then.

#####Variables

@article = Article.find(params[:id])

The @article variable get's the right article from the routing (/articles/[id]/destroy)

#####Job's

if @article.favorites.count > 0
@article.favorites.each do |favorite|
  favorite.destroy
end

If there are favorites linked to the article each favorite is destroyed.

if @article.reviews.count > 0
  @article.reviews.each do |review|
    review.destroy
  end
end

If there are reviews linked to the article each review is destroyed.

@article.destroy

The article is destroyed after all other job's have taken place.

#####Responses

respond_to do |format|
    format.html { redirect_to articles_url }
    format.json { head :no_content }
end

If destroying the article and it's extra's is completed the server redirects the user to the articles index method. No Json is generated.

respond_to do |format|
  format.html { redirect_to @article, notice: 'U hebt geen toegang dit artikel te verwijderen' }
  format.json { head :no_content }
end

If the article can't be destroyed (caused by User access) the user is redirected to the article view method and told that it has no access to destroying the article. No Json is generated.

####Accept

def accept
  @article = Article.find(params[:id])
  if current_user.admin?
    @article.update_attribute(:accepted, true)
    redirect_to '/dashboard/admin'
  else
    redirect_to @article, notice: 'U hebt geen toegang tot de acceptatie-pagina'
  end
end

Accept is a tool used by the admin. Admin's can Accept an article to have it show on the frontpage of the site and pop-up in searches. non-accepted articles can only be viewed by the creator and people with the right link.

#####Variables

@article = Article.find(params[:id])

@article get's the article_id from the route (/articles/[id]/accept).

#####Tasks

@article.update_attribute(:accepted, true)

If the logged-in user is an admin this job will set the :accepted atribute in the article to true.

#####Responses

redirect_to '/dashboard/admin'
redirect_to @article, notice: 'U hebt geen toegang tot de acceptatie-pagina'

If the logged-in user is admin and the previous job went well the user will be redirected to Dasbboard#admin method. If the logged-in user is not an admin the user is redirected to the article and told that it has no access to do such.

####Reject

def reject
  @article = Article.find(params[:id])
  if current_user.admin?
    @article.update_attribute(:accepted, false)
    redirect_to '/dashboard/admin'
  else
    redirect_to @article, notice: 'U hebt geen toegang tot de weiger-pagina'
  end
end

Reject is a tool used by the admin. Admin's can Reject an article to hide it fom the frontpage of the site and pop-up in searches. non-accepted articles can only be viewed by the creator and people with the right link.

#####Variables

@article = Article.find(params[:id])

@article get's the article_id from the route (/articles/[id]/accept).

#####Tasks

@article.update_attribute(:accepted, false)

If the logged-in user is an admin this job will set the :accepted atribute in the article to false.

#####Responses

redirect_to '/dashboard/admin'
redirect_to @article, notice: 'U hebt geen toegang tot de weiger-pagina'

If the logged-in user is admin and the previous job went well the user will be redirected to Dasbboard#admin method. If the logged-in user is not an admin the user is redirected to the article and told that it has no access to do such.

###Views In this section key-elements in the HTML and _form pages are explained. To get more info on the Javascripts used in the views refer to the Javascript article.

####Edit

<%= render 'form' %>

This renders the article form

<%= link_to 'Show', @article %> |
<%= link_to 'Back', articles_path %>

These two links link back to the show-method and the articles-method

####Index

<% indexarticlenumber = 0 %>

indexarticlenumber is set to 0 It is placed here to help Javascript with it's Json file.

<% @articles.each do |article| %>

Each loaded article from the articles-controller is accesed and put in an array called article

<h3><%= article.title %></h3>

the article-title is called from the database to give a name to the article block

<img class="artikel_tegel" src="<%= article.picture_url(:thumb) %>" alt="images">

The source of the image (set by the picture-column in the articlestable is put in the img-src

<%=h truncate(article.content, :length => 270) %>

The articles content is caled and truncated. this truncate option splits the text at the beginning of a word when 270 is reached and places ... at the end.

<button class="article-index-button" id="<%= indexarticlenumber %>">

The indexarticlenumber is called here and set as the id of a button Jquery will extract the Id and call the Json array with the same number.

<% indexarticlenumber += 1 %>

When the indexarticlenumber is used it goes up by 1. The next article will call another Json array.

####New

<%= render 'form' %>

This renders the article form

<%= link_to 'Back', articles_path %>

When the user clicked the "create new article" button by mistake he or she can get back to the Articles#index method by clicking the rendered link.

####Show

<%= link_to 'Edit', edit_article_path(@article) %> | 

The user can click this link to go the Articles#edit method of the current article. The manual address would be /articles/[id]/edit.

<h3><%= @article.title %></h3>

Here the title of the current article is called.

<%= @article.content %>

Here the content of the current article is called.

<%= image_tag @article.picture_url(:thumb) %>

here an article is rendered with the src set by the picture_url -> thumb from the articlestable.

<%= @article.tags %>

Here the tags of the article are rendered (without further options)

<%= @userName %>

Here the name of the user that created the article is called.

<% if @showheart %>
	<div class='unfavoritebutton'></div>
<% elsif user_signed_in? %>
	<div class='favoritebutton' id='<%= @article.id %>'></div>
<% end %>

If the logged-in user has favorited the article a small block with a filled out heart is shown if this parameter is false a hollow heart is shown.

<% if user_signed_in? && current_user.admin? && @article.accepted %>
		<%= link_to "Afkeuren", reject_article_path(@article), method: :put %>
<% end %>

Here the admin can click the link to the Articles#reject method.

<% @article.reviews.each do |review| %>

Each review (linked to the article) is called and extracted.

<b><%= review.title %></b>

Each review-title is called and put in a bold text-style

<%= review.content %>

Each review-content is called and put in the html code.

<%= review.user.name + " || " + time_ago_in_words(review.created_at)%>

The username linked on the review is shown and the time ago is posted.

<% if user_signed_in? && review.user_id == current_user.id %>
		<%= link_to 'Verwijderen', article_review_path(review.article, review), 
				 		:confirm => "Weet u zeker dat u deze comment wil verwijderen?",
				 			:method => :delete  %>
<% else %>

<% end %>

If the current logged-in user created the review it get's the option to remove it's review.

<% if user_signed_in? %>

The code before the <% end %> tag is only rendered when the user is logged in.

<%= form_for([@article, @article.reviews.build]) do |f| %>

A form is declared and rendered in the current view.

<%= f.hidden_field :user_id, :value => current_user.id %>

At the time of this posting this hidden field contains and sends the id of the currently signed-in user. this is very unsafe and needs to be handled by the controller User.reviews.create(:article_id => params[:article_id]) Users can now with some small injection post on behalf of others.

<%= f.label :title %>
<%= f.text_field :title %>
<%= f.label :content %><br />
<%= f.text_area :content %>

Labels and inputs are provided with post methods to make-up the form.

<%= f.submit %>

Here the submitbutton for the form is rendered. clicking it will call the Reviews#create method.

Clone this wiki locally