diff --git a/Gemfile b/Gemfile index c1f1ee3e2..a42f46a1f 100644 --- a/Gemfile +++ b/Gemfile @@ -13,11 +13,15 @@ gem 'carrierwave' # gem 'mini_magick' gem 'ffaker' +gem 'kaminari' +gem 'bootstrap-sass','~>3.3.7' +gem 'sass-rails','~>5.0' +gem 'jquery-rails' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '~> 5.1.4' # Use sqlite3 as the database for Active Record -gem 'sqlite3' + # Use Puma as the app server gem 'puma', '~> 3.7' # Use SCSS for stylesheets @@ -51,8 +55,10 @@ group :development, :test do gem 'factory_bot_rails' gem 'shoulda-matchers', '~> 3.1' gem 'rails-controller-testing' + gem 'sqlite3' end + group :development do # Access an IRB console on exception pages or by using <%= console %> anywhere in the code. gem 'web-console', '>= 3.3.0' @@ -64,3 +70,7 @@ end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] + +group :production do + gem 'pg','~>0.21' +end diff --git a/Gemfile.lock b/Gemfile.lock index e55e9522f..965cceb20 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,65 +1,70 @@ GEM remote: https://rubygems.org/ specs: - actioncable (5.1.4) - actionpack (= 5.1.4) + actioncable (5.1.6) + actionpack (= 5.1.6) nio4r (~> 2.0) websocket-driver (~> 0.6.1) - actionmailer (5.1.4) - actionpack (= 5.1.4) - actionview (= 5.1.4) - activejob (= 5.1.4) + actionmailer (5.1.6) + actionpack (= 5.1.6) + actionview (= 5.1.6) + activejob (= 5.1.6) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.1.4) - actionview (= 5.1.4) - activesupport (= 5.1.4) + actionpack (5.1.6) + actionview (= 5.1.6) + activesupport (= 5.1.6) rack (~> 2.0) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.1.4) - activesupport (= 5.1.4) + actionview (5.1.6) + activesupport (= 5.1.6) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) - activejob (5.1.4) - activesupport (= 5.1.4) + activejob (5.1.6) + activesupport (= 5.1.6) globalid (>= 0.3.6) - activemodel (5.1.4) - activesupport (= 5.1.4) - activerecord (5.1.4) - activemodel (= 5.1.4) - activesupport (= 5.1.4) + activemodel (5.1.6) + activesupport (= 5.1.6) + activerecord (5.1.6) + activemodel (= 5.1.6) + activesupport (= 5.1.6) arel (~> 8.0) - activesupport (5.1.4) + activesupport (5.1.6) concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (~> 0.7) + i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) addressable (2.5.2) public_suffix (>= 2.0.2, < 4.0) arel (8.0.0) - bcrypt (3.1.11) - bcrypt (3.1.11-java) - bcrypt (3.1.11-x64-mingw32) - bcrypt (3.1.11-x86-mingw32) + autoprefixer-rails (9.0.0) + execjs + bcrypt (3.1.12) + bcrypt (3.1.12-java) + bcrypt (3.1.12-x64-mingw32) + bcrypt (3.1.12-x86-mingw32) bindex (0.5.0) + bootstrap-sass (3.3.7) + autoprefixer-rails (>= 5.2.1) + sass (>= 3.3.4) builder (3.2.3) - byebug (10.0.0) - capybara (2.17.0) + byebug (10.0.2) + capybara (2.18.0) addressable mini_mime (>= 0.1.3) nokogiri (>= 1.3.3) rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (>= 2.0, < 4.0) - carrierwave (1.2.2) + carrierwave (1.2.3) activemodel (>= 4.0.0) activesupport (>= 4.0.0) mime-types (>= 1.16) - childprocess (0.8.0) + childprocess (0.9.0) ffi (~> 1.0, >= 1.0.11) coffee-rails (4.2.2) coffee-script (>= 2.2.0) @@ -70,38 +75,54 @@ GEM coffee-script-source (1.12.2) concurrent-ruby (1.0.5) concurrent-ruby (1.0.5-java) - crass (1.0.3) - devise (4.4.1) + crass (1.0.4) + devise (4.4.3) bcrypt (~> 3.0) orm_adapter (~> 0.1) - railties (>= 4.1.0, < 5.2) + railties (>= 4.1.0, < 6.0) responders warden (~> 1.2.3) diff-lcs (1.3) - erubi (1.7.0) + erubi (1.7.1) execjs (2.7.0) - factory_bot (4.8.2) + factory_bot (4.10.0) activesupport (>= 3.0.0) - factory_bot_rails (4.8.2) - factory_bot (~> 4.8.2) + factory_bot_rails (4.10.0) + factory_bot (~> 4.10.0) railties (>= 3.0.0) - ffaker (2.8.1) - ffi (1.9.18) - ffi (1.9.18-java) - ffi (1.9.18-x64-mingw32) - ffi (1.9.18-x86-mingw32) + ffaker (2.10.0) + ffi (1.9.25) + ffi (1.9.25-java) + ffi (1.9.25-x64-mingw32) + ffi (1.9.25-x86-mingw32) globalid (0.4.1) activesupport (>= 4.2.0) - i18n (0.9.3) + i18n (1.0.1) concurrent-ruby (~> 1.0) jbuilder (2.7.0) activesupport (>= 4.2.0) multi_json (>= 1.2) + jquery-rails (4.3.3) + rails-dom-testing (>= 1, < 3) + railties (>= 4.2.0) + thor (>= 0.14, < 2.0) + kaminari (1.1.1) + activesupport (>= 4.1.0) + kaminari-actionview (= 1.1.1) + kaminari-activerecord (= 1.1.1) + kaminari-core (= 1.1.1) + kaminari-actionview (1.1.1) + actionview + kaminari-core (= 1.1.1) + kaminari-activerecord (1.1.1) + activerecord + kaminari-core (= 1.1.1) + kaminari-core (1.1.1) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) ruby_dep (~> 1.2) - loofah (2.1.1) + loofah (2.2.2) crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.7.0) @@ -114,33 +135,36 @@ GEM mini_portile2 (2.3.0) minitest (5.11.3) multi_json (1.13.1) - nio4r (2.2.0) - nio4r (2.2.0-java) - nokogiri (1.8.1) + nio4r (2.3.1) + nio4r (2.3.1-java) + nokogiri (1.8.4) mini_portile2 (~> 2.3.0) - nokogiri (1.8.1-java) - nokogiri (1.8.1-x64-mingw32) + nokogiri (1.8.4-java) + nokogiri (1.8.4-x64-mingw32) mini_portile2 (~> 2.3.0) - nokogiri (1.8.1-x86-mingw32) + nokogiri (1.8.4-x86-mingw32) mini_portile2 (~> 2.3.0) orm_adapter (0.5.0) - public_suffix (3.0.1) - puma (3.11.2) - puma (3.11.2-java) - rack (2.0.3) - rack-test (0.8.2) + pg (0.21.0) + pg (0.21.0-x64-mingw32) + pg (0.21.0-x86-mingw32) + public_suffix (3.0.2) + puma (3.12.0) + puma (3.12.0-java) + rack (2.0.5) + rack-test (1.1.0) rack (>= 1.0, < 3) - rails (5.1.4) - actioncable (= 5.1.4) - actionmailer (= 5.1.4) - actionpack (= 5.1.4) - actionview (= 5.1.4) - activejob (= 5.1.4) - activemodel (= 5.1.4) - activerecord (= 5.1.4) - activesupport (= 5.1.4) + rails (5.1.6) + actioncable (= 5.1.6) + actionmailer (= 5.1.6) + actionpack (= 5.1.6) + actionview (= 5.1.6) + activejob (= 5.1.6) + activemodel (= 5.1.6) + activerecord (= 5.1.6) + activesupport (= 5.1.6) bundler (>= 1.3.0) - railties (= 5.1.4) + railties (= 5.1.6) sprockets-rails (>= 2.0.0) rails-controller-testing (1.0.2) actionpack (~> 5.x, >= 5.0.1) @@ -149,16 +173,16 @@ GEM rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.0.3) - loofah (~> 2.0) - railties (5.1.4) - actionpack (= 5.1.4) - activesupport (= 5.1.4) + rails-html-sanitizer (1.0.4) + loofah (~> 2.2, >= 2.2.2) + railties (5.1.6) + actionpack (= 5.1.6) + activesupport (= 5.1.6) method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - rake (12.3.0) - rb-fsevent (0.10.2) + rake (12.3.1) + rb-fsevent (0.10.3) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) responders (2.4.0) @@ -180,10 +204,10 @@ GEM rspec-expectations (~> 3.7.0) rspec-mocks (~> 3.7.0) rspec-support (~> 3.7.0) - rspec-support (3.7.0) + rspec-support (3.7.1) ruby_dep (1.5.0) rubyzip (1.2.1) - sass (3.5.5) + sass (3.5.7) sass-listen (~> 4.0.0) sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) @@ -194,9 +218,9 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) - selenium-webdriver (3.8.0) + selenium-webdriver (3.13.1) childprocess (~> 0.5) - rubyzip (~> 1.0) + rubyzip (~> 1.2) shoulda-matchers (3.1.2) activesupport (>= 4.0.0) spring (2.0.2) @@ -204,7 +228,7 @@ GEM spring-watcher-listen (2.0.1) listen (>= 2.7, < 4.0) spring (>= 1.2, < 3.0) - sprockets (3.7.1) + sprockets (3.7.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) sprockets-rails (3.2.1) @@ -219,18 +243,18 @@ GEM thread_safe (0.3.6) thread_safe (0.3.6-java) tilt (2.0.8) - turbolinks (5.1.0) + turbolinks (5.1.1) turbolinks-source (~> 5.1) turbolinks-source (5.1.0) - tzinfo (1.2.4) + tzinfo (1.2.5) thread_safe (~> 0.1) - tzinfo-data (1.2018.3) + tzinfo-data (1.2018.5) tzinfo (>= 1.0.0) - uglifier (4.1.4) + uglifier (4.1.17) execjs (>= 0.3.0, < 3) warden (1.2.7) rack (>= 1.0) - web-console (3.5.1) + web-console (3.6.2) actionview (>= 5.0) activemodel (>= 5.0) bindex (>= 0.4.0) @@ -240,7 +264,7 @@ GEM websocket-driver (0.6.5-java) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.3) - xpath (3.0.0) + xpath (3.1.0) nokogiri (~> 1.8) PLATFORMS @@ -251,6 +275,7 @@ PLATFORMS x86-mswin32 DEPENDENCIES + bootstrap-sass (~> 3.3.7) byebug capybara (~> 2.13) carrierwave @@ -259,7 +284,10 @@ DEPENDENCIES factory_bot_rails ffaker jbuilder (~> 2.5) + jquery-rails + kaminari listen (>= 3.0.5, < 3.2) + pg (~> 0.21) puma (~> 3.7) rails (~> 5.1.4) rails-controller-testing @@ -276,4 +304,4 @@ DEPENDENCIES web-console (>= 3.3.0) BUNDLED WITH - 1.16.1 + 1.16.2 diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 46b20359f..38d5802a2 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -13,3 +13,5 @@ //= require rails-ujs //= require turbolinks //= require_tree . +//= require jquery +//= require bootstrap-sprockets diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.scss similarity index 90% rename from app/assets/stylesheets/application.css rename to app/assets/stylesheets/application.scss index d05ea0f51..8abd327e9 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.scss @@ -10,6 +10,8 @@ * files in this directory. Styles in this file should be added after the last require_* statement. * It is generally better to create a new file per style scope. * - *= require_tree . - *= require_self + */ + @import "bootstrap-sprockets"; + @import "bootstrap"; + @import "style" diff --git a/app/assets/stylesheets/style.scss b/app/assets/stylesheets/style.scss new file mode 100644 index 000000000..d747ead27 --- /dev/null +++ b/app/assets/stylesheets/style.scss @@ -0,0 +1,43 @@ +.user-item{ + margin-bottom: 15px; +} + +.user-item img{ + width: 128px; + height: 128px; +} + +#tweet_description{ + height: 100px; + font-size: 30px; + resize: none; + } + +.tweet-item{ + border: 1px solid #e7e7e7; + margin-top: 5px; + padding: 10px; + word-wrap:break-word; + position: relative; + width: 100%; +} + +.index-tweet-content{ + position: relative; +} + +.img-responsive { + width: 64px; + height: 64px; +} + + +.user-item{ + border: 1px solid #e8e8e8; + margin-top: 5px; + padding: 10px; + word-wrap:break-word; + position: relative; + width: 100%; +} + diff --git a/app/controllers/admin/base_controller.rb b/app/controllers/admin/base_controller.rb index 4a89583f5..3b33184d3 100644 --- a/app/controllers/admin/base_controller.rb +++ b/app/controllers/admin/base_controller.rb @@ -1,3 +1,13 @@ class Admin::BaseController < ApplicationController + before_action :authenticate_admin + + private + + def authenticate_admin + unless current_user.admin? + flash[:alert] = "Not allow!" + redirect_to root_path + end + end end diff --git a/app/controllers/admin/tweets_controller.rb b/app/controllers/admin/tweets_controller.rb index 24a57566c..c92980fd2 100644 --- a/app/controllers/admin/tweets_controller.rb +++ b/app/controllers/admin/tweets_controller.rb @@ -1,7 +1,27 @@ class Admin::TweetsController < Admin::BaseController + before_action :set_tweet, only: [:destroy] + + + def index + @tweets = Tweet.page(params[:page]).per(10) end def destroy + @tweet = Tweet.find(params[:id]) + @tweet.destroy + redirect_to admin_tweets_path(@tweet) + flash[:alert] = "tweet was delete" + end + +private + + def set_tweet + @tweet = Tweet.find(params[:id]) + end + + def tweet_params + params.require(:tweet).permit(:name, :avatar, :introduction) end + end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 3ba9f0a36..c111aa710 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -1,4 +1,7 @@ class Admin::UsersController < Admin::BaseController + def index + @users = User.all.order(created_at: :desc) end + end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 0da627f1a..fd6490f0e 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,7 +1,21 @@ class ApplicationController < ActionController::Base + protect_from_forgery with: :exception + before_action :configure_permitted_parameters, if: :devise_controller? + before_action :authenticate_user! + + protected + + def configure_permitted_parameters + devise_parameter_sanitizer.permit(:sign_up, keys: [:name]) + devise_parameter_sanitizer.permit(:account_update, keys: [:name]) + end + # 請參考 Devise 文件自訂表單後通過 Strong Parameters 的方法 # https://github.com/plataformatec/devise#strong-parameters # 注意有 sign_up 和 account_update 兩種參數要處理 + + private + end diff --git a/app/controllers/followships_controller.rb b/app/controllers/followships_controller.rb index 05f01b552..9a2dab81b 100644 --- a/app/controllers/followships_controller.rb +++ b/app/controllers/followships_controller.rb @@ -1,7 +1,22 @@ class FollowshipsController < ApplicationController + def create + @followship = current_user.followships.build(following_id: params[:following_id]) + + if @followship.save + flash[:notice] = "Successfully followed" + redirect_back(fallback_location: root_path) + else + flash[:alert] = @followship.errors.full_messages.to_sentence + redirect_back(fallback_location: root_path) + end end def destroy + @followship = current_user.followships.where(following_id: params[:id]).first + @followship.destroy + flash[:alert] = "Followship destroyed" + redirect_back(fallback_location: root_path) end + end diff --git a/app/controllers/replies_controller.rb b/app/controllers/replies_controller.rb index a9b6a315b..282d1d40e 100644 --- a/app/controllers/replies_controller.rb +++ b/app/controllers/replies_controller.rb @@ -1,9 +1,25 @@ class RepliesController < ApplicationController def index + @tweet = Tweet.find(params[:tweet_id]) + @user = @tweet.user + @reply = Reply.new + @replies = Reply.where('tweet_id' => params[:tweet_id]) end def create + @tweet = Tweet.find(params[:tweet_id]) + @reply = @tweet.replies.create(reply_params) + @reply.user = current_user + @reply.save! + redirect_to tweet_replies_path end + private + + def reply_params + params.require(:reply).permit(:comment) + end + + end diff --git a/app/controllers/tweets_controller.rb b/app/controllers/tweets_controller.rb index ad14115c1..31c1608aa 100644 --- a/app/controllers/tweets_controller.rb +++ b/app/controllers/tweets_controller.rb @@ -1,16 +1,46 @@ class TweetsController < ApplicationController + before_action :set_tweet, only: [:like, :unlike] + + def index - @users # 基於測試規格,必須講定變數名稱,請用此變數中存放關注人數 Top 10 的使用者資料 + @tweets = Tweet.all.order(created_at: :desc) + @users = User.order(followers_count: :desc).limit(10) + # 基於測試規格,必須講定變數名稱,請用此變數中存放關注人數 Top 10 的使用者資料 + @tweet = Tweet.new end def create + @tweet = Tweet.new(tweet_params) + @tweet.user = current_user + if @tweet.save + flash[:notice] = "Tweet Update" + redirect_to tweets_path + else + flash[:alert] = @tweet.errors.full_messages.to_sentence + render :index + end end def like + @tweet.likes.create!(user: current_user) + redirect_to tweets_path end def unlike + likes = Like.where(tweet: @tweet, user: current_user) + likes.destroy_all + redirect_to tweets_path + end + + private + + def tweet_params + params.require(:tweet).permit(:description) + end + + def set_tweet + @tweet = Tweet.find(params[:id]) end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 750e3c6b5..ab2d9d7e5 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,24 +1,48 @@ class UsersController < ApplicationController + before_action :set_user, only: [:tweets, :edit, :update, :followings, :followers, :likes] def tweets + @tweets = @user.tweets.all.order(created_at: :desc).page(params[:page]).per(10) + @likes = @user.likes + @followings = @user.followings + @followers = @user.followers end def edit + unless @user == current_user + redirect_to tweets_user_path(@user) + end end def update + if @user.update(user_params) + redirect_to root_path, notice: "Profile Update" + else + flash[:alert] = @user.errors.full_messages.to_sentence + render :edit + end end def followings - @followings # 基於測試規格,必須講定變數名稱 + @followings = @user.followings.order('followships.created_at desc')# 基於測試規格,必須講定變數名稱 end def followers - @followers # 基於測試規格,必須講定變數名稱 + @followers = @user.followers.order('followships.created_at desc')# 基於測試規格,必須講定變數名稱 end def likes - @likes # 基於測試規格,必須講定變數名稱 + @likes = @user.likes.order(created_at: :desc)# 基於測試規格,必須講定變數名稱 + end + + private + + def set_user + @user = User.find(params[:id]) + end + + def user_params + params.require(:user).permit(:name, :introduction, :avatar ) end end diff --git a/app/models/followship.rb b/app/models/followship.rb index 1aed01396..ceff25c53 100644 --- a/app/models/followship.rb +++ b/app/models/followship.rb @@ -1,4 +1,17 @@ class Followship < ApplicationRecord validates :following_id, uniqueness: { scope: :user_id } + validate :check_self_following + + belongs_to :user + belongs_to :following, class_name: "User" + + def check_self_following + if user_id == following_id + errors[:following_id]<<"can't follow yourself!" + end + end + + + end diff --git a/app/models/like.rb b/app/models/like.rb index d99b93a32..098a6172d 100644 --- a/app/models/like.rb +++ b/app/models/like.rb @@ -1,2 +1,5 @@ class Like < ApplicationRecord + + belongs_to :user + belongs_to :tweet, optional: true end diff --git a/app/models/reply.rb b/app/models/reply.rb index bae6f9463..4804da5d9 100644 --- a/app/models/reply.rb +++ b/app/models/reply.rb @@ -1,2 +1,6 @@ class Reply < ApplicationRecord + + belongs_to :user + belongs_to :tweet, counter_cache: true + end diff --git a/app/models/tweet.rb b/app/models/tweet.rb index 6715fada2..ae6c15680 100644 --- a/app/models/tweet.rb +++ b/app/models/tweet.rb @@ -1,4 +1,15 @@ class Tweet < ApplicationRecord validates_length_of :description, maximum: 140 + validates_presence_of :description + + belongs_to :user, optional: true + has_many :replies, dependent: :delete_all + + has_many :likes, dependent: :delete_all + has_many :liked_users, through: :likes, source: :user + + def is_liked?(user) + self.liked_users.include?(user) + end end diff --git a/app/models/user.rb b/app/models/user.rb index 6b05b8c21..1c9ee367e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -9,6 +9,34 @@ class User < ApplicationRecord # 需要 app/views/devise 裡找到樣板,加上 name 屬性 # 並參考 Devise 文件自訂表單後通過 Strong Parameters 的方法 validates_presence_of :name + validates_uniqueness_of :name # 加上驗證 name 不能重覆 (關鍵字提示: uniqueness) + has_many :tweets, dependent: :delete_all + has_many :replies, dependent: :delete_all + + has_many :likes, dependent: :delete_all + has_many :like_tweets, through: :likes, source: :tweet + + has_many :followships, dependent: :destroy + has_many :followings, through: :followships + + has_many :inverse_followships, class_name: "Followship", foreign_key: "following_id" + has_many :followers, through: :inverse_followships, source: :user + + + def admin? + self.role == "admin" + end + + def following?(user) + self.followings.include?(user) + end + + def count_followers + self.followers_count = self.followers.size + self.save + end + + end diff --git a/app/views/admin/tweets/index.html.erb b/app/views/admin/tweets/index.html.erb new file mode 100644 index 000000000..e7945a4fc --- /dev/null +++ b/app/views/admin/tweets/index.html.erb @@ -0,0 +1,68 @@ +
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/app/views/admin/users/index.html.erb b/app/views/admin/users/index.html.erb new file mode 100644 index 000000000..fc53f9c7e --- /dev/null +++ b/app/views/admin/users/index.html.erb @@ -0,0 +1,43 @@ +
+
+ + +
+
+
+ + diff --git a/app/views/devise/confirmations/new.html.erb b/app/views/devise/confirmations/new.html.erb index 2dea36607..be34ccf6b 100644 --- a/app/views/devise/confirmations/new.html.erb +++ b/app/views/devise/confirmations/new.html.erb @@ -3,9 +3,14 @@ <%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %> <%= devise_error_messages! %> +
+ <%= f.label :name %>
+ <%= f.text_field :name,autofoucs: true %> +
+
<%= f.label :email %>
- <%= f.email_field :email, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %> + <%= f.email_field :email,autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %>
diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb index 10ed32a9e..63fca694f 100644 --- a/app/views/devise/registrations/edit.html.erb +++ b/app/views/devise/registrations/edit.html.erb @@ -3,9 +3,14 @@ <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> <%= devise_error_messages! %> +
+ <%= f.label :name %>
+ <%= f.text_field :name, autofoucs: true %> +
+
<%= f.label :email %>
- <%= f.email_field :email, autofocus: true, autocomplete: "email" %> + <%= f.email_field :email, autocomplete: "email" %>
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb index 602803cff..e26e35509 100644 --- a/app/views/devise/registrations/new.html.erb +++ b/app/views/devise/registrations/new.html.erb @@ -3,9 +3,14 @@ <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> <%= devise_error_messages! %> +
+ <%= f.label :name %>
+ <%= f.text_field :name,autofoucs: true %> +
+
<%= f.label :email %>
- <%= f.email_field :email, autofocus: true, autocomplete: "email" %> + <%= f.email_field :email, autocomplete: "email" %>
diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb index 3ebb001d1..a85adcb34 100644 --- a/app/views/devise/sessions/new.html.erb +++ b/app/views/devise/sessions/new.html.erb @@ -3,7 +3,7 @@ <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<%= f.label :email %>
- <%= f.email_field :email, autofocus: true, autocomplete: "email" %> + <%= f.email_field :email, autocomplete: "email" %>
diff --git a/app/views/devise/unlocks/new.html.erb b/app/views/devise/unlocks/new.html.erb index cfe8aa816..3a04b87b0 100644 --- a/app/views/devise/unlocks/new.html.erb +++ b/app/views/devise/unlocks/new.html.erb @@ -5,7 +5,7 @@
<%= f.label :email %>
- <%= f.email_field :email, autofocus: true, autocomplete: "email" %> + <%= f.email_field :email, autocomplete: "email" %>
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 952cb7a1b..c3f00ff3b 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -1,4 +1,4 @@ - + SimpleTwitter @@ -9,20 +9,60 @@ - <% if current_user %> - <% if current_user&.admin? %> -
  • <%= link_to 'Admin Panel', admin_restaurants_path %>
  • - <% end %> -
  • -
  • <%= link_to('登出', destroy_user_session_path, method: :delete) %>
  • -
  • <%= link_to('修改個人資料', edit_user_path(current_user)) %>
  • -
  • <%= link_to('修改密碼', edit_user_registration_path) %>
  • - <% else %> -
  • <%= link_to('註冊', new_user_registration_path) %>
  • -
  • <%= link_to('登入', new_user_session_path) %>
  • - <% end %> -

    <%= notice %>

    -

    <%= alert %>

    + +
    + + + +
    + + + <% if flash[:notice] %> +
    +
    + +
    +
    + <% end %> + + <% if flash[:alert] %> +
    +
    +
    <%= alert %>
    +
    +
    + <% end %> + <%= yield %> diff --git a/app/views/replies/index.html.erb b/app/views/replies/index.html.erb new file mode 100644 index 000000000..73f8c7b30 --- /dev/null +++ b/app/views/replies/index.html.erb @@ -0,0 +1,39 @@ +
    + +
    + <%= render partial: "shared/user_info", locals: {user: @user} %> +
    + +
    +
    +
    +

    Tweets

    + <%= render partial: "shared/tweet_item", locals: {tweet: @tweet} %> +
    +

    Replies

    + <% @replies.each do |reply| %> +
    +
    + <%= image_tag reply.user.avatar, size: "75" %> +
    + +
    + <%= reply.user.name %>,<%=reply.created_at%> +
    +

    <%=reply.comment %>

    +
    +
    + <% end %> +
    + + <%= form_for [@tweet,@reply] do |t| %> + <%= t.text_area :comment, placeholder: "Leave your messages!", class: "form-control" %> + <%= t.submit "Reply", class: "btn btn-primary" %> + <% end %> + + +
    +
    +
    + +
    \ No newline at end of file diff --git a/app/views/shared/_follow.html.erb b/app/views/shared/_follow.html.erb new file mode 100644 index 000000000..6b25621e4 --- /dev/null +++ b/app/views/shared/_follow.html.erb @@ -0,0 +1,7 @@ + <% unless user == current_user %> + <% if current_user.following?(user) %> + <%= link_to 'Unfollow', followship_path(user), method: :delete, class: "btn btn-info" %> + <% else %> + <%= link_to 'Follow', followships_path(following_id: user), method: :post, class: "btn btn-primary" %> + <% end %> +<% end %> \ No newline at end of file diff --git a/app/views/shared/_like.html.erb b/app/views/shared/_like.html.erb new file mode 100644 index 000000000..e059f5cb5 --- /dev/null +++ b/app/views/shared/_like.html.erb @@ -0,0 +1,5 @@ +<% if tweet.is_liked?(current_user) %> + <%= link_to 'Unlike', unlike_tweet_path(tweet), method: :post, class: "btn btn-info" %> +<% else %> + <%= link_to 'Like', like_tweet_path(tweet), method: :post, class: "btn btn-primary" %> +<% end %> \ No newline at end of file diff --git a/app/views/shared/_tweet_item.html.erb b/app/views/shared/_tweet_item.html.erb new file mode 100644 index 000000000..fe5d26d67 --- /dev/null +++ b/app/views/shared/_tweet_item.html.erb @@ -0,0 +1,21 @@ + + +
    +
    + <%= image_tag (tweet.user.avatar),size: "75" %> +
    + +
    + <%=link_to tweet.user.name,tweets_user_path(tweet.user) %> + <%=tweet.created_at%> +
    +

    <%=tweet.description %>

    + <%= link_to tweet_replies_path(tweet) do %> + Reply (<%= tweet.replies.size %>) + <% end %> + <%= render partial: "shared/like", locals: {tweet: tweet} %> + +
    + +
    + diff --git a/app/views/shared/_user_info.html.erb b/app/views/shared/_user_info.html.erb new file mode 100644 index 000000000..66f7e0612 --- /dev/null +++ b/app/views/shared/_user_info.html.erb @@ -0,0 +1,29 @@ + <%= image_tag @user.avatar, class:"img-responsive" %> + +

    <%= @user.name %>


    +

    <%= simple_format @user.introduction %>


    + +

    <%= link_to tweets_user_path(@user) do %> + Tweets: <%= @user.tweets.size %>


    + <% end %> + +

    <%= link_to followings_user_path(@user) do %> + Followings: <%= @user.followings.size %>


    + <% end %> + + +

    <%= link_to followers_user_path(@user) do %> + Follower: <%= @user.followers.size %>


    + <% end %> + + +

    <%= link_to likes_user_path(@user) do %> + Likes: <%= @user.likes.size %>


    + <% end %> + + + <% if user == current_user %> + <%= link_to 'Edit Profile', edit_user_path(@user), class: "btn btn-primary" %> + <% else %> + <%= render partial: "shared/follow", locals: {user: user} %> + <% end %> \ No newline at end of file diff --git a/app/views/shared/_user_item.html.erb b/app/views/shared/_user_item.html.erb new file mode 100644 index 000000000..ece8b5a6f --- /dev/null +++ b/app/views/shared/_user_item.html.erb @@ -0,0 +1,12 @@ +
    +
    + <%= image_tag user.avatar, class: "img-responsive" %> +
    + +
    +

    <%=link_to user.name,tweets_user_path(user)%>

    + <%= simple_format user.introduction, length: 200 %> + <%= render partial: "shared/follow", locals: {user: user} %> +
    + +
    \ No newline at end of file diff --git a/app/views/tweets/index.html.erb b/app/views/tweets/index.html.erb new file mode 100644 index 000000000..c5b7204c1 --- /dev/null +++ b/app/views/tweets/index.html.erb @@ -0,0 +1,26 @@ +
    +
    +
    + <%= form_for (@tweet) do |f| %> + <%= f.text_area :description, placeholder: "What's on your mind?", class: "form-control" %> + <%= f.submit "Tweet", class: "btn btn-primary" %> + <% end %> +
    +
    + +
    + <% @tweets.each do |tweet| %> + <%= render partial: "shared/tweet_item", locals: {tweet: tweet} %> + <% end %> +
    +
    + +
    +

    Popular

    + <%= @users.each do |user| %> + <%= render partial: "shared/user_item", locals: {user: user} %> + <% end %> + + +
    +
    diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb new file mode 100644 index 000000000..82293a742 --- /dev/null +++ b/app/views/users/edit.html.erb @@ -0,0 +1,35 @@ +
    +
    + + <%= form_for @user do |f| %> + +
    +
    +
    + <%= image_tag @user.avatar if @user.avatar? %> + <%= f.file_field :avatar %> +
    +
    +
    + +
    +
    + <%= f.text_field :name,placeholder: "Name", class: "form-control" %> +
    + +
    + <%= f.text_area :introduction,placeholder: "Type anything about you here....", class: "form-control" %> +
    + + +
    + <%= f.submit "Update", class: "btn btn-primary" %> +
    +
    + + <% end %> + +
    + +
    +
    \ No newline at end of file diff --git a/app/views/users/followers.html.erb b/app/views/users/followers.html.erb new file mode 100644 index 000000000..fa3ca454d --- /dev/null +++ b/app/views/users/followers.html.erb @@ -0,0 +1,24 @@ +
    +
    + +
    + <%= render partial: "shared/user_info", locals: {user: @user} %> +
    + +
    + +

    Follower

    + + <% @followers.each do |follower| %> +
    +
    + <%= image_tag follower.avatar, class:"img-responsive" %> +

    <%= follower.name %>

    + <%= simple_format follower.introduction, length: 200 %> + <%= render partial: "shared/follow", locals:{user: follower} %> +
    +
    + + <% end %> +
    +
    \ No newline at end of file diff --git a/app/views/users/followings.html.erb b/app/views/users/followings.html.erb new file mode 100644 index 000000000..76369992e --- /dev/null +++ b/app/views/users/followings.html.erb @@ -0,0 +1,24 @@ +
    +
    + +
    + <%= render partial: "shared/user_info", locals: {user: @user} %> +
    + +
    + +

    Followimgs

    + + <% @followings.each do |following| %> +
    +
    + <%= image_tag following.avatar, class:"img-responsive" %> +

    <%= following.name %>

    + <%= simple_format following.introduction, length: 200 %> + <%= render partial: "shared/follow", locals:{user: following} %> +
    +
    + + <% end %> +
    +
    \ No newline at end of file diff --git a/app/views/users/likes.html.erb b/app/views/users/likes.html.erb new file mode 100644 index 000000000..73939bc7b --- /dev/null +++ b/app/views/users/likes.html.erb @@ -0,0 +1,21 @@ +
    +
    + +
    + <%= render partial: "shared/user_info", locals: {user: @user} %> +
    + +
    + +

    Like

    + +
    +
    + <% @likes.each do |like| %> + <%= render partial: "shared/tweet_item", locals: {tweet: like.tweet} %> + <% end %> +
    +
    + +
    +
    \ No newline at end of file diff --git a/app/views/users/tweets.html.erb b/app/views/users/tweets.html.erb new file mode 100644 index 000000000..509b8c6d8 --- /dev/null +++ b/app/views/users/tweets.html.erb @@ -0,0 +1,21 @@ +
    +
    + +
    + <%= render partial: "shared/user_info", locals: {user: @user} %> +
    + +
    + +

    Tweets

    + +
    +
    + <% @tweets.each do |tweet| %> + <%= render partial: "shared/tweet_item", locals: {tweet: tweet} %> + <% end %> +
    +
    + +
    +
    \ No newline at end of file diff --git a/config/database.yml b/config/database.yml index 0d02f2498..816288eaa 100644 --- a/config/database.yml +++ b/config/database.yml @@ -23,3 +23,5 @@ test: production: <<: *default database: db/production.sqlite3 + adapter: postgresql + encoding: Unicode diff --git a/config/routes.rb b/config/routes.rb index 90856d4fe..8eb8c3272 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -4,4 +4,33 @@ # 請依照專案指定規格來設定路由 + namespace :admin do + resources :tweets, only: [:index, :destroy] + resources :users, only: [:index] + root "tweets#index" + end + + resources :tweets, only: [:index, :create] do + resources :replies, only:[:index, :create] + + member do + post :like + post :unlike + end + end + + resources :users, only: [:tweet, :edit, :update] do + + member do + get :tweets + get :likes + get :followings + get :followers + end + end + + resources :followships, only: [:create, :destroy] + + root "tweets#index" + end diff --git a/db/seeds.rb b/db/seeds.rb index 1beea2acc..4ff65e25f 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -5,3 +5,5 @@ # # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) # Character.create(name: 'Luke', movie: movies.first) + + diff --git a/lib/tasks/dev.rake b/lib/tasks/dev.rake index 9b1e87ae4..fda85d4a7 100644 --- a/lib/tasks/dev.rake +++ b/lib/tasks/dev.rake @@ -1,23 +1,66 @@ namespace :dev do # 請先執行 rails dev:fake_user,可以產生 20 個資料完整的 User 紀錄 # 其他測試用的假資料請依需要自行撰寫 + task fake_user: :environment do User.destroy_all 20.times do |i| name = FFaker::Name::first_name file = File.open("#{Rails.root}/public/avatar/user#{i+1}.jpg") + user = User.new( name: name, email: "#{name}@example.co", password: "12345678", introduction: FFaker::Lorem::sentence(30), avatar: file - ) + ) user.save! - puts user.name + + end + + User.create(name: "Dojo AC", email: "root@example.com", password: "12345678",introduction: FFaker::Lorem::sentence(30), role: "admin",avatar: file) + puts "Default admin created!" + + + puts "now you have #{User.count} users data" + + end + + task fake_tweets: :environment do + Tweet.destroy_all() + 100.times do |i| + Tweet.create!( + description: FFaker::Lorem.sentence(5), + user: User.all.sample + ) end + puts "now you have #{Tweet.count} tweets are created" end + task fake_replies: :environment do + Reply.destroy_all() + 178.times do |i| + Reply.create!( + comment: FFaker::Lorem.sentence(30), + tweet: Tweet.all.sample, + user: User.all.sample + ) + end + puts "now you have #{Reply.count} rsplies are created" + end + + task fake_all: :environment do + Rake::Task["db:migrate"].execute + Rake::Task["dev:fake_user"].execute + Rake::Task["dev:fake_tweets"].execute + Rake::Task["dev:fake_replies"].execute + end + + + + + end