diff --git a/Gemfile b/Gemfile index c1f1ee3e2..339c7a799 100644 --- a/Gemfile +++ b/Gemfile @@ -14,10 +14,12 @@ gem 'carrierwave' gem 'ffaker' +gem 'bootstrap-sass', '~> 3.3.7' + # 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 @@ -41,6 +43,12 @@ gem 'jbuilder', '~> 2.5' # Use Capistrano for deployment # gem 'capistrano-rails', group: :development +gem 'jquery-rails' + +group :production do + gem 'pg', '~> 0.20' +end + group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] @@ -51,6 +59,7 @@ group :development, :test do gem 'factory_bot_rails' gem 'shoulda-matchers', '~> 3.1' gem 'rails-controller-testing' + gem 'sqlite3' end group :development do diff --git a/Gemfile.lock b/Gemfile.lock index e55e9522f..2a824a571 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -41,11 +41,16 @@ GEM addressable (2.5.2) public_suffix (>= 2.0.2, < 4.0) arel (8.0.0) + autoprefixer-rails (9.0.0) + execjs bcrypt (3.1.11) bcrypt (3.1.11-java) bcrypt (3.1.11-x64-mingw32) bcrypt (3.1.11-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) @@ -97,6 +102,10 @@ GEM 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) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -124,6 +133,9 @@ GEM nokogiri (1.8.1-x86-mingw32) mini_portile2 (~> 2.3.0) orm_adapter (0.5.0) + pg (0.21.0) + pg (0.21.0-x64-mingw32) + pg (0.21.0-x86-mingw32) public_suffix (3.0.1) puma (3.11.2) puma (3.11.2-java) @@ -251,6 +263,7 @@ PLATFORMS x86-mswin32 DEPENDENCIES + bootstrap-sass (~> 3.3.7) byebug capybara (~> 2.13) carrierwave @@ -259,7 +272,9 @@ DEPENDENCIES factory_bot_rails ffaker jbuilder (~> 2.5) + jquery-rails listen (>= 3.0.5, < 3.2) + pg (~> 0.20) puma (~> 3.7) rails (~> 5.1.4) rails-controller-testing @@ -276,4 +291,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..8e7956cfb 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -13,3 +13,6 @@ //= 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 64% rename from app/assets/stylesheets/application.css rename to app/assets/stylesheets/application.scss index d05ea0f51..1fc2d2451 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.scss @@ -10,6 +10,32 @@ * 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"; + +.container-fluid{ + background-color: #B0C4DE; +} + +.navbar-header{ + font-size: 50px; + color: #696969; +} + +.navbar-body{ + font-size: 20px; + color: #696969; +} + +textarea.text-area { + font-size: 24px; + height: 128px; +} + +.tweet-item { + border: 2px solid lightgray; + padding: 10px; + margin-bottom: 15px; + } diff --git a/app/assets/stylesheets/users.scss b/app/assets/stylesheets/users.scss index 1efc835cc..914d1bc3f 100644 --- a/app/assets/stylesheets/users.scss +++ b/app/assets/stylesheets/users.scss @@ -1,3 +1,14 @@ // 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/ + +.well{ + width: 100%; + height: 150px; + padding: 12px 20px; + box-sizing: border-box; + border: 2px solid #ccc; + border-radius: 4px; + background-color: #f8f8f8; + resize: none; +} diff --git a/app/controllers/admin/tweets_controller.rb b/app/controllers/admin/tweets_controller.rb index 24a57566c..a4571dbaa 100644 --- a/app/controllers/admin/tweets_controller.rb +++ b/app/controllers/admin/tweets_controller.rb @@ -1,7 +1,13 @@ class Admin::TweetsController < Admin::BaseController def index + @tweets = Tweet.all.order(created_at: :desc) + @replies = Reply.all end def destroy + @tweet = Tweet.find(params[:id]) + @tweet.destroy + redirect_to admin_tweets_path + flash[:alert] = "tweet was deleted" end end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 3ba9f0a36..1cb09b046 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -1,4 +1,5 @@ class Admin::UsersController < Admin::BaseController def index + @users = User.all.order(tweets_count: :desc) end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 0da627f1a..0c554362f 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,7 +1,25 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :exception + before_action :configure_permitted_parameters, if: :devise_controller? # 請參考 Devise 文件自訂表單後通過 Strong Parameters 的方法 # https://github.com/plataformatec/devise#strong-parameters # 注意有 sign_up 和 account_update 兩種參數要處理 + + private + + def configure_permitted_parameters + devise_parameter_sanitizer.permit(:sign_up, keys: [:name]) + devise_parameter_sanitizer.permit(:account_update, keys: [:name]) + end + + def authenticate_admin + puts "authenticate_admin" + unless current_user.admin? + puts "===================================================" + puts "???????????????????????????????????????????/" + flash[:alert] = "Not allow!" + redirect_to root_path + end + end end diff --git a/app/controllers/followships_controller.rb b/app/controllers/followships_controller.rb index 05f01b552..19b6cefea 100644 --- a/app/controllers/followships_controller.rb +++ b/app/controllers/followships_controller.rb @@ -1,7 +1,19 @@ 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: tweets_path) end end diff --git a/app/controllers/replies_controller.rb b/app/controllers/replies_controller.rb index a9b6a315b..6542c1f18 100644 --- a/app/controllers/replies_controller.rb +++ b/app/controllers/replies_controller.rb @@ -1,9 +1,23 @@ class RepliesController < ApplicationController def index + @tweet = Tweet.find(params[:tweet_id]) + @reply = Reply.new + @user = @tweet.user end def create + @tweet = Tweet.find(params[:tweet_id]) + @reply = @tweet.replies.build(reply_params) + @reply.user = current_user + @reply.save! + redirect_to tweet_replies_path(@tweet) + 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..362dcbdad 100644 --- a/app/controllers/tweets_controller.rb +++ b/app/controllers/tweets_controller.rb @@ -1,16 +1,49 @@ class TweetsController < ApplicationController + before_action :authenticate_user! + # before_action :authenticate_admin def index @users # 基於測試規格,必須講定變數名稱,請用此變數中存放關注人數 Top 10 的使用者資料 + @tweets = Tweet.all.order(created_at: :desc) + @tweet = Tweet.new + @users = User.order(followers_count: :desc).limit(10) end def create + @tweet = current_user.tweets.build(tweet_params) + @tweet.save! + redirect_to tweets_path + end + + def show + @tweet = Tweet.find(params[:id]) + @reply = Reply.new end def like + @tweet = Tweet.find(params[:id]) + @tweet.likes.create!(user: current_user) + redirect_back(fallback_location: tweets_path) end def unlike + @tweet = Tweet.find(params[:id]) + @likes = Like.where(tweet: @tweet, user: current_user) + @likes.destroy_all + redirect_back(fallback_location: tweets_path) + end + + def unfavorite + @restaurant = Restaurant.find(params[:id]) + favorites = Favorite.where(restaurant: @restaurant, user: current_user) + favorites.destroy_all + redirect_back(fallback_location: root_path) + end + +private + + def tweet_params + params.require(:tweet).permit(:description) end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 750e3c6b5..b6f7c5f84 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,24 +1,55 @@ class UsersController < ApplicationController def tweets + @user = User.find(params[:id]) + @tweets = @user.tweets + end + + def show + @user = User.find(params[:id]) end def edit + @user = User.find(params[:id]) + unless @user == current_user + redirect_to user_path(@user) + end end def update + @user = User.find(params[:id]) + if @user.update(user_params) + flash[:notice] ="user was successfully updated" + redirect_to tweets_user_path + else + flash[:alert] ="user was failed to update" + render :edit + end end def followings @followings # 基於測試規格,必須講定變數名稱 + @user = User.find(params[:id]) + @users = @user.followings.all end def followers @followers # 基於測試規格,必須講定變數名稱 + @user = User.find(params[:id]) + @users = @user.followers.all end def likes @likes # 基於測試規格,必須講定變數名稱 + @user = User.find(params[:id]) + @user.count_likes + @likes = @user.liked_tweets.all.order(created_at: :desc) + end + +private + + 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..4d8325d4a 100644 --- a/app/models/followship.rb +++ b/app/models/followship.rb @@ -1,4 +1,7 @@ class Followship < ApplicationRecord validates :following_id, uniqueness: { scope: :user_id } + belongs_to :user + belongs_to :following, class_name:"User" + end diff --git a/app/models/like.rb b/app/models/like.rb index d99b93a32..0d1910145 100644 --- a/app/models/like.rb +++ b/app/models/like.rb @@ -1,2 +1,4 @@ class Like < ApplicationRecord + belongs_to :user, counter_cache: true + belongs_to :tweet, counter_cache: true end diff --git a/app/models/reply.rb b/app/models/reply.rb index bae6f9463..223589f44 100644 --- a/app/models/reply.rb +++ b/app/models/reply.rb @@ -1,2 +1,4 @@ class Reply < ApplicationRecord + belongs_to :user + belongs_to :tweet end diff --git a/app/models/tweet.rb b/app/models/tweet.rb index 6715fada2..92a04900e 100644 --- a/app/models/tweet.rb +++ b/app/models/tweet.rb @@ -1,4 +1,18 @@ class Tweet < ApplicationRecord validates_length_of :description, maximum: 140 + belongs_to :user, counter_cache: true + + has_many :replies, dependent: :destroy + has_many :replied_users, through: :replies, source: :user + + has_many :likes, dependent: :destroy + has_many :liked_tweets, through: :likes, source: :user + + + def is_liked?(tweet) + self.liked_tweets.include?(tweet) + end + + end diff --git a/app/models/user.rb b/app/models/user.rb index 6b05b8c21..389d7078b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -4,11 +4,42 @@ class User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable + def admin? + self.role == "admin" + end + + def count_likes + self.likes_count = self.likes.size + self.save + end + mount_uploader :avatar, AvatarUploader # 需要 app/views/devise 裡找到樣板,加上 name 屬性 # 並參考 Devise 文件自訂表單後通過 Strong Parameters 的方法 validates_presence_of :name + validates_uniqueness_of :name # 加上驗證 name 不能重覆 (關鍵字提示: uniqueness) + has_many :tweets, dependent: :destroy + has_many :replies, dependent: :destroy + has_many :replied_tweets, through: :replies, source: :tweet + + has_many :likes, dependent: :destroy + has_many :liked_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 is_liked?(tweet) + self.liked_tweets.include?(tweet) + end + + def following?(user) + self.followings.include?(user) + 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..60a39e726 --- /dev/null +++ b/app/views/admin/tweets/index.html.erb @@ -0,0 +1,38 @@ +
ID | +Authur | +Description | +Replies | +# | +
---|---|---|---|---|
<%= tweet.id %> | +<%= tweet.user.name %> | +<%= tweet.description %> | +
+ <% tweet.replies.each do |reply| %>
+ <%= reply.comment %> + <% end %> + |
+ <%= link_to 'Delete', admin_tweet_path(tweet), method: :delete, data: {confirm:"Are you sure?"} %> | +
ID | +Name | +Tweets | +Followings | +Follwers | +Likes | +
---|---|---|---|---|---|
<%= user.id %> | +<%= user.name %> | +<%= user.tweets.count %> | +<%= user.followings.count %> | +<%= user.followers_count %> | +<%= user.likes_count %> | +
<%= notice %>
-<%= alert %>
- <%= yield %> - +