diff --git a/Gemfile b/Gemfile index 67c5b18..213ef62 100644 --- a/Gemfile +++ b/Gemfile @@ -3,6 +3,7 @@ git_source(:github){|repo| "https://github.com/#{repo}.git"} ruby "2.7.0" +gem "active_storage_validations", "0.8.2" gem "bcrypt", "3.1.13" gem "bootsnap", ">= 1.4.2", require: false gem "bootstrap-sass", "3.4.1" @@ -10,7 +11,9 @@ gem "bootstrap-will_paginate", "1.0.0" gem "config" gem "faker", "2.1.2" gem "figaro" +gem "image_processing", "1.9.3" gem "jbuilder", "~> 2.7" +gem "mini_magick", "4.9.5" gem "puma", "~> 4.1" gem "rails", "~> 6.0.3", ">= 6.0.3.3" gem "rails-i18n" diff --git a/Gemfile.lock b/Gemfile.lock index d8b5c89..a600a44 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -37,6 +37,8 @@ GEM erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) + active_storage_validations (0.8.2) + rails (>= 5.2.0) activejob (6.0.3.3) activesupport (= 6.0.3.3) globalid (>= 0.3.6) @@ -136,6 +138,9 @@ GEM activesupport (>= 4.2.0) i18n (1.8.5) concurrent-ruby (~> 1.0) + image_processing (1.9.3) + mini_magick (>= 4.9.5, < 5) + ruby-vips (>= 2.0.13, < 3) jaro_winkler (1.5.4) jbuilder (2.10.1) activesupport (>= 5.0.0) @@ -151,6 +156,7 @@ GEM mimemagic (~> 0.3.2) method_source (1.0.0) mimemagic (0.3.5) + mini_magick (4.9.5) mini_mime (1.0.2) mini_portile2 (2.4.0) minitest (5.14.2) @@ -217,6 +223,8 @@ GEM rack (>= 1.1) rubocop (>= 0.72.0) ruby-progressbar (1.10.1) + ruby-vips (2.0.17) + ffi (~> 1.9) rubyzip (2.3.0) sass-rails (6.0.0) sassc-rails (~> 2.1, >= 2.1.1) @@ -285,6 +293,7 @@ PLATFORMS web-unknown DEPENDENCIES + active_storage_validations (= 0.8.2) bcrypt (= 3.1.13) bootsnap (>= 1.4.2) bootstrap-sass (= 3.4.1) @@ -294,8 +303,10 @@ DEPENDENCIES config faker (= 2.1.2) figaro + image_processing (= 1.9.3) jbuilder (~> 2.7) listen (~> 3.2) + mini_magick (= 4.9.5) puma (~> 4.1) rails (~> 6.0.3, >= 6.0.3.3) rails-i18n diff --git a/app/assets/stylesheets/custom.scss b/app/assets/stylesheets/custom.scss index bc8ee01..ea2c193 100644 --- a/app/assets/stylesheets/custom.scss +++ b/app/assets/stylesheets/custom.scss @@ -146,3 +146,53 @@ input { border-bottom: 1px solid $gray-lighter; } } + +.microposts { + list-style: none; + padding: 0; + li { + padding: 10px 0; + border-top: 1px solid #e8e8e8; + } + .user { + margin-top: 5em; + padding-top: 0; + } + .content { + display: block; + margin-left: 60px; + img { + display: block; + padding: 5px 0; + } + } + + .timestamp { + color: $gray-light; + display: block; + margin-left: 60px; + } +.gravatar { + float: left; + margin-right: 10px; + width: 8%; + } +} +aside { + textarea { + height: 100px; + margin-bottom: 5px; + } +} +span.image { + margin-top: 10px; + width: 20px; + input { + border: 0; + } +} + +input[type="file"] { + display: block; + position: absolute; +} diff --git a/app/controllers/account_activations_controller.rb b/app/controllers/account_activations_controller.rb index 24150f3..eeb0d95 100644 --- a/app/controllers/account_activations_controller.rb +++ b/app/controllers/account_activations_controller.rb @@ -4,7 +4,7 @@ def edit if user && !user.activated? && user.authenticated?(:activation, params[:id]) user.activate log_in user - flash[:success] = t("reset.account_activated") + flash[:success] = t("reset.messagerb") redirect_to user else flash[:danger] = t("reset.invalid_activation") diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index c5f5898..edde33a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -11,4 +11,12 @@ def set_locale def default_url_options {locale: I18n.locale} end + + def logged_in_user + unless logged_in? + store_location + flash[:danger] = t("login_in.messages") + redirect_to login_url + end + end end diff --git a/app/controllers/microposts_controller.rb b/app/controllers/microposts_controller.rb new file mode 100644 index 0000000..4241503 --- /dev/null +++ b/app/controllers/microposts_controller.rb @@ -0,0 +1,39 @@ +class MicropostsController < ApplicationController + before_action :logged_in_user, only: [:create, :destroy] + before_action :correct_user, only: :destroy + + def create + @micropost = current_user.microposts.build micropost_params + @micropost.image.attach(params[:micropost][:image]) + if @micropost.save + flash[:success] = I18n.t("micropost.micropost_created") + redirect_to root_url + else + @feed_items = current_user.feed.paginate(page: params[:page]) + render "static_pages/home" + end + end + + def destroy + if @micropost.destroy + flash[:success] = t("micropost.micropost_deleted") + else + flash[:warning] = t("micropost.error") + end + redirect_to request.referer || root_url + end + + private + + def micropost_params + params.require(:micropost).permit :content, :picture + end + + def correct_user + @micropost = current_user.microposts.find_by id: params[:id] + return if @micropost.present? + + flash[:warning] = t("micropost.delete_error") + redirect_to root_url + end +end diff --git a/app/controllers/password_resets_controller.rb b/app/controllers/password_resets_controller.rb index 0417aa5..6aed42c 100644 --- a/app/controllers/password_resets_controller.rb +++ b/app/controllers/password_resets_controller.rb @@ -4,7 +4,7 @@ class PasswordResetsController < ApplicationController def new; end def create - if @user = User.find_by(email: params[:password_reset][:email].downcase) + if @user = User.find_by email: params[:password_reset][:email].downcase @user.create_reset_digest @user.send_password_reset_email flash[:info] = t("reset.email_sent") @@ -17,7 +17,7 @@ def create def update if params[:user][:password].empty? - @user.errors.add(:password, t("reset.email_address")) + @user.errors.add(:password, t("reset.can_be") render :edit elsif @user.update(user_params) log_in @user @@ -41,7 +41,7 @@ def get_user end def valid_user - return if activated? && @user.authenticated?(:reset, params[:id]) + return if (@user.activated? && @user.authenticated?(:reset, params[:id])) flash[:danger] = t("reset.home_has_been_reset") redirect_to root_url end @@ -57,4 +57,5 @@ def check_expiration redirect_to new_password_reset_url end end + end diff --git a/app/controllers/static_pages_controller.rb b/app/controllers/static_pages_controller.rb index 5268023..b0e8676 100644 --- a/app/controllers/static_pages_controller.rb +++ b/app/controllers/static_pages_controller.rb @@ -1,5 +1,10 @@ class StaticPagesController < ApplicationController - def home; end + def home + if logged_in? + @micropost = current_user.microposts.build + @feed_items = current_user.feed.paginate(page: params[:page]) + end + end def help; end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 1b7af64..10759c7 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -4,7 +4,9 @@ class UsersController < ApplicationController before_action :correct_user, only: %i(edit update) before_action :admin_user, only: :destroy - def show; end + def show + @microposts = @user.microposts.paginate(page: params[:page]) + end def edit; end diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index 78e7d9d..a34ee56 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -4,3 +4,4 @@ require("@rails/activestorage").start() require("channels") require("jquery") require ("bootstrap") +require("packs/micopost") diff --git a/app/javascript/packs/micropost.js b/app/javascript/packs/micropost.js new file mode 100644 index 0000000..fda83f6 --- /dev/null +++ b/app/javascript/packs/micropost.js @@ -0,0 +1,7 @@ + $("#micropost_image").bind("change", function() { + var size_in_megabytes = this.files[0].size/1024/1024; + if (size_in_megabytes > 5) { + alert("Maximum file size is 5MB. Please choose a smaller file."); + } + }); + diff --git a/app/models/micropost.rb b/app/models/micropost.rb new file mode 100644 index 0000000..2bb821a --- /dev/null +++ b/app/models/micropost.rb @@ -0,0 +1,13 @@ +class Micropost < ApplicationRecord + belongs_to :user + # default_scope -> { order(created_at: :desc) } + scope :by_created_at, ->{order(created_at: :desc)} + has_one_attached :image + validates :user_id, presence: true + validates :content, presence: true, length: {maximum: 140} + validates :image, content_type: {in: %w[image/jpeg image/gif image/png], message: "must be a valid image format"}, size: {less_than: 5.megabytes, message: "should be less than 5MB"} + + def display_image + image.variant(resize_to_limit: [500, 500]) + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 17fdf4f..088b15c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -10,6 +10,7 @@ class User < ApplicationRecord has_secure_password validates :password, presence: true, length: {minimum: Settings.user.password}, allow_nil: true + has_many :microposts, dependent: :destroy def self.digest string cost = if ActiveModel::SecurePassword.min_cost @@ -33,6 +34,7 @@ def authenticated? remember_token return false if remember_digest.nil? BCrypt::Password.new(remember_digest).is_password?(remember_token) + end def authenticated? attribute, token @@ -46,13 +48,10 @@ def activate update_columns activated: true, activated_at: DateTime.now end - def send_activation_email - UserMailer.account_activation(self).deliver_now - end - def create_reset_digest + self.reset_token = User.new_token - update_column reset_digest: User.digest(reset_token), reset_sent_at: DateTime.now + update_columns reset_digest: User.digest(reset_token), reset_sent_at: DateTime.now end def send_password_reset_email @@ -67,6 +66,10 @@ def forget update_attribute(:remember_digest, nil) end + def feed + Micropost.by_created_at + end + private def create_activation_digest diff --git a/app/views/microposts/_micropost.html.erb b/app/views/microposts/_micropost.html.erb new file mode 100644 index 0000000..cb6c91b --- /dev/null +++ b/app/views/microposts/_micropost.html.erb @@ -0,0 +1,14 @@ +
  • + <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %> + <%= link_to micropost.user.name, micropost.user %> + + <%= micropost.content %> + <%= image_tag micropost.display_image if micropost.image.attached? %> + + + Posted <%= time_ago_in_words(micropost.created_at) %> ago. + <% if current_user? micropost.user %> + <%= link_to t("micropost.button"), micropost, method: :delete, data: {confirm: t("micropost.confirm")} %> + <% end %> + +
  • diff --git a/app/views/shared/_error_messages.html.erb b/app/views/shared/_error_messages.html.erb index 6ae0a98..10fa4c8 100644 --- a/app/views/shared/_error_messages.html.erb +++ b/app/views/shared/_error_messages.html.erb @@ -1,10 +1,10 @@ -<% if @user.errors.any? %> +<% if object.errors.any? %>
    - <%= t("users.messages") %> <%= pluralize(@user.errors.count, t("users.error")) %>. + <%= t("users.messages") %> <%= pluralize object.errors.count, t("users.error") %>.
    diff --git a/app/views/shared/_feed.html.erb b/app/views/shared/_feed.html.erb new file mode 100644 index 0000000..f15a53c --- /dev/null +++ b/app/views/shared/_feed.html.erb @@ -0,0 +1,6 @@ +<% if @feed_items.any? %> +
      + <%= render @feed_items %> +
    + <%= will_paginate @feed_items, params: { controller: :static_pages, action: :home } %> +<% end %> diff --git a/app/views/shared/_micropost_form.html.erb b/app/views/shared/_micropost_form.html.erb new file mode 100644 index 0000000..ec35312 --- /dev/null +++ b/app/views/shared/_micropost_form.html.erb @@ -0,0 +1,12 @@ +<%= form_for(@micropost, html: { multipart: true }) do |f| %> + <%= render "shared/error_messages", object: f.object %> +
    + <%= f.text_area :content, placeholder: "Compose new micropost..." %> +
    + <%= f.submit "Post", class: "btn btn-primary" %> + + <%= f.file_field :image %> + +<% end %> + +<%= javascript_pack_tag "micropost" %> diff --git a/app/views/shared/_user_info.html.erb b/app/views/shared/_user_info.html.erb new file mode 100644 index 0000000..b9c43ba --- /dev/null +++ b/app/views/shared/_user_info.html.erb @@ -0,0 +1,4 @@ +<%= link_to gravatar_for(current_user, size: 50), current_user %> +

    <%= current_user.name %>

    +<%= link_to t("micropost.title"), current_user %> +<%= pluralize(current_user.microposts.count, "micropost") %> diff --git a/app/views/static_pages/home.html.erb b/app/views/static_pages/home.html.erb index 8431798..488cae7 100644 --- a/app/views/static_pages/home.html.erb +++ b/app/views/static_pages/home.html.erb @@ -1,4 +1,19 @@ -<% provide :title, t("home") %> +<% if logged_in? %> +
    + +
    +

    Micropost Feed

    + <%= render "shared/feed" %> +
    +
    +<% else %>

    <%= t("title") %>

    @@ -8,3 +23,4 @@

    <%= link_to t("static_pages.home.button"), '#', class: "btn btn-lg btn-primary" %>
    +<% end %> diff --git a/app/views/user_mailer/account_activation.html.erb b/app/views/user_mailer/account_activation.html.erb index a8bbc92..d37174f 100644 --- a/app/views/user_mailer/account_activation.html.erb +++ b/app/views/user_mailer/account_activation.html.erb @@ -5,3 +5,4 @@ <%= t("email.account")%>

    <%= link_to t("email.activate"), edit_account_activation_url(id: @user.activation_token, email: @user.email) %> + diff --git a/app/views/users/_form.html.erb b/app/views/users/_form.html.erb index 9021e90..6f19a6e 100644 --- a/app/views/users/_form.html.erb +++ b/app/views/users/_form.html.erb @@ -1,5 +1,5 @@ -<%= form_for(@user) do |f| %> - <%= render "shared/error_messages", object: @user %> +<%= form_for (@user) do |f| %> + <%= render 'shared/error_messages', object: f.object %> <%= f.label :name, t("users.new.lable_name") %> <%= f.text_field :name, class: "form-control" %> <%= f.label :email %> diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb index 911cfc8..abdaf57 100644 --- a/app/views/users/index.html.erb +++ b/app/views/users/index.html.erb @@ -1,4 +1,4 @@ -<% provide(:title, 'All users') %> +<% provide(:title, t("micropost.all_users")) %>

    <%= t("session.title") %>

    <%= will_paginate %>