+ Text: + <%= @article.text %> +
+diff --git a/.gitignore b/.gitignore index 5b61ab0..5d402a0 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ # Ignore bundler config. /.bundle +/config/initializers/app_keys.rb + # Ignore all logfiles and tempfiles. /log/* !/log/.keep diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..4e1e0d2 --- /dev/null +++ b/.rspec @@ -0,0 +1 @@ +--color diff --git a/Gemfile b/Gemfile index 0f2b178..22eab39 100644 --- a/Gemfile +++ b/Gemfile @@ -6,7 +6,7 @@ gem 'rails', '4.2.6' # Use postgresql as the database for Active Record gem 'pg', '~> 0.15' # Use SCSS for stylesheets -gem 'sass-rails', '~> 5.0' +gem 'sass-rails' # Use Uglifier as compressor for JavaScript assets gem 'uglifier', '>= 1.3.0' # Use CoffeeScript for .coffee assets and views @@ -30,9 +30,20 @@ gem "faker", github: "stympy/faker" gem 'interactive_editor' gem 'awesome_print' gem 'hirb' +gem 'will_paginate' +gem 'kaminari' +gem 'rails_12factor', group: :production +gem 'cancancan' +gem 'bootstrap-sass' +gem "font-awesome-rails" +gem 'simple_form' +gem 'ransack' + +gem 'omniauth-twitter' + # Use ActiveModel has_secure_password -# gem 'bcrypt', '~> 3.1.7' +gem 'bcrypt', '~> 3.1.7' # Use Unicorn as the app server # gem 'unicorn' @@ -44,6 +55,7 @@ gem 'hirb' group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug' + gem 'rspec-rails' end group :development do diff --git a/Gemfile.lock b/Gemfile.lock index 3e9faf5..55fc70f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,6 @@ GIT remote: git://github.com/stympy/faker.git - revision: b05809e3839016d42e4d40bc4a28ac25326fe03a + revision: 3b4368a51ecafc21952f28d5456d1e19cf6b1454 specs: faker (1.6.3) i18n (~> 0.5) @@ -44,11 +44,18 @@ GEM thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) arel (6.0.3) + autoprefixer-rails (6.3.7) + execjs awesome_print (1.7.0) + bcrypt (3.1.11) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) + bootstrap-sass (3.3.6) + autoprefixer-rails (>= 5.2.1) + sass (>= 3.3.4) builder (3.2.2) byebug (9.0.5) + cancancan (1.15.0) coffee-rails (4.1.1) coffee-script (>= 2.2.0) railties (>= 4.0.0, < 5.1.x) @@ -59,11 +66,15 @@ GEM concurrent-ruby (1.0.2) cowsay (0.2.0) debug_inspector (0.0.2) + diff-lcs (1.2.5) erubis (2.7.0) execjs (2.7.0) ffi (1.9.10) + font-awesome-rails (4.6.3.1) + railties (>= 3.2, < 5.1) globalid (0.3.6) activesupport (>= 4.1.0) + hashie (3.4.4) hirb (0.7.3) i18n (0.7.0) interactive_editor (0.0.10) @@ -76,6 +87,9 @@ GEM railties (>= 4.2.0) thor (>= 0.14, < 2.0) json (1.8.3) + kaminari (0.17.0) + actionpack (>= 3.0.0) + activesupport (>= 3.0.0) loofah (2.0.3) nokogiri (>= 1.5.9) mail (2.6.4) @@ -89,8 +103,20 @@ GEM nokogiri (1.6.8) mini_portile2 (~> 2.1.0) pkg-config (~> 1.1.7) + oauth (0.5.1) + omniauth (1.3.1) + hashie (>= 1.2, < 4) + rack (>= 1.0, < 3) + omniauth-oauth (1.1.0) + oauth + omniauth (~> 1.0) + omniauth-twitter (1.2.1) + json (~> 1.3) + omniauth-oauth (~> 1.1) pg (0.18.4) pkg-config (1.1.7) + polyamorous (1.3.1) + activerecord (>= 3.0) rack (1.6.4) rack-test (0.6.3) rack (>= 1.0) @@ -113,14 +139,42 @@ GEM rails-deprecated_sanitizer (>= 1.0.1) rails-html-sanitizer (1.0.3) loofah (~> 2.0) + rails_12factor (0.0.3) + rails_serve_static_assets + rails_stdout_logging + rails_serve_static_assets (0.0.5) + rails_stdout_logging (0.0.5) railties (4.2.6) actionpack (= 4.2.6) activesupport (= 4.2.6) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - rake (11.2.0) + rake (11.2.2) + ransack (1.8.0) + actionpack (>= 3.0) + activerecord (>= 3.0) + activesupport (>= 3.0) + i18n + polyamorous (~> 1.3) rdoc (4.2.2) json (~> 1.4) + rspec-core (3.4.4) + rspec-support (~> 3.4.0) + rspec-expectations (3.4.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.4.0) + rspec-mocks (3.4.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.4.0) + rspec-rails (3.4.2) + actionpack (>= 3.0, < 4.3) + activesupport (>= 3.0, < 4.3) + railties (>= 3.0, < 4.3) + rspec-core (~> 3.4.0) + rspec-expectations (~> 3.4.0) + rspec-mocks (~> 3.4.0) + rspec-support (~> 3.4.0) + rspec-support (3.4.1) sass (3.4.22) sass-rails (5.0.4) railties (>= 4.0.0, < 5.0) @@ -131,10 +185,13 @@ GEM sdoc (0.4.1) json (~> 1.7, >= 1.7.7) rdoc (~> 4.0) + simple_form (3.2.1) + actionpack (> 4, < 5.1) + activemodel (> 4, < 5.1) spoon (0.0.4) ffi spring (1.7.1) - sprockets (3.6.0) + sprockets (3.6.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) sprockets-rails (3.0.4) @@ -155,28 +212,40 @@ GEM binding_of_caller (>= 0.7.2) railties (>= 4.0) sprockets-rails (>= 2.0, < 4.0) + will_paginate (3.1.0) PLATFORMS ruby DEPENDENCIES awesome_print + bcrypt (~> 3.1.7) + bootstrap-sass byebug + cancancan coffee-rails (~> 4.1.0) cowsay faker! + font-awesome-rails hirb interactive_editor jbuilder (~> 2.0) jquery-rails + kaminari + omniauth-twitter pg (~> 0.15) rails (= 4.2.6) - sass-rails (~> 5.0) + rails_12factor + ransack + rspec-rails + sass-rails sdoc (~> 0.4.0) + simple_form spring turbolinks uglifier (>= 1.3.0) web-console (~> 2.0) + will_paginate BUNDLED WITH 1.12.5 diff --git a/app/assets/images/weather.png b/app/assets/images/weather.png new file mode 100644 index 0000000..8c4086f Binary files /dev/null and b/app/assets/images/weather.png differ diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index e07c5a8..7465856 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -13,4 +13,5 @@ //= require jquery //= require jquery_ujs //= require turbolinks +//= require bootstrap-sprockets //= require_tree . diff --git a/app/assets/javascripts/comments.coffee b/app/assets/javascripts/comments.coffee new file mode 100644 index 0000000..24f83d1 --- /dev/null +++ b/app/assets/javascripts/comments.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/likes.coffee b/app/assets/javascripts/likes.coffee new file mode 100644 index 0000000..24f83d1 --- /dev/null +++ b/app/assets/javascripts/likes.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/password_resets.coffee b/app/assets/javascripts/password_resets.coffee new file mode 100644 index 0000000..24f83d1 --- /dev/null +++ b/app/assets/javascripts/password_resets.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/sessions.coffee b/app/assets/javascripts/sessions.coffee new file mode 100644 index 0000000..24f83d1 --- /dev/null +++ b/app/assets/javascripts/sessions.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/users.coffee b/app/assets/javascripts/users.coffee new file mode 100644 index 0000000..24f83d1 --- /dev/null +++ b/app/assets/javascripts/users.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index f9cd5b3..7482d23 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -1,4 +1,4 @@ -/* + /* * This is a manifest file that'll be compiled into application.css, which will include all the files * listed below. * diff --git a/app/assets/stylesheets/bootstrap_and_overrides.scss b/app/assets/stylesheets/bootstrap_and_overrides.scss new file mode 100644 index 0000000..bda0285 --- /dev/null +++ b/app/assets/stylesheets/bootstrap_and_overrides.scss @@ -0,0 +1,23 @@ +$brand-primary: #ffcc99; +$border-radius-base: 0px; + +@import "bootstrap-sprockets"; +@import "bootstrap"; + +abbr[title] { + cursor: default; +} + +body{ + font-size: 1.4em; + line-height: 1.5em; + font-family: "Open Sans", Helvetica, Arial, sans-serif; +} + +.navbar{ + padding-top: 10px; + background-color: darkblue; + position: fixed; + width: 100%; + top: 0; +} diff --git a/app/assets/stylesheets/comments.scss b/app/assets/stylesheets/comments.scss new file mode 100644 index 0000000..3722c12 --- /dev/null +++ b/app/assets/stylesheets/comments.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the comments controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/home.scss b/app/assets/stylesheets/home.scss index f0ddc68..afe2aba 100644 --- a/app/assets/stylesheets/home.scss +++ b/app/assets/stylesheets/home.scss @@ -1,3 +1,6 @@ // Place all the styles related to the home controller here. // They will automatically be included in application.css. // You can use Sass (SCSS) here: http://sass-lang.com/ +body { + background: url(image_path('weather.png')) +} diff --git a/app/assets/stylesheets/likes.scss b/app/assets/stylesheets/likes.scss new file mode 100644 index 0000000..1f08d32 --- /dev/null +++ b/app/assets/stylesheets/likes.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the likes controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/password_resets.scss b/app/assets/stylesheets/password_resets.scss new file mode 100644 index 0000000..eb9649c --- /dev/null +++ b/app/assets/stylesheets/password_resets.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the PasswordResets controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/scaffolds.scss b/app/assets/stylesheets/scaffolds.scss new file mode 100644 index 0000000..ed7a765 --- /dev/null +++ b/app/assets/stylesheets/scaffolds.scss @@ -0,0 +1,73 @@ +body { + background-color: #fff; + color: #333; + font-family: verdana, arial, helvetica, sans-serif; + font-size: 13px; + line-height: 18px; +} + +p, ol, ul, td { + font-family: verdana, arial, helvetica, sans-serif; + font-size: 13px; + line-height: 18px; +} + +pre { + background-color: #eee; + padding: 10px; + font-size: 11px; +} + +a { + color: #000; + + &:visited { + color: #666; + } + + &:hover { + color: #fff; + background-color: #000; + } +} + +div { + &.field, &.actions { + margin-bottom: 10px; + } +} + +#notice { + color: green; +} + +.field_with_errors { + padding: 2px; + background-color: red; + display: table; +} + +#error_explanation { + width: 450px; + border: 2px solid red; + padding: 7px; + padding-bottom: 0; + margin-bottom: 20px; + background-color: #f0f0f0; + + h2 { + text-align: left; + font-weight: bold; + padding: 5px 5px 5px 15px; + font-size: 12px; + margin: -7px; + margin-bottom: 0px; + background-color: #c00; + color: #fff; + } + + ul li { + font-size: 12px; + list-style: square; + } +} diff --git a/app/assets/stylesheets/sessions.scss b/app/assets/stylesheets/sessions.scss new file mode 100644 index 0000000..7bef9cf --- /dev/null +++ b/app/assets/stylesheets/sessions.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the sessions controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/users.scss b/app/assets/stylesheets/users.scss new file mode 100644 index 0000000..1efc835 --- /dev/null +++ b/app/assets/stylesheets/users.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the users controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d83690e..fb2dde9 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,4 +2,33 @@ class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception + + def authenticate_user + redirect_to new_session_path, alert: "Please sign in" unless user_signed_in? + end + + def authorize_user + find_article + unless @article.user_id == current_user.id + flash[:notice] = "You are not the creator of this article, you don't have the permissions" + redirect_to root_path + return false + end + end + + def current_user + @current_user ||= User.find_by_id(session[:user_id]) + # @current_user ||= User.find_by_auth_token!(cookies[:auth_token]) if cookies[:auth_token] + end + helper_method :current_user + + def user_signed_in? + current_user.present? + end + + helper_method :user_signed_in? + + def sign_in(user) + session[:user_id] = user.id + end end diff --git a/app/controllers/articles_controller.rb b/app/controllers/articles_controller.rb index 2d15b66..35c89bf 100644 --- a/app/controllers/articles_controller.rb +++ b/app/controllers/articles_controller.rb @@ -1,42 +1,61 @@ class ArticlesController < ApplicationController +before_action :authorize_user, only: [:edit, :update, :destroy] +before_action :authenticate_user!, except: [:index, :show] before_action :find_article, only: [:show, :edit, :update, :destroy] + def index - @articles = Article.order(created_at: :desc) + @articles = Article.all + @a = Article.ransack(params[:q]) + @people = @a.result(distinct: true) + @articles = Article.order(:title).page params[:page] end - def edit - end +def edit + redirect_to root_path, alert: "access defined" unless can? :edit, @article +end def show - @article = Article.find(params[:id]) + @comment = Comment.new + @like = @article.like_for(current_user) end def new - + @article = Article.new end def create - @article = Article.new(article_params) - @article.save - redirect_to @article + @article = Article.new article_params + @article.user = current_user + if @article.save + flash[:notice] = "Article Created!" + redirect_to article_path(@article) + else + flash[:alert] = "Article not created!" + render :new + end end def update + redirect_to root_path, alert: "access denined" unless can? :update, @article if @article.update article_params redirect_to article_path(@article), notice: "Article Updated" + else + render :edit end + end def destroy + redirect_to root_path, alert: "access denined" unless can? :destroy, @article @article.destroy - redirect_to article_path, notice: "Question Deleted" + redirect_to articles_path, notice: "Questions deleted" end private def article_params - params.require(:article).permit(:title, :text) + params.require(:article).permit(:title, :text, :category_id, :user_id, :tag_names) end def find_article diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb new file mode 100644 index 0000000..7121a97 --- /dev/null +++ b/app/controllers/comments_controller.rb @@ -0,0 +1,28 @@ +class CommentsController < ApplicationController + before_action :authorize_user, only: [:edit, :update, :destroy] + + def create + @comment = Comment.new comment_params + @article = Article.find params[:article_id] + @comment.article = @article + if @comment.save + redirect_to article_path(@article), notice: "Comment created!" + else + render "/article/show" + end + + def destroy + comment = Comment.find params[:id] + article = Article.find params[:question_id] + redirect_to root_path, alert: "access denined" unless can? :destroy, @comment + comment.destroy + redirect_to article_path(article), notice: "Comment deleted" + end + end + + private + + def comment_params + params.require(:comment).permit(:body) + end +end diff --git a/app/controllers/likes_controller.rb b/app/controllers/likes_controller.rb new file mode 100644 index 0000000..74a6a19 --- /dev/null +++ b/app/controllers/likes_controller.rb @@ -0,0 +1,22 @@ +class LikesController < ApplicationController + before_action :authenticate_user + + def create + like = current_user.likes.new + article = Article.find params[:article_id] + like.article = article + if like.save + LikesMailer.notify_article_owner(like).deliver_now + redirect_to article, notice: "Liked!" + else + redirect_to article, notice: "Didn't Like!" + end + end + + def destroy + article = Article.find params[:article_id] + like = current_user.likes.find params[:id] + like.destroy + redirect_to like.article, notice: "Unliked" + end +end diff --git a/app/controllers/password_resets_controller.rb b/app/controllers/password_resets_controller.rb new file mode 100644 index 0000000..9f289e4 --- /dev/null +++ b/app/controllers/password_resets_controller.rb @@ -0,0 +1,33 @@ +class PasswordResetsController < ApplicationController + def new + end + + def create + @user = User.find_by(email: params[:email]) + if @user + # @user.create_reset_digest + @user.send_password_reset + flash[:info] = "Email sent with password reset instructions" + redirect_to root_url + else + flash.now[:danger] = "Email address not found" + render 'new' + end + end + + def edit + @user = User.find_by_password_reset_token!(params[:id]) + end + + def update + @user = User.find_by_password_reset_token!(params[:id]) + user_params = params.require(:user).permit(:password, :password_confirmation) + if @user.password_reset_sent_at < 3.days.ago + redirect_to new_password_reset_path, :alert => "Password reset has expired." + elsif @user.update user_params + redirect_to root_url, :notice => "Password has been reset!" + else + render :edit + end + end +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb new file mode 100644 index 0000000..95cb201 --- /dev/null +++ b/app/controllers/sessions_controller.rb @@ -0,0 +1,49 @@ +# class SessionsController < ApplicationController +# def new +# end +# +# def create +# user = User.find_by_email params[:email] +# if user && user.authenticate(params[:password]) +# +# if params[:remember_me] +# cookies.permanent[:auth_token] = user.auth_token +# sign_in(user) +# else +# cookies[:auth_token] = user.auth_token +# end +# redirect_to root_path, notice: "Signed In!" +# else +# flash[:alert] = "wrong credentials" +# render :new +# end +# end +# +# def destroy +# cookies.delete(:auth_token) +# redirect_to root_path, notice: "Signed out!" +# end +# +# end + +class SessionsController < ApplicationController + def new + end + + def create + user = User.find_by_email params[:sessions][:email] + if user && user.authenticate(params[:sessions][:password]) + sign_in(user) + redirect_to root_path, notice: "Signed In!" + else + flash[:alert] = "wrong credentials" + render :new + end +end + +def destroy + session[:user_id] = nil + redirect_to root_path, notice: "Signed out!" +end + +end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb new file mode 100644 index 0000000..8b0e455 --- /dev/null +++ b/app/controllers/users_controller.rb @@ -0,0 +1,71 @@ +class UsersController < ApplicationController + before_action :set_user, only: [:show, :edit, :update, :destroy] + + def index + @users = User.all + end + + + def show + end + + # GET /users/new + def new + @user = User.new + end + + # GET /users/1/edit + def edit + end + + # POST /users + # POST /users.json + def create + @user = User.new(user_params) + + respond_to do |format| + if @user.save + format.html { redirect_to @user, notice: 'User was successfully created.' } + format.json { render :show, status: :created, location: @user } + else + format.html { render :new } + format.json { render json: @user.errors, status: :unprocessable_entity } + end + end + end + + # PATCH/PUT /users/1 + # PATCH/PUT /users/1.json + def update + respond_to do |format| + if @user.update(user_params) + format.html { redirect_to @user, notice: 'User was successfully updated.' } + format.json { render :show, status: :ok, location: @user } + else + format.html { render :edit } + format.json { render json: @user.errors, status: :unprocessable_entity } + end + end + end + + # DELETE /users/1 + # DELETE /users/1.json + def destroy + @user.destroy + respond_to do |format| + format.html { redirect_to users_url, notice: 'User was successfully destroyed.' } + format.json { head :no_content } + end + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_user + @user = User.find(params[:id]) + end + + # Never trust parameters from the scary internet, only allow the white list through. + def user_params + params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation) + end +end diff --git a/app/helpers/comments_helper.rb b/app/helpers/comments_helper.rb new file mode 100644 index 0000000..0ec9ca5 --- /dev/null +++ b/app/helpers/comments_helper.rb @@ -0,0 +1,2 @@ +module CommentsHelper +end diff --git a/app/helpers/likes_helper.rb b/app/helpers/likes_helper.rb new file mode 100644 index 0000000..a78a759 --- /dev/null +++ b/app/helpers/likes_helper.rb @@ -0,0 +1,2 @@ +module LikesHelper +end diff --git a/app/helpers/password_resets_helper.rb b/app/helpers/password_resets_helper.rb new file mode 100644 index 0000000..0c9d96e --- /dev/null +++ b/app/helpers/password_resets_helper.rb @@ -0,0 +1,2 @@ +module PasswordResetsHelper +end diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb new file mode 100644 index 0000000..309f8b2 --- /dev/null +++ b/app/helpers/sessions_helper.rb @@ -0,0 +1,2 @@ +module SessionsHelper +end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb new file mode 100644 index 0000000..2310a24 --- /dev/null +++ b/app/helpers/users_helper.rb @@ -0,0 +1,2 @@ +module UsersHelper +end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb new file mode 100644 index 0000000..4fc4605 --- /dev/null +++ b/app/mailers/application_mailer.rb @@ -0,0 +1,4 @@ +class ApplicationMailer < ActionMailer::Base + default from: "asaroha@sfu.ca" + layout 'mailer' +end diff --git a/app/mailers/likes_mailer.rb b/app/mailers/likes_mailer.rb new file mode 100644 index 0000000..6f7c48f --- /dev/null +++ b/app/mailers/likes_mailer.rb @@ -0,0 +1,7 @@ +class LikesMailer < ApplicationMailer + def notify_article_owner(like) + @user = like.user + @article = like.article + mail(to: @article.user.email, subject: "#{@user.full_name} liked you post") + end +end diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb new file mode 100644 index 0000000..0a81ad8 --- /dev/null +++ b/app/mailers/user_mailer.rb @@ -0,0 +1,15 @@ +class UserMailer < ApplicationMailer + default from: "anuragemailer@gmail.com" + + def welcome_email(user) + @user = user + @url = 'http://localhost:3000/' + mail(to: @user.email, subject: 'Welcome to My Awesome Site') + end + + def password_reset(user) + @user = user + mail(to: @user.email, subject: 'Password Reset') + end + +end diff --git a/app/models/ability.rb b/app/models/ability.rb new file mode 100644 index 0000000..fbcc206 --- /dev/null +++ b/app/models/ability.rb @@ -0,0 +1,44 @@ +class Ability + include CanCan::Ability + + def initialize(user) + user ||= User.new + + # if user.admin? + # can :manage, :all + # else + # can :read, :all + # end + + can :manage, Article do |article| + article.user == user + end + + can :manage, Comment do |comment| + comment.user_id == user + end + + # Define abilities for the passed in user here. For example: + # + # user ||= User.new # guest user (not logged in) + + # + # The first argument to `can` is the action you are giving the user + # permission to do. + # If you pass :manage it will apply to every action. Other common actions + # here are :read, :create, :update and :destroy. + # + # The second argument is the resource the user can perform the action on. + # If you pass :all it will apply to every resource. Otherwise pass a Ruby + # class of the resource. + # + # The third argument is an optional hash of conditions to further filter the + # objects. + # For example, here the user can only update published articles. + # + # can :update, Article, :published => true + # + # See the wiki for details: + # https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities + end +end diff --git a/app/models/article.rb b/app/models/article.rb index b7ac9f7..87dcb50 100644 --- a/app/models/article.rb +++ b/app/models/article.rb @@ -1,2 +1,42 @@ class Article < ActiveRecord::Base + has_many :comments, dependent: :destroy + has_many :likes, dependent: :destroy + has_many :users, through: :likes + belongs_to :category + belongs_to :user + has_many :taggings, dependent: :destroy + has_many :tags, through: :taggings + + validates :title, presence: true, + length: { minimum: 5 } + + + def self.search(search) + if search + self.where("name like ?", "%#{search}%") + else + self.all + end + end + + def new_first_comments + comments.order(created_at: :desc) + end + + def like_for(user) + likes.find_by_user_id(user) + end + + def tag_names=(names) + self.tags = names.split(",").map do |name| + Tag.where(name: name.strip).first_or_create! + end + end + + def tag_names + self.tags.map(&:name).join(", ") + end + + paginates_per 10 + end diff --git a/app/models/category.rb b/app/models/category.rb index 910a009..bfd80b7 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -1,2 +1,4 @@ class Category < ActiveRecord::Base + has_many :articles, dependent: :nullify + validates :title, presence: true, uniqueness: true end diff --git a/app/models/comment.rb b/app/models/comment.rb index 45b2d38..b550a4e 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -1,2 +1,4 @@ class Comment < ActiveRecord::Base + belongs_to :article + validates :body, presence: true, uniqueness: {scope: :article_id} end diff --git a/app/models/like.rb b/app/models/like.rb new file mode 100644 index 0000000..e7d18e1 --- /dev/null +++ b/app/models/like.rb @@ -0,0 +1,4 @@ +class Like < ActiveRecord::Base + belongs_to :user + belongs_to :article +end diff --git a/app/models/tag.rb b/app/models/tag.rb new file mode 100644 index 0000000..ebda770 --- /dev/null +++ b/app/models/tag.rb @@ -0,0 +1,5 @@ +class Tag < ActiveRecord::Base + has_many :taggings + has_many :articles, through: :taggings + +end diff --git a/app/models/tagging.rb b/app/models/tagging.rb new file mode 100644 index 0000000..3df7d12 --- /dev/null +++ b/app/models/tagging.rb @@ -0,0 +1,4 @@ +class Tagging < ActiveRecord::Base + belongs_to :article + belongs_to :tag +end diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000..ef7fcea --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,36 @@ +class User < ActiveRecord::Base + + has_many :likes, dependent: :destroy + has_secure_password + validates :email, presence: true, uniqueness: true, format: /\A([\w+\-].?)+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i + validates :last_name, presence: true + validates :first_name, presence: true + # validates :password, presence: true + + before_create {generate_token(:auth_token)} + + def full_name + "#{first_name} #{last_name}" + end + + def self.create_from_twitter(twitter_data) + user = User.new + end + + def send_password_reset + generate_token(:password_reset_token) + self.password_reset_sent_at = Time.zone.now + save! + UserMailer.password_reset(self).deliver + end + + def generate_token(column) + begin + self[column] = SecureRandom.urlsafe_base64 + end while User.exists?(column => self[column]) + end + + + attr_accessor :abc + +end diff --git a/app/views/articles/_article.html.erb b/app/views/articles/_article.html.erb new file mode 100644 index 0000000..e69de29 diff --git a/app/views/articles/_form.html.erb b/app/views/articles/_form.html.erb new file mode 100644 index 0000000..ce6797f --- /dev/null +++ b/app/views/articles/_form.html.erb @@ -0,0 +1,17 @@ +<%= simple_form_for @article do |f| %> +
+ <%= f.input :title %> +
++ <%= f.input :text, {rows: "10", cols: "50"}%> +
+ <%= f.input :category_id, :collection => Category.all %> ++ <%= f.submit %> +
+<%= button_to "Submit a New Request", new_article_path, :method => :get %>
+ +<%= search_form_for @a do |f| %> + <%= f.label :name_cont %> + <%= f.search_field :title_cont %> + <%= f.submit %> + <% end %> +<% if @articles.present? %> + <%= render @articles %> +<% else %> +There are no requests containing the term(s) <%= params[:search] %>.
+<% end %>| Title | +Text | + <% if can? :edit, @article %> +Actions | + <% end %> +Tags | +
|---|---|---|---|
| <%= link_to s.title, article_path(s) %> | +<%= truncate(s.text, length: 50) %> | + <% if can? :edit, @article %> +<%= link_to 'Edit', edit_article_path(s) %> + <% end %> + <% if can? :destroy, @article %> + <%= link_to 'Delete', article_path(s), + method: :delete, + data: { confirm: 'Are you sure?' } %> | + <% end %> +<%= s.tag_names %> | +
- <%= f.label :title %>
- <%= f.text_field :title %>
-
- <%= f.label :text %>
- <%= f.text_area :text %>
-
- <%= f.submit %> -
-<% end %> - +<%= render 'form' %> <%= link_to 'Back', articles_path %> diff --git a/app/views/articles/show.html.erb b/app/views/articles/show.html.erb index aeebef6..45a7707 100644 --- a/app/views/articles/show.html.erb +++ b/app/views/articles/show.html.erb @@ -1,14 +1,51 @@ -- Title: - <%= @article.title %> -
- -- Text: - <%= @article.text %> -
-<%= link_to "Edit", edit_article_path(@article) %> -<%= link_to "Delete", article_path(@article), - method: :delete, - data: {confirm: "Are you sure"}%> ++ Title: + <%= @article.title %> +
++ Text: + <%= @article.text %> +
+
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.