Merge pull request #1140 from consul/proposal-notifications
Proposal notifications and Direct Messages
This commit is contained in:
BIN
app/assets/images/icon_mailer_comment.png
Normal file
BIN
app/assets/images/icon_mailer_comment.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 251 B |
BIN
app/assets/images/icon_mailer_reply.png
Normal file
BIN
app/assets/images/icon_mailer_reply.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 230 B |
BIN
app/assets/images/icon_mailer_share.png
Normal file
BIN
app/assets/images/icon_mailer_share.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 379 B |
@@ -3,6 +3,7 @@
|
||||
//
|
||||
// Table of Contents:
|
||||
//
|
||||
// 0. Custom variables
|
||||
// 1. Global
|
||||
// 2. Breakpoints
|
||||
// 3. The Grid
|
||||
@@ -43,6 +44,70 @@
|
||||
|
||||
@import 'util/util';
|
||||
|
||||
// 0. Custom variables
|
||||
// --------------------
|
||||
|
||||
$base-font-size: rem-calc(17);
|
||||
$base-line-height: rem-calc(26);
|
||||
$small-font-size: rem-calc(14);
|
||||
$line-height: rem-calc(24);
|
||||
|
||||
$brand: #004A83;
|
||||
$body: #E9E9E9;
|
||||
$background: #EDEFF0;
|
||||
$border: #DEE0E3;
|
||||
$dark: darken($brand, 10%);
|
||||
|
||||
$text: #222222;
|
||||
$text-medium: #999999;
|
||||
$text-light: #CCCCCC;
|
||||
|
||||
$link: #2895F1;
|
||||
$link-hover: #2178BF;
|
||||
|
||||
$debates: #008CCF;
|
||||
$votes-bg: #26AEEE;
|
||||
$votes-border: #1F94CB;
|
||||
|
||||
$votes-like: #7BD2A8;
|
||||
$votes-like-act: #5D9E7F;
|
||||
$votes-unlike: #EF8585;
|
||||
$votes-unlike-act: #BD6A6A;
|
||||
|
||||
$delete: #F04124;
|
||||
$check: #46DB91;
|
||||
|
||||
$proposals: #FFA42D;
|
||||
$proposals-border: #CC8425;
|
||||
|
||||
$budget: #454372;
|
||||
$budget-hover: #7571BF;
|
||||
|
||||
$highlight: #E7F2FC;
|
||||
$featured: #FED900;
|
||||
|
||||
$footer-bg: #DEE0E2;
|
||||
$footer-color: #171819;
|
||||
$footer-link: #454A4C;
|
||||
$footer-border: #BFC1C3;
|
||||
|
||||
$success-bg: #DFF0D8;
|
||||
$success-border: #D6E9C6;
|
||||
$color-success: #3C763D;
|
||||
|
||||
$info-bg: #D9EDF7;
|
||||
$info-border: #BCE8F1;
|
||||
$color-info: #31708F;
|
||||
|
||||
$warning-bg: #FCF8E3;
|
||||
$warning-border: #FAEBCC;
|
||||
$color-warning: #8A6D3B;
|
||||
|
||||
$alert-bg: #F2DEDE;
|
||||
$alert-border: #EBCCD1;
|
||||
$color-alert: #A94442;
|
||||
|
||||
|
||||
// 1. Global
|
||||
// ---------
|
||||
|
||||
@@ -127,7 +192,7 @@ $header-color: inherit;
|
||||
$header-lineheight: 1.4;
|
||||
$header-margin-bottom: 0.5rem;
|
||||
$header-text-rendering: optimizeLegibility;
|
||||
$small-font-size: 80%;
|
||||
$small-font-size: rem-calc(14);
|
||||
$header-small-font-color: $medium-gray;
|
||||
$paragraph-lineheight: 1.6;
|
||||
$paragraph-margin-bottom: 1rem;
|
||||
@@ -510,13 +575,13 @@ $show-header-for-stacked: false;
|
||||
|
||||
$tab-margin: 0;
|
||||
$tab-background: $white;
|
||||
$tab-background-active: $light-gray;
|
||||
$tab-item-font-size: rem-calc(12);
|
||||
$tab-background-active: $white;
|
||||
$tab-item-font-size: $base-font-size;
|
||||
$tab-item-background-hover: $white;
|
||||
$tab-item-padding: 1.25rem 1.5rem;
|
||||
$tab-item-padding: $line-height/2 0;
|
||||
$tab-expand-max: 6;
|
||||
$tab-content-background: $white;
|
||||
$tab-content-border: $light-gray;
|
||||
$tab-content-border: $border;
|
||||
$tab-content-color: foreground($tab-background, $primary-color);
|
||||
$tab-content-padding: 1rem;
|
||||
|
||||
@@ -563,66 +628,3 @@ $topbar-submenu-background: $topbar-background;
|
||||
$topbar-title-spacing: 1rem;
|
||||
$topbar-input-width: 200px;
|
||||
$topbar-unstack-breakpoint: medium;
|
||||
|
||||
// 37. Custom variables
|
||||
// --------------------
|
||||
|
||||
$base-font-size: rem-calc(17);
|
||||
$base-line-height: rem-calc(26);
|
||||
$small-font-size: rem-calc(14);
|
||||
$line-height: rem-calc(24);
|
||||
|
||||
$brand: #004A83;
|
||||
$body: #E9E9E9;
|
||||
$background: #EDEFF0;
|
||||
$border: #DEE0E3;
|
||||
$dark: darken($brand, 10%);
|
||||
|
||||
$text: #222222;
|
||||
$text-medium: #999999;
|
||||
$text-light: #CCCCCC;
|
||||
|
||||
$link: #2895F1;
|
||||
$link-hover: #2178BF;
|
||||
|
||||
$debates: #008CCF;
|
||||
$votes-bg: #26AEEE;
|
||||
$votes-border: #1F94CB;
|
||||
|
||||
$votes-like: #7BD2A8;
|
||||
$votes-like-act: #5D9E7F;
|
||||
$votes-unlike: #EF8585;
|
||||
$votes-unlike-act: #BD6A6A;
|
||||
|
||||
$delete: #F04124;
|
||||
$check: #46DB91;
|
||||
|
||||
$proposals: #FFA42D;
|
||||
$proposals-border: #CC8425;
|
||||
|
||||
$budget: #454372;
|
||||
$budget-hover: #7571BF;
|
||||
|
||||
$highlight: #E7F2FC;
|
||||
$featured: #FED900;
|
||||
|
||||
$footer-bg: #DEE0E2;
|
||||
$footer-color: #171819;
|
||||
$footer-link: #454A4C;
|
||||
$footer-border: #BFC1C3;
|
||||
|
||||
$success-bg: #DFF0D8;
|
||||
$success-border: #D6E9C6;
|
||||
$color-success: #3C763D;
|
||||
|
||||
$info-bg: #D9EDF7;
|
||||
$info-border: #BCE8F1;
|
||||
$color-info: #31708F;
|
||||
|
||||
$warning-bg: #FCF8E3;
|
||||
$warning-border: #FAEBCC;
|
||||
$color-warning: #8A6D3B;
|
||||
|
||||
$alert-bg: #F2DEDE;
|
||||
$alert-border: #EBCCD1;
|
||||
$color-alert: #A94442;
|
||||
|
||||
@@ -219,10 +219,44 @@ a {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.tabs-content {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
border: {
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
};
|
||||
margin-bottom: $line-height;
|
||||
|
||||
.tabs-title > a {
|
||||
color: $text-medium;
|
||||
margin-bottom: rem-calc(-1);
|
||||
margin-right: $line-height;
|
||||
|
||||
&[aria-selected='true'],
|
||||
&.is-active {
|
||||
color: $brand;
|
||||
border-bottom: 2px solid $brand;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: $base-font-size;
|
||||
}
|
||||
}
|
||||
|
||||
.no-max-width {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.button.float-right ~ .button.float-right {
|
||||
margin: 0 $line-height/2;
|
||||
}
|
||||
|
||||
// 02. Header
|
||||
// ----------
|
||||
|
||||
@@ -1464,7 +1498,6 @@ table {
|
||||
.comments {
|
||||
background: $white;
|
||||
background-repeat: repeat-x;
|
||||
padding-top: $line-height;
|
||||
padding-bottom: $line-height*4;
|
||||
|
||||
h2 {
|
||||
@@ -1488,7 +1521,7 @@ table {
|
||||
|
||||
.comment-votes {
|
||||
color: $text-medium;
|
||||
font-size: $small-font-size;
|
||||
font-size: rem-calc(14);
|
||||
line-height: $line-height;
|
||||
|
||||
a {
|
||||
@@ -1651,7 +1684,7 @@ table {
|
||||
|
||||
&:first-child {
|
||||
padding-left: $line-height*1.5;
|
||||
width: 80%;
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
&:before {
|
||||
|
||||
@@ -715,6 +715,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
.more-info {
|
||||
clear: both;
|
||||
color: $text-medium;
|
||||
font-size: $small-font-size;
|
||||
|
||||
a {
|
||||
color: $text-medium;
|
||||
}
|
||||
}
|
||||
|
||||
.debate {
|
||||
|
||||
.votes {
|
||||
|
||||
@@ -25,7 +25,7 @@ class AccountController < ApplicationController
|
||||
if @account.organization?
|
||||
params.require(:account).permit(:phone_number, :email_on_comment, :email_on_comment_reply, :newsletter, organization_attributes: [:name, :responsible_name])
|
||||
else
|
||||
params.require(:account).permit(:username, :public_activity, :email_on_comment, :email_on_comment_reply, :newsletter)
|
||||
params.require(:account).permit(:username, :public_activity, :email_on_comment, :email_on_comment_reply, :email_on_direct_message, :email_digest, :newsletter)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
36
app/controllers/direct_messages_controller.rb
Normal file
36
app/controllers/direct_messages_controller.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
class DirectMessagesController < ApplicationController
|
||||
load_and_authorize_resource
|
||||
|
||||
def new
|
||||
@receiver = User.find(params[:user_id])
|
||||
@direct_message = DirectMessage.new(receiver: @receiver)
|
||||
end
|
||||
|
||||
def create
|
||||
@sender = current_user
|
||||
@receiver = User.find(params[:user_id])
|
||||
|
||||
@direct_message = DirectMessage.new(parsed_params)
|
||||
if @direct_message.save
|
||||
Mailer.direct_message_for_receiver(@direct_message).deliver_later
|
||||
Mailer.direct_message_for_sender(@direct_message).deliver_later
|
||||
redirect_to [@receiver, @direct_message], notice: I18n.t("flash.actions.create.direct_message")
|
||||
else
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
@direct_message = DirectMessage.find(params[:id])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def direct_message_params
|
||||
params.require(:direct_message).permit(:title, :body)
|
||||
end
|
||||
|
||||
def parsed_params
|
||||
direct_message_params.merge(sender: @sender, receiver: @receiver)
|
||||
end
|
||||
end
|
||||
@@ -13,6 +13,7 @@ class Management::ProposalsController < Management::BaseController
|
||||
|
||||
def show
|
||||
super
|
||||
@notifications = @proposal.notifications
|
||||
redirect_to management_proposal_path(@proposal), status: :moved_permanently if request.path != management_proposal_path(@proposal)
|
||||
end
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ class NotificationsController < ApplicationController
|
||||
|
||||
def show
|
||||
@notification = current_user.notifications.find(params[:id])
|
||||
redirect_to url_for(@notification.notifiable)
|
||||
redirect_to url_for(@notification.linkable_resource)
|
||||
end
|
||||
|
||||
def mark_all_as_read
|
||||
|
||||
33
app/controllers/proposal_notifications_controller.rb
Normal file
33
app/controllers/proposal_notifications_controller.rb
Normal file
@@ -0,0 +1,33 @@
|
||||
class ProposalNotificationsController < ApplicationController
|
||||
load_and_authorize_resource except: [:new]
|
||||
|
||||
def new
|
||||
@proposal = Proposal.find(params[:proposal_id])
|
||||
@notification = ProposalNotification.new(proposal_id: @proposal.id)
|
||||
authorize! :new, @notification
|
||||
end
|
||||
|
||||
def create
|
||||
@notification = ProposalNotification.new(proposal_notification_params)
|
||||
@proposal = Proposal.find(proposal_notification_params[:proposal_id])
|
||||
if @notification.save
|
||||
@proposal.voters.each do |voter|
|
||||
Notification.add(voter.id, @notification)
|
||||
end
|
||||
redirect_to @notification, notice: I18n.t("flash.actions.create.proposal_notification")
|
||||
else
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
@notification = ProposalNotification.find(params[:id])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def proposal_notification_params
|
||||
params.require(:proposal_notification).permit(:title, :body, :proposal_id)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -2,7 +2,6 @@ class ProposalsController < ApplicationController
|
||||
include CommentableActions
|
||||
include FlagActions
|
||||
|
||||
|
||||
before_action :parse_search_terms, only: [:index, :suggest]
|
||||
before_action :parse_advanced_search_terms, only: :index
|
||||
before_action :parse_tag_filter, only: :index
|
||||
@@ -22,6 +21,7 @@ class ProposalsController < ApplicationController
|
||||
|
||||
def show
|
||||
super
|
||||
@notifications = @proposal.notifications
|
||||
redirect_to proposal_path(@proposal), status: :moved_permanently if request.path != proposal_path(@proposal)
|
||||
end
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ class UsersController < ApplicationController
|
||||
|
||||
load_and_authorize_resource
|
||||
helper_method :authorized_for_filter?
|
||||
helper_method :author?
|
||||
helper_method :author_or_admin?
|
||||
|
||||
def show
|
||||
@@ -65,8 +66,12 @@ class UsersController < ApplicationController
|
||||
@user.public_activity || authorized_current_user?
|
||||
end
|
||||
|
||||
def author?
|
||||
@author ||= current_user && (current_user == @user)
|
||||
end
|
||||
|
||||
def author_or_admin?
|
||||
@author_or_admin ||= current_user && (current_user == @user || current_user.administrator?)
|
||||
@author_or_admin ||= current_user && (author? || current_user.administrator?)
|
||||
end
|
||||
|
||||
def authorized_current_user?
|
||||
|
||||
@@ -35,4 +35,9 @@ module ApplicationHelper
|
||||
}
|
||||
Redcarpet::Markdown.new(renderer, extensions).render(text).html_safe
|
||||
end
|
||||
|
||||
def author_of?(authorable, user)
|
||||
return false if authorable.blank? || user.blank?
|
||||
authorable.author_id == user.id
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
module NotificationsHelper
|
||||
|
||||
def notification_action(notification)
|
||||
notification.notifiable_type == "Comment" ? "replies_to" : "comments_on"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class ApplicationMailer < ActionMailer::Base
|
||||
helper :settings
|
||||
default from: "participacion@madrid.es"
|
||||
default from: "Decide Madrid <no-reply@madrid.es>"
|
||||
layout 'mailer'
|
||||
end
|
||||
|
||||
@@ -42,6 +42,32 @@ class Mailer < ApplicationMailer
|
||||
end
|
||||
end
|
||||
|
||||
def direct_message_for_receiver(direct_message)
|
||||
@direct_message = direct_message
|
||||
@receiver = @direct_message.receiver
|
||||
|
||||
with_user(@receiver) do
|
||||
mail(to: @receiver.email, subject: t('mailers.direct_message_for_receiver.subject'))
|
||||
end
|
||||
end
|
||||
|
||||
def direct_message_for_sender(direct_message)
|
||||
@direct_message = direct_message
|
||||
@sender = @direct_message.sender
|
||||
|
||||
with_user(@sender) do
|
||||
mail(to: @sender.email, subject: t('mailers.direct_message_for_sender.subject'))
|
||||
end
|
||||
end
|
||||
|
||||
def proposal_notification_digest(user)
|
||||
@notifications = user.notifications.where(notifiable_type: "ProposalNotification")
|
||||
|
||||
with_user(user) do
|
||||
mail(to: user.email, subject: t('mailers.proposal_notification_digest.title', org_name: Setting['org_name']))
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def with_user(user, &block)
|
||||
|
||||
@@ -46,11 +46,14 @@ module Abilities
|
||||
can :vote_featured, Proposal
|
||||
can :vote, SpendingProposal
|
||||
can :create, SpendingProposal
|
||||
can :create, DirectMessage
|
||||
can :show, DirectMessage, sender_id: user.id
|
||||
end
|
||||
|
||||
can [:create, :show], ProposalNotification, proposal: { author_id: user.id }
|
||||
|
||||
can :create, Annotation
|
||||
can [:update, :destroy], Annotation, user_id: user.id
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,6 +10,7 @@ module Abilities
|
||||
can :read, Legislation
|
||||
can :read, User
|
||||
can [:search, :read], Annotation
|
||||
can :new, DirectMessage
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
22
app/models/direct_message.rb
Normal file
22
app/models/direct_message.rb
Normal file
@@ -0,0 +1,22 @@
|
||||
class DirectMessage < ActiveRecord::Base
|
||||
belongs_to :sender, class_name: 'User', foreign_key: 'sender_id'
|
||||
belongs_to :receiver, class_name: 'User', foreign_key: 'receiver_id'
|
||||
|
||||
validates :title, presence: true
|
||||
validates :body, presence: true
|
||||
validates :sender, presence: true
|
||||
validates :receiver, presence: true
|
||||
validate :max_per_day
|
||||
|
||||
scope :today, lambda { where('DATE(created_at) = ?', Date.today) }
|
||||
|
||||
def max_per_day
|
||||
return if errors.any?
|
||||
max = Setting[:direct_message_max_per_day]
|
||||
|
||||
if sender.direct_messages_sent.today.count >= max.to_i
|
||||
errors.add(:title, I18n.t('activerecord.errors.models.direct_message.attributes.max_per_day.invalid', max: max))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -21,4 +21,30 @@ class Notification < ActiveRecord::Base
|
||||
Notification.create!(user_id: user_id, notifiable: notifiable)
|
||||
end
|
||||
end
|
||||
|
||||
def notifiable_title
|
||||
case notifiable.class.name
|
||||
when "ProposalNotification"
|
||||
notifiable.proposal.title
|
||||
when "Comment"
|
||||
notifiable.commentable.title
|
||||
else
|
||||
notifiable.title
|
||||
end
|
||||
end
|
||||
|
||||
def notifiable_action
|
||||
case notifiable_type
|
||||
when "ProposalNotification"
|
||||
"proposal_notification"
|
||||
when "Comment"
|
||||
"replies_to"
|
||||
else
|
||||
"comments_on"
|
||||
end
|
||||
end
|
||||
|
||||
def linkable_resource
|
||||
notifiable.is_a?(ProposalNotification) ? notifiable.proposal : notifiable
|
||||
end
|
||||
end
|
||||
@@ -16,6 +16,7 @@ class Proposal < ActiveRecord::Base
|
||||
belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id'
|
||||
belongs_to :geozone
|
||||
has_many :comments, as: :commentable
|
||||
has_many :proposal_notifications
|
||||
|
||||
validates :title, presence: true
|
||||
validates :question, presence: true
|
||||
@@ -97,6 +98,10 @@ class Proposal < ActiveRecord::Base
|
||||
cached_votes_up + physical_votes
|
||||
end
|
||||
|
||||
def voters
|
||||
votes_for.voters
|
||||
end
|
||||
|
||||
def editable?
|
||||
total_votes <= Setting["max_votes_for_proposal_edit"].to_i
|
||||
end
|
||||
@@ -150,6 +155,10 @@ class Proposal < ActiveRecord::Base
|
||||
Setting['votes_for_proposal_success'].to_i
|
||||
end
|
||||
|
||||
def notifications
|
||||
proposal_notifications
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def set_responsible_name
|
||||
|
||||
17
app/models/proposal_notification.rb
Normal file
17
app/models/proposal_notification.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
class ProposalNotification < ActiveRecord::Base
|
||||
belongs_to :author, class_name: 'User', foreign_key: 'author_id'
|
||||
belongs_to :proposal
|
||||
|
||||
validates :title, presence: true
|
||||
validates :body, presence: true
|
||||
validates :proposal, presence: true
|
||||
validate :minimum_interval
|
||||
|
||||
def minimum_interval
|
||||
return true if proposal.try(:notifications).blank?
|
||||
if proposal.notifications.last.created_at > (Time.now - Setting[:proposal_notification_minimum_interval_in_days].to_i.days).to_datetime
|
||||
errors.add(:title, I18n.t('activerecord.errors.models.proposal_notification.attributes.minimum_interval.invalid', interval: Setting[:proposal_notification_minimum_interval_in_days]))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -23,6 +23,8 @@ class User < ActiveRecord::Base
|
||||
has_many :spending_proposals, foreign_key: :author_id
|
||||
has_many :failed_census_calls
|
||||
has_many :notifications
|
||||
has_many :direct_messages_sent, class_name: 'DirectMessage', foreign_key: :sender_id
|
||||
has_many :direct_messages_received, class_name: 'DirectMessage', foreign_key: :receiver_id
|
||||
belongs_to :geozone
|
||||
|
||||
validates :username, presence: true, if: :username_required?
|
||||
@@ -50,6 +52,7 @@ class User < ActiveRecord::Base
|
||||
scope :officials, -> { where("official_level > 0") }
|
||||
scope :for_render, -> { includes(:organization) }
|
||||
scope :by_document, -> (document_type, document_number) { where(document_type: document_type, document_number: document_number) }
|
||||
scope :email_digest, -> { where(email_digest: true) }
|
||||
|
||||
before_validation :clean_document_number
|
||||
|
||||
|
||||
@@ -34,7 +34,9 @@
|
||||
<div>
|
||||
<%= f.label :public_activity do %>
|
||||
<%= f.check_box :public_activity, title: t('account.show.public_activity_label'), label: false %>
|
||||
<span class="checkbox"><%= t("account.show.public_activity_label") %></span>
|
||||
<span class="checkbox">
|
||||
<%= t("account.show.public_activity_label") %>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
@@ -43,21 +45,45 @@
|
||||
<div>
|
||||
<%= f.label :email_on_comment do %>
|
||||
<%= f.check_box :email_on_comment, title: t('account.show.email_on_comment_label'), label: false %>
|
||||
<span class="checkbox"><%= t("account.show.email_on_comment_label") %></span>
|
||||
<span class="checkbox">
|
||||
<%= t("account.show.email_on_comment_label") %>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<%= f.label :email_on_comment_reply do %>
|
||||
<%= f.check_box :email_on_comment_reply, title: t('account.show.email_on_comment_reply_label'), label: false %>
|
||||
<span class="checkbox"><%= t("account.show.email_on_comment_reply_label") %></span>
|
||||
<span class="checkbox">
|
||||
<%= t("account.show.email_on_comment_reply_label") %>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<%= f.label :email_newsletter_subscribed do %>
|
||||
<%= f.check_box :newsletter, title: t('account.show.subscription_to_website_newsletter_label'), label: false %>
|
||||
<span class="checkbox"><%= t("account.show.subscription_to_website_newsletter_label") %></span>
|
||||
<span class="checkbox">
|
||||
<%= t("account.show.subscription_to_website_newsletter_label") %>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<%= f.label :email_digest do %>
|
||||
<%= f.check_box :email_digest, title: t('account.show.email_digest_label'), label: false %>
|
||||
<span class="checkbox">
|
||||
<%= t("account.show.email_digest_label") %>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<%= f.label :email_on_direct_message do %>
|
||||
<%= f.check_box :email_on_direct_message, title: t('account.show.email_on_direct_message_label'), label: false %>
|
||||
<span class="checkbox">
|
||||
<%= t("account.show.email_on_direct_message_label") %>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
|
||||
47
app/views/direct_messages/new.html.erb
Normal file
47
app/views/direct_messages/new.html.erb
Normal file
@@ -0,0 +1,47 @@
|
||||
<div class="row">
|
||||
<div class="small-12 medium-9 column">
|
||||
<%= render 'shared/back_link' %>
|
||||
|
||||
<h1>
|
||||
<%= t("users.direct_messages.new.title", receiver: @receiver.name) %>
|
||||
</h1>
|
||||
|
||||
<% if not current_user %>
|
||||
<div class="callout primary">
|
||||
<p>
|
||||
<%= t("users.direct_messages.new.authenticate",
|
||||
signin: link_to(t("users.direct_messages.new.signin"), new_user_session_path),
|
||||
signup: link_to(t("users.direct_messages.new.signup"), new_user_registration_path)).html_safe %>
|
||||
</p>
|
||||
</div>
|
||||
<% elsif not @receiver.email_on_direct_message? %>
|
||||
<div class="callout primary">
|
||||
<p>
|
||||
<%= t("users.direct_messages.new.direct_messages_bloqued") %>
|
||||
</p>
|
||||
</div>
|
||||
<% elsif can? :create, @direct_message %>
|
||||
<%= form_for [@receiver, @direct_message] do |f| %>
|
||||
<%= render "shared/errors", resource: @direct_message %>
|
||||
|
||||
<%= f.label :title, t("users.direct_messages.new.title_label") %>
|
||||
<%= f.text_field :title, label: false %>
|
||||
|
||||
<%= f.label :body, t("users.direct_messages.new.body_label") %>
|
||||
<%= f.text_area :body, label: false, rows: "3" %>
|
||||
|
||||
<div class="small-12 medium-4">
|
||||
<%= f.submit t("users.direct_messages.new.submit_button"), class: "button expanded" %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<div class="callout warning">
|
||||
<p>
|
||||
<%= t("users.direct_messages.new.verified_only",
|
||||
verify_account: link_to( t("users.direct_messages.new.verify_account"),
|
||||
verification_path )).html_safe %>
|
||||
</p>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
18
app/views/direct_messages/show.html.erb
Normal file
18
app/views/direct_messages/show.html.erb
Normal file
@@ -0,0 +1,18 @@
|
||||
<div class="row">
|
||||
<div class="small-12 medium-9 column">
|
||||
<%= link_to user_path(@direct_message.receiver), class: "back" do %>
|
||||
<span class="icon-angle-left"></span>
|
||||
<%= t("shared.back") %>
|
||||
<% end %>
|
||||
|
||||
<div class="clear"></div>
|
||||
|
||||
<div class="margin-top">
|
||||
<span><strong><%= t("users.direct_messages.show.receiver",
|
||||
receiver: @direct_message.receiver.name) %></strong></span>
|
||||
</div>
|
||||
|
||||
<h1><%= @direct_message.title %></h1>
|
||||
<p><%= @direct_message.body %></p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -32,6 +32,10 @@
|
||||
<td style="text-align: center; border-top: 1px solid #dadfe1; padding-top: 20px;">
|
||||
<p style="font-family: 'Open Sans','Helvetica Neue',arial,sans-serif; margin: 0;padding: 0;line-height: 1.5em;color: #797f7f; font-size: 12px;">
|
||||
<%= setting['org_name'] %></p>
|
||||
|
||||
<p style="font-family: 'Open Sans','Helvetica Neue',arial,sans-serif; margin: 0;padding: 0;line-height: 1.5em;color: #222; font-size: 10px; margin-top: 12px;">
|
||||
<%= t('mailers.no_reply') %></p>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
37
app/views/mailer/direct_message_for_receiver.html.erb
Normal file
37
app/views/mailer/direct_message_for_receiver.html.erb
Normal file
@@ -0,0 +1,37 @@
|
||||
<td style="padding-bottom: 20px; padding-left: 10px;">
|
||||
<h1 style="font-family: 'Open Sans','Helvetica Neue',arial,sans-serif;">
|
||||
<%= @direct_message.title %>
|
||||
</h1>
|
||||
|
||||
<p style="font-family: 'Open Sans','Helvetica Neue',arial,sans-serif;font-size: 14px;font-weight: normal;line-height: 24px;">
|
||||
<%= @direct_message.body %>
|
||||
</p>
|
||||
|
||||
<table style="width: 100%; border-top: 1px solid #DEE0E3; margin-top: 60px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="padding-bottom: 12px; padding-top: 24px; text-align: center;">
|
||||
<%= link_to user_url(@direct_message.sender), style: "font-family: 'Open Sans','Helvetica Neue',arial,sans-serif; background: #f7f5f2; border-radius: 6px; color: #3d3d66!important; font-weight: bold; margin: 0px; padding: 10px 15px; text-align: center; text-decoration: none; min-width: 200px; display: inline-block;", target: "_blank" do %>
|
||||
<%= image_tag('icon_mailer_reply.png', style: "border: 0; display: inline-block; width: 100%; max-width: 12px; vertical-align: sub;", alt: "") %>
|
||||
<%= t('mailers.direct_message_for_receiver.reply',
|
||||
sender: @direct_message.sender.name) %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table style="width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="padding-left: 10px;">
|
||||
<p style="font-family: 'Open Sans','Helvetica Neue',arial,sans-serif;font-size: 14px;font-weight: normal;line-height: 24px; margin: 0; font-style: italic; padding-bottom: 20px;">
|
||||
<%= t('mailers.direct_message_for_receiver.unsubscribe',
|
||||
account: link_to(t('mailers.direct_message_for_receiver.unsubscribe_account'),
|
||||
account_url, style: "color: #2895F1; text-decoration: none;")).html_safe %>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
15
app/views/mailer/direct_message_for_sender.html.erb
Normal file
15
app/views/mailer/direct_message_for_sender.html.erb
Normal file
@@ -0,0 +1,15 @@
|
||||
<td style="padding-bottom: 20px; padding-left: 10px;">
|
||||
|
||||
<p style="font-family: 'Open Sans','Helvetica Neue',arial,sans-serif; font-size: 20px;">
|
||||
<%= t('mailers.direct_message_for_sender.title_html',
|
||||
receiver: @direct_message.receiver.name ) %>
|
||||
</p>
|
||||
|
||||
<h2 style="font-family: 'Open Sans','Helvetica Neue',arial,sans-serif; font-size: 18px;">
|
||||
<%= @direct_message.title %>
|
||||
</h2>
|
||||
|
||||
<p style="font-family: 'Open Sans','Helvetica Neue',arial,sans-serif;font-size: 14px;font-weight: normal;line-height: 24px;">
|
||||
<%= @direct_message.body %>
|
||||
</p>
|
||||
</td>
|
||||
68
app/views/mailer/proposal_notification_digest.html.erb
Normal file
68
app/views/mailer/proposal_notification_digest.html.erb
Normal file
@@ -0,0 +1,68 @@
|
||||
<td>
|
||||
<table style="width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="padding-left: 10px;">
|
||||
<h1 style="font-family: 'Open Sans','Helvetica Neue',arial,sans-serif;">
|
||||
<%= t('mailers.proposal_notification_digest.title',
|
||||
org_name: Setting['org_name']) %>
|
||||
</h1>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<% @notifications.each do |notification| %>
|
||||
<table style="width: 100%; border-top: 1px solid #DEE0E3; padding-top: 12px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="padding-bottom: 20px; padding-left: 10px;">
|
||||
<p style="font-family: 'Open Sans','Helvetica Neue',arial,sans-serif;font-size: 20px;font-weight: bold;line-height: 24px; margin: 0;">
|
||||
<%= link_to notification.notifiable.title, notification_url(notification), style: "color: #2895F1; text-decoration: none;" %>
|
||||
</p>
|
||||
<p style="font-family: 'Open Sans','Helvetica Neue',arial,sans-serif;font-size: 14px;font-weight: normal;line-height: 24px; margin-top: 0; color: #cccccc;">
|
||||
<%= notification.notifiable.proposal.title %> •
|
||||
<%= notification.notifiable.proposal.created_at.to_date %> •
|
||||
<%= notification.notifiable.proposal.author.name %>
|
||||
</p>
|
||||
<p style="font-family: 'Open Sans','Helvetica Neue',arial,sans-serif;font-size: 14px;font-weight: normal;line-height: 24px;">
|
||||
<%= notification.notifiable.body %>
|
||||
</p>
|
||||
|
||||
<table style="width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="padding-bottom: 12px;">
|
||||
<%= link_to proposal_url(notification.notifiable.proposal, anchor: "social-share"), style: "font-family: 'Open Sans','Helvetica Neue',arial,sans-serif; background: #f7f5f2; border-radius: 6px; color: #3d3d66!important; font-weight: bold; margin: 0px; padding: 10px 15px; text-align: center; text-decoration: none; min-width: 160px; display: inline-block;" do %>
|
||||
<%= image_tag('icon_mailer_share.png', style: "border: 0; display: inline-block; width: 100%; max-width: 16px", alt: "") %>
|
||||
<%= t('mailers.proposal_notification_digest.share') %>
|
||||
<% end %>
|
||||
|
||||
<%= link_to proposal_url(notification.notifiable.proposal, anchor: "comments"), style: "font-family: 'Open Sans','Helvetica Neue',arial,sans-serif; background: #f7f5f2; border-radius: 6px; color: #3d3d66!important; font-weight: bold; margin: 0px; padding: 10px 15px; text-align: center; text-decoration: none; min-width: 160px; display: inline-block; margin-left: 12px;" do %>
|
||||
<%= image_tag('icon_mailer_comment.png', style: "border: 0; display: inline-block; width: 100%; max-width: 16px; vertical-align: middle;", alt: "") %>
|
||||
<%= t('mailers.proposal_notification_digest.comment') %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<% end %>
|
||||
|
||||
<table style="width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="padding-left: 10px;">
|
||||
<p style="font-family: 'Open Sans','Helvetica Neue',arial,sans-serif;font-size: 14px;font-weight: normal;line-height: 24px; margin: 0; font-style: italic; padding-bottom: 20px;">
|
||||
<%= t('mailers.proposal_notification_digest.unsubscribe',
|
||||
account: link_to(t('mailers.proposal_notification_digest.unsubscribe_account'),
|
||||
account_url, style: "color: #2895F1; text-decoration: none;")).html_safe %>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
@@ -1,9 +1,13 @@
|
||||
<li id="<%= dom_id(notification) %>" class="notification">
|
||||
<%= link_to notification do %>
|
||||
<p>
|
||||
<em><%= t("notifications.index.#{notification_action(notification)}", count: notification.counter) %></em>
|
||||
<strong><%= notification.notifiable.is_a?(Comment) ? notification.notifiable.commentable.title : notification.notifiable.title %></strong>
|
||||
<em>
|
||||
<%= t("notifications.index.#{notification.notifiable_action}",
|
||||
count: notification.counter) %>
|
||||
</em>
|
||||
<strong><%= notification.notifiable_title %></strong>
|
||||
</p>
|
||||
|
||||
<p class="time"><%= l notification.timestamp, format: :datetime %></p>
|
||||
<% end %>
|
||||
</li>
|
||||
36
app/views/proposal_notifications/new.html.erb
Normal file
36
app/views/proposal_notifications/new.html.erb
Normal file
@@ -0,0 +1,36 @@
|
||||
<div class="row">
|
||||
<div class="small-12 column">
|
||||
<%= render 'shared/back_link' %>
|
||||
|
||||
<h1><%= t("proposal_notifications.new.title") %></h1>
|
||||
|
||||
<div class="callout primary">
|
||||
<p>
|
||||
<%= t("proposal_notifications.new.info_about_receivers_html",
|
||||
count: @proposal.voters.count,
|
||||
proposal_page: link_to(t("proposal_notifications.new.proposal_page"),
|
||||
proposal_path(@proposal, anchor: "comments"))).html_safe %>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="small-12 medium-9 column">
|
||||
<%= form_for @notification do |f| %>
|
||||
<%= render "shared/errors", resource: @notification %>
|
||||
|
||||
<%= f.label :title, t("proposal_notifications.new.title_label") %>
|
||||
<%= f.text_field :title, label: false %>
|
||||
|
||||
<%= f.label :body, t("proposal_notifications.new.body_label") %>
|
||||
<%= f.text_area :body, label: false, rows: "3" %>
|
||||
|
||||
<%= f.hidden_field :proposal_id, value: @proposal.id %>
|
||||
|
||||
<div class="small-12 medium-4">
|
||||
<%= f.submit t("proposal_notifications.new.submit_button"), class: "button expanded" %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
11
app/views/proposal_notifications/show.html.erb
Normal file
11
app/views/proposal_notifications/show.html.erb
Normal file
@@ -0,0 +1,11 @@
|
||||
<div class="row">
|
||||
<div class="small-12 medium-9">
|
||||
<%= link_to user_path(current_user), class: "back" do %>
|
||||
<span class="icon-angle-left"></span>
|
||||
<%= t("proposal_notifications.show.back") %>
|
||||
<% end %>
|
||||
|
||||
<h2><%= @notification.title %></h2>
|
||||
<p><%= @notification.body %></p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,12 +1,7 @@
|
||||
<% cache [locale_and_user_status, @current_order, commentable_cache_key(@proposal), @comment_tree.comments, @comment_tree.comment_authors, @proposal.comments_count, @comment_flags] do %>
|
||||
<section class="row-full comments">
|
||||
<section class="comments">
|
||||
<div class="row">
|
||||
<div id="comments" class="small-12 column">
|
||||
<h2>
|
||||
<%= t("proposals.show.comments_title") %>
|
||||
<span class="js-comments-count">(<%= @proposal.comments_count %>)</span>
|
||||
</h2>
|
||||
|
||||
<%= render 'shared/wide_order_selector', i18n_namespace: "comments" %>
|
||||
|
||||
<% if user_signed_in? %>
|
||||
|
||||
22
app/views/proposals/_filter_subnav.html.erb
Normal file
22
app/views/proposals/_filter_subnav.html.erb
Normal file
@@ -0,0 +1,22 @@
|
||||
<div class="row">
|
||||
<div class="small-12 column">
|
||||
<ul class="tabs" data-tabs id="example-tabs">
|
||||
<li class="tabs-title is-active">
|
||||
<%= link_to "#tab-comments" do %>
|
||||
<h2>
|
||||
<%= t("proposals.show.comments_tab") %>
|
||||
<span class="js-comments-count">(<%= @proposal.comments_count %>)</span>
|
||||
</h2>
|
||||
<% end %>
|
||||
</li>
|
||||
<li class="tabs-title">
|
||||
<%= link_to "#tab-notifications" do %>
|
||||
<h2>
|
||||
<%= t("proposals.show.notifications_tab") %>
|
||||
(<%= @notifications.count %>)
|
||||
</h2>
|
||||
<% end %>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
17
app/views/proposals/_notifications.html.erb
Normal file
17
app/views/proposals/_notifications.html.erb
Normal file
@@ -0,0 +1,17 @@
|
||||
<div class="tabs-panel" id="tab-notifications">
|
||||
<div class="row">
|
||||
<div class="small-12 column">
|
||||
<% if @notifications.blank? %>
|
||||
<div class="callout primary text-center">
|
||||
<%= t('proposals.show.no_notifications') %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% @notifications.each do |notification| %>
|
||||
<h3><%= notification.title %></h3>
|
||||
<p class="more-info"><%= notification.created_at.to_date %></p>
|
||||
<p><%= notification.body %></p>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -12,9 +12,13 @@
|
||||
<div class="small-12 medium-9 column">
|
||||
<%= render "shared/back_link" %>
|
||||
|
||||
<% if author_of?(@proposal, current_user) %>
|
||||
<%= link_to t("proposals.show.send_notification"), new_proposal_notification_path(proposal_id: @proposal.id),
|
||||
class: 'button hollow float-right' %>
|
||||
<% end %>
|
||||
|
||||
<% if current_user && @proposal.editable_by?(current_user) %>
|
||||
<%= link_to edit_proposal_path(@proposal), class: 'edit-proposal button success small float-right' do %>
|
||||
<span class="icon-edit"></span>
|
||||
<%= link_to edit_proposal_path(@proposal), class: 'edit-proposal button hollow float-right' do %>
|
||||
<%= t("proposals.show.edit_proposal_link") %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -104,7 +108,7 @@
|
||||
{ proposal: @proposal, vote_url: vote_proposal_path(@proposal, value: 'yes') } %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar-divider"></div>
|
||||
<div id="social-share" class="sidebar-divider"></div>
|
||||
<h3><%= t("proposals.show.share") %></h3>
|
||||
<div class="social-share-full">
|
||||
<%= social_share_button_tag("#{@proposal.title} #{setting['twitter_hashtag']}") %>
|
||||
@@ -118,4 +122,12 @@
|
||||
</div>
|
||||
</section>
|
||||
<% end %>
|
||||
<%= render "proposals/comments" %>
|
||||
|
||||
<div class="tabs-content" data-tabs-content="example-tabs">
|
||||
<%= render "proposals/filter_subnav" %>
|
||||
<%= render "proposals/notifications" %>
|
||||
|
||||
<div class="tabs-panel is-active" id="tab-comments">
|
||||
<%= render "proposals/comments" %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,17 +7,25 @@
|
||||
<%= proposal.summary %>
|
||||
</td>
|
||||
|
||||
<% if author? %>
|
||||
<td class="text-center">
|
||||
<%= link_to t("users.proposals.send_notification"), new_proposal_notification_path(proposal_id: proposal.id),
|
||||
class: 'button hollow' %>
|
||||
</td>
|
||||
<% end %>
|
||||
|
||||
<% if author_or_admin? %>
|
||||
<td class="text-center">
|
||||
<% if proposal.retired? %>
|
||||
<span class="label alert"><%= t('users.show.retired') %></span>
|
||||
<span class="label alert"><%= t('users.proposals.retired') %></span>
|
||||
<% else %>
|
||||
<%= link_to t('users.show.retire'),
|
||||
<%= link_to t('users.proposals.retire'),
|
||||
retire_form_proposal_path(proposal),
|
||||
class: 'delete' %>
|
||||
class: 'button hollow alert' %>
|
||||
<% end %>
|
||||
</td>
|
||||
<% end %>
|
||||
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
|
||||
@@ -2,7 +2,17 @@
|
||||
<div class="activity row margin-top">
|
||||
<div class="small-12 column">
|
||||
|
||||
<h2>
|
||||
<% if @user != current_user && @user.email_on_direct_message? %>
|
||||
<%= link_to t("users.show.send_private_message"),
|
||||
new_user_direct_message_path(@user),
|
||||
class: "button hollow float-right" %>
|
||||
<% else %>
|
||||
<div class="callout primary float-right">
|
||||
<%= t("users.show.no_private_messages") %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<h2 class="inline-block">
|
||||
<%= avatar_image(@user, seed: @user.id, size: 60) %>
|
||||
<%= @user.name %>
|
||||
<% if current_administrator? %>
|
||||
|
||||
@@ -95,6 +95,8 @@ search:
|
||||
# - '{devise,simple_form}.*'
|
||||
ignore_missing:
|
||||
- 'unauthorized.*'
|
||||
- 'activerecord.errors.models.proposal_notification.*'
|
||||
- 'activerecord.errors.models.direct_message.*'
|
||||
- 'errors.messages.blank'
|
||||
- 'errors.messages.taken'
|
||||
- 'devise.failure.invalid'
|
||||
@@ -134,6 +136,7 @@ ignore_unused:
|
||||
- 'spending_proposals.index.search_form.*'
|
||||
- 'notifications.index.comments_on*'
|
||||
- 'notifications.index.replies_to*'
|
||||
- 'notifications.index.proposal_notification*'
|
||||
- 'helpers.page_entries_info.*' # kaminari
|
||||
- 'views.pagination.*' # kaminari
|
||||
- 'shared.suggest.*'
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
en:
|
||||
activerecord:
|
||||
errors:
|
||||
models:
|
||||
user:
|
||||
attributes:
|
||||
email:
|
||||
password_already_set: "This user already has a password"
|
||||
models:
|
||||
activity:
|
||||
one: "activity"
|
||||
@@ -76,11 +70,23 @@ en:
|
||||
title: "Title"
|
||||
errors:
|
||||
models:
|
||||
user:
|
||||
attributes:
|
||||
email:
|
||||
password_already_set: "This user already has a password"
|
||||
debate:
|
||||
attributes:
|
||||
tag_list:
|
||||
less_than_or_equal_to: "tags must be less than or equal to %{count}"
|
||||
direct_message:
|
||||
attributes:
|
||||
max_per_day:
|
||||
invalid: "You can only send a maximum of %{max} direct messages per day"
|
||||
proposal:
|
||||
attributes:
|
||||
tag_list:
|
||||
less_than_or_equal_to: "tags must be less than or equal to %{count}"
|
||||
proposal_notification:
|
||||
attributes:
|
||||
minimum_interval:
|
||||
invalid: "You have to wait a minium of %{interval} days between notifications"
|
||||
@@ -1,11 +1,5 @@
|
||||
es:
|
||||
activerecord:
|
||||
errors:
|
||||
models:
|
||||
user:
|
||||
attributes:
|
||||
email:
|
||||
password_already_set: "Este usuario ya tiene una clave asociada"
|
||||
models:
|
||||
activity:
|
||||
one: "actividad"
|
||||
@@ -76,11 +70,23 @@ es:
|
||||
title: "Título"
|
||||
errors:
|
||||
models:
|
||||
user:
|
||||
attributes:
|
||||
email:
|
||||
password_already_set: "Este usuario ya tiene una clave asociada"
|
||||
debate:
|
||||
attributes:
|
||||
tag_list:
|
||||
less_than_or_equal_to: "los temas deben ser menor o igual que %{count}"
|
||||
direct_message:
|
||||
attributes:
|
||||
max_per_day:
|
||||
invalid: "Sólo puedes enviar %{max} mensajes directos por día"
|
||||
proposal:
|
||||
attributes:
|
||||
tag_list:
|
||||
less_than_or_equal_to: "los temas deben ser menor o igual que %{count}"
|
||||
proposal_notification:
|
||||
attributes:
|
||||
minimum_interval:
|
||||
invalid: "Debes esperar un mínimo de %{interval} días entre notificaciones"
|
||||
@@ -15,6 +15,8 @@ en:
|
||||
public_activity_label: Keep my list of activities public
|
||||
save_changes_submit: Save changes
|
||||
subscription_to_website_newsletter_label: Receive by email website relevant information
|
||||
email_on_direct_message_label: Receive emails about direct messages
|
||||
email_digest_label: Receive a summary of proposal notifications
|
||||
title: My account
|
||||
user_permission_debates: Participate on debates
|
||||
user_permission_info: With your account you can...
|
||||
@@ -141,11 +143,13 @@ en:
|
||||
accept_terms_title: I agree to the Privacy Policy and the Terms and conditions of use
|
||||
conditions: Terms and conditions of use
|
||||
debate: Debate
|
||||
direct_message: private message
|
||||
error: error
|
||||
errors: errors
|
||||
not_saved: 'prevented this %{resource} from being saved:'
|
||||
policy: Privacy Policy
|
||||
proposal: Proposal
|
||||
proposal_notification: "Notification"
|
||||
spending_proposal: Spending proposal
|
||||
user: Account
|
||||
verification/sms: phone
|
||||
@@ -221,6 +225,9 @@ en:
|
||||
other: There are %{count} new comments on
|
||||
empty_notifications: You don't have new notifications.
|
||||
mark_all_as_read: Mark all as read
|
||||
proposal_notification:
|
||||
one: There is one new notification on
|
||||
other: There are %{count} new notifications on
|
||||
replies_to:
|
||||
one: Someone replied to your comment on
|
||||
other: There are %{count} new replies to your comment on
|
||||
@@ -349,17 +356,30 @@ en:
|
||||
one: 1 comment
|
||||
other: "%{count} comments"
|
||||
zero: No comments
|
||||
comments_title: Comments
|
||||
comments_tab: Comments
|
||||
edit_proposal_link: Edit
|
||||
flag: This proposal has been flagged as inappropriate by several users.
|
||||
login_to_comment: You must %{signin} or %{signup} to leave a comment.
|
||||
notifications_tab: Notifications
|
||||
retired_warning: "The author considers this proposal should not receive more supports."
|
||||
retired_warning_link_to_explanation: Read the explanation before voting for it.
|
||||
retired: Proposal retired by the author
|
||||
share: Share
|
||||
send_notification: Send notification
|
||||
no_notifications: "This proposal has any notifications."
|
||||
update:
|
||||
form:
|
||||
submit_button: Save changes
|
||||
proposal_notifications:
|
||||
new:
|
||||
title: "Send message"
|
||||
title_label: "Title"
|
||||
body_label: "Message"
|
||||
submit_button: "Send message"
|
||||
info_about_receivers_html: "This message will be send to <strong>%{count} people</strong> and it will be visible in %{proposal_page}.<br> Message are not sent immediately, users will receive periodically an email with all proposal notifications."
|
||||
proposal_page: "the proposal's page"
|
||||
show:
|
||||
back: "Go back to my activity"
|
||||
shared:
|
||||
advanced_search:
|
||||
author_type: 'By author category'
|
||||
@@ -483,6 +503,20 @@ en:
|
||||
manage:
|
||||
all: "You do not have permission to carry out the action '%{action}' on %{subject}."
|
||||
users:
|
||||
direct_messages:
|
||||
new:
|
||||
body_label: Message
|
||||
direct_messages_bloqued: "This user has decided not to receive direct messages"
|
||||
submit_button: Send message
|
||||
title: Send private message to %{receiver}
|
||||
title_label: Title
|
||||
verified_only: To send a private message %{verify_account}
|
||||
verify_account: verify your account
|
||||
authenticate: You must %{signin} or %{signup} to continue.
|
||||
signin: sign in
|
||||
signup: sign up
|
||||
show:
|
||||
receiver: Message sent to %{receiver}
|
||||
show:
|
||||
deleted: Deleted
|
||||
deleted_debate: This debate has been deleted
|
||||
@@ -503,9 +537,13 @@ en:
|
||||
one: 1 Spending proposal
|
||||
other: "%{count} Spending proposals"
|
||||
no_activity: User has no public activity
|
||||
no_private_messages: "This user doesn't accept private messages."
|
||||
private_activity: This user decided to keep the activity list private
|
||||
retire: Retire
|
||||
retired: Retired
|
||||
send_private_message: "Send private message"
|
||||
proposals:
|
||||
send_notification: "Send notification"
|
||||
retire: "Retire"
|
||||
retired: "Retired"
|
||||
votes:
|
||||
agree: I agree
|
||||
anonymous: Too many anonymous votes to admit vote %{verify_account}.
|
||||
|
||||
@@ -15,6 +15,8 @@ es:
|
||||
public_activity_label: Mostrar públicamente mi lista de actividades
|
||||
save_changes_submit: Guardar cambios
|
||||
subscription_to_website_newsletter_label: Recibir emails con información interesante sobre la web
|
||||
email_on_direct_message_label: Recibir emails con mensajes directos
|
||||
email_digest_label: Recibir resumen de notificaciones sobre propuestas
|
||||
title: Mi cuenta
|
||||
user_permission_debates: Participar en debates
|
||||
user_permission_info: Con tu cuenta ya puedes...
|
||||
@@ -141,11 +143,13 @@ es:
|
||||
accept_terms_title: Acepto la Política de privacidad y las Condiciones de uso
|
||||
conditions: Condiciones de uso
|
||||
debate: el debate
|
||||
direct_message: el mensaje privado
|
||||
error: error
|
||||
errors: errores
|
||||
not_saved: 'impidieron guardar %{resource}:'
|
||||
policy: Política de privacidad
|
||||
proposal: la propuesta
|
||||
proposal_notification: "la notificación"
|
||||
spending_proposal: la propuesta de gasto
|
||||
user: la cuenta
|
||||
verification/sms: el teléfono
|
||||
@@ -221,6 +225,9 @@ es:
|
||||
other: Hay %{count} comentarios nuevos en
|
||||
empty_notifications: No tienes notificaciones nuevas.
|
||||
mark_all_as_read: Marcar todas como leídas
|
||||
proposal_notification:
|
||||
one: Hay una nueva notificación en
|
||||
other: Hay %{count} nuevas notificaciones en
|
||||
replies_to:
|
||||
one: Hay una respuesta nueva a tu comentario en
|
||||
other: Hay %{count} nuevas respuestas a tu comentario en
|
||||
@@ -349,17 +356,30 @@ es:
|
||||
one: 1 Comentario
|
||||
other: "%{count} Comentarios"
|
||||
zero: Sin comentarios
|
||||
comments_title: Comentarios
|
||||
comments_tab: Comentarios
|
||||
edit_proposal_link: Editar propuesta
|
||||
flag: Esta propuesta ha sido marcada como inapropiada por varios usuarios.
|
||||
login_to_comment: Necesitas %{signin} o %{signup} para comentar.
|
||||
notifications_tab: Notificaciones
|
||||
retired_warning: "El autor de esta propuesta considera que ya no debe seguir recogiendo apoyos."
|
||||
retired_warning_link_to_explanation: Revisa su explicación antes de apoyarla.
|
||||
retired: Propuesta retirada por el autor
|
||||
send_notification: Enviar notificación
|
||||
share: Compartir
|
||||
no_notifications: "Esta propuesta no tiene notificaciones."
|
||||
update:
|
||||
form:
|
||||
submit_button: Guardar cambios
|
||||
proposal_notifications:
|
||||
new:
|
||||
title: "Enviar mensaje"
|
||||
title_label: "Título"
|
||||
body_label: "Mensaje"
|
||||
submit_button: "Enviar mensaje"
|
||||
info_about_receivers_html: "Este mensaje se enviará a <strong>%{count} usuarios</strong> y se publicará en %{proposal_page}.<br> El mensaje no se enviará inmediatamente, los usuarios recibirán periódicamente un email con todas las notificaciones de propuestas."
|
||||
proposal_page: "la página de la propuesta"
|
||||
show:
|
||||
back: "Volver a mi actividad"
|
||||
shared:
|
||||
advanced_search:
|
||||
author_type: 'Por categoría de autor'
|
||||
@@ -483,6 +503,20 @@ es:
|
||||
manage:
|
||||
all: "No tienes permiso para realizar la acción '%{action}' sobre %{subject}."
|
||||
users:
|
||||
direct_messages:
|
||||
new:
|
||||
body_label: "Mensaje"
|
||||
direct_messages_bloqued: "Este usuarios ha decidido no recibir mensajes directos"
|
||||
submit_button: "Enviar mensaje"
|
||||
title: Enviar mensaje privado a %{receiver}
|
||||
title_label: "Título"
|
||||
verified_only: Para enviar un mensaje privado %{verify_account}
|
||||
verify_account: verifica tu cuenta
|
||||
authenticate: Necesitas %{signin} o %{signup}.
|
||||
signin: iniciar sesión
|
||||
signup: registrarte
|
||||
show:
|
||||
receiver: Mensaje enviado a %{receiver}
|
||||
show:
|
||||
deleted: Eliminado
|
||||
deleted_debate: Este debate ha sido eliminado
|
||||
@@ -503,9 +537,13 @@ es:
|
||||
one: 1 Propuesta de inversión
|
||||
other: "%{count} Propuestas de inversión"
|
||||
no_activity: Usuario sin actividad pública
|
||||
no_private_messages: "Este usuario no acepta mensajes privados."
|
||||
private_activity: Este usuario ha decidido mantener en privado su lista de actividades
|
||||
retire: Retirar
|
||||
retired: Retirada
|
||||
send_private_message: "Enviar un mensaje privado"
|
||||
proposals:
|
||||
send_notification: "Enviar notificación"
|
||||
retire: "Retirar"
|
||||
retired: "Retirada"
|
||||
votes:
|
||||
agree: Estoy de acuerdo
|
||||
anonymous: Demasiados votos anónimos, para poder votar %{verify_account}.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
en:
|
||||
mailers:
|
||||
no_reply: "This message was sent from an email address that does not accept replies."
|
||||
comment:
|
||||
hi: Hi
|
||||
new_comment_by_html: There is a new comment from <b>%{commenter}</b>
|
||||
@@ -29,4 +30,18 @@ en:
|
||||
signatory: "DEPARTMENT OF PUBLIC PARTICIPATION"
|
||||
sorry: "Sorry for the inconvenience and we again thank you for your invaluable participation."
|
||||
subject: "Your investment project '%{code}' has been marked as unfeasible"
|
||||
unfeasible_html: "From the Madrid City Council we want to thank you for your participation in the <strong>participatory budgets of the city of Madrid</strong>. We regret to inform you that your proposal <strong>'%{title}'</strong> will be excluded from this participatory process for the following reason:"
|
||||
unfeasible_html: "From the Madrid City Council we want to thank you for your participation in the <strong>participatory budgets of the city of Madrid</strong>. We regret to inform you that your proposal <strong>'%{title}'</strong> will be excluded from this participatory process for the following reason:"
|
||||
proposal_notification_digest:
|
||||
title: "Proposal notifications in %{org_name}"
|
||||
share: Share proposal
|
||||
comment: Comment proposal
|
||||
unsubscribe: "If you don't want receive proposal's notification, visit %{account} and unckeck 'Receive a summary of proposal notifications'."
|
||||
unsubscribe_account: My account
|
||||
direct_message_for_receiver:
|
||||
subject: "You have received a new private message"
|
||||
reply: Reply to %{sender}
|
||||
unsubscribe: "If you don't want receive direct messages, visit %{account} and unckeck 'Receive emails about direct messages'."
|
||||
unsubscribe_account: My account
|
||||
direct_message_for_sender:
|
||||
subject: "You have send a new private message"
|
||||
title_html: "You have send a new private message to <strong>%{receiver}</strong> with the content:"
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
es:
|
||||
mailers:
|
||||
no_reply: "Este mensaje se ha enviado desde una dirección de correo electrónico que no admite respuestas."
|
||||
comment:
|
||||
hi: Hola
|
||||
new_comment_by_html: Hay un nuevo comentario de <b>%{commenter}</b> en
|
||||
@@ -30,3 +31,17 @@ es:
|
||||
sorry: "Sentimos las molestias ocasionadas y volvemos a darte las gracias por tu inestimable participación."
|
||||
subject: "Tu propuesta de inversión '%{code}' ha sido marcada como inviable"
|
||||
unfeasible_html: "Desde el Ayuntamiento de Madrid queremos agradecer tu participación en los <strong>Presupuestos Participativos de la ciudad de Madrid</strong>. Lamentamos informarte de que tu propuesta <strong>'%{title}'</strong> quedará excluida de este proceso participativo por el siguiente motivo:"
|
||||
proposal_notification_digest:
|
||||
title: "Notificaciones de propuestas en %{org_name}"
|
||||
share: Compartir propuesta
|
||||
comment: Comentar propuesta
|
||||
unsubscribe: "Si no quieres recibir notificaciones de propuestas, puedes entrar en %{account} y desmarcar la opción 'Recibir resumen de notificaciones sobre propuestas'."
|
||||
unsubscribe_account: Mi cuenta
|
||||
direct_message_for_receiver:
|
||||
subject: "Has recibido un nuevo mensaje privado"
|
||||
reply: Responder a %{sender}
|
||||
unsubscribe: "Si no quieres recibir mensajes privados, puedes entrar en %{account} y desmarcar la opción 'Recibir emails con mensajes directos'."
|
||||
unsubscribe_account: Mi cuenta
|
||||
direct_message_for_sender:
|
||||
subject: "Has enviado un nuevo mensaje privado"
|
||||
title_html: "Has enviado un nuevo mensaje privado a <strong>%{receiver}</strong> con el siguiente contenido:"
|
||||
@@ -5,8 +5,11 @@ en:
|
||||
create:
|
||||
notice: "%{resource_name} created successfully."
|
||||
debate: "Debate created successfully."
|
||||
direct_message: "You message has been sent successfully."
|
||||
proposal: "Proposal created successfully."
|
||||
proposal_notification: "Your message has been sent correctly."
|
||||
spending_proposal: "Spending proposal created successfully. You can access it from %{activity}"
|
||||
|
||||
save_changes:
|
||||
notice: Changes saved
|
||||
update:
|
||||
|
||||
@@ -5,7 +5,9 @@ es:
|
||||
create:
|
||||
notice: "%{resource_name} creado correctamente."
|
||||
debate: "Debate creado correctamente."
|
||||
direct_message: "Tu mensaje ha sido enviado correctamente."
|
||||
proposal: "Propuesta creada correctamente."
|
||||
proposal_notification: "Tu message ha sido enviado correctamente."
|
||||
spending_proposal: "Propuesta de inversión creada correctamente. Puedes acceder a ella desde %{activity}"
|
||||
save_changes:
|
||||
notice: Cambios guardados
|
||||
|
||||
@@ -83,7 +83,9 @@ Rails.application.routes.draw do
|
||||
get :search, on: :collection
|
||||
end
|
||||
|
||||
resources :users, only: [:show]
|
||||
resources :users, only: [:show] do
|
||||
resources :direct_messages, only: [:new, :create, :show]
|
||||
end
|
||||
|
||||
resource :account, controller: "account", only: [:show, :update, :delete] do
|
||||
get :erase, on: :collection
|
||||
@@ -93,6 +95,8 @@ Rails.application.routes.draw do
|
||||
put :mark_all_as_read, on: :collection
|
||||
end
|
||||
|
||||
resources :proposal_notifications, only: [:new, :create, :show]
|
||||
|
||||
resource :verification, controller: "verification", only: [:show]
|
||||
|
||||
scope module: :verification do
|
||||
|
||||
12
db/migrate/20160601103338_create_proposal_notifications.rb
Normal file
12
db/migrate/20160601103338_create_proposal_notifications.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
class CreateProposalNotifications < ActiveRecord::Migration
|
||||
def change
|
||||
create_table :proposal_notifications do |t|
|
||||
t.string :title
|
||||
t.text :body
|
||||
t.integer :author_id
|
||||
t.integer :proposal_id
|
||||
|
||||
t.timestamps null: false
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
class AddEmailOnProposalNotificationToUsers < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :users, :email_on_proposal_notification, :boolean, default: true
|
||||
end
|
||||
end
|
||||
12
db/migrate/20160608174104_create_direct_messages.rb
Normal file
12
db/migrate/20160608174104_create_direct_messages.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
class CreateDirectMessages < ActiveRecord::Migration
|
||||
def change
|
||||
create_table :direct_messages do |t|
|
||||
t.integer :sender_id
|
||||
t.integer :receiver_id
|
||||
t.string :title
|
||||
t.text :body
|
||||
|
||||
t.timestamps null: false
|
||||
end
|
||||
end
|
||||
end
|
||||
5
db/migrate/20160613150659_add_email_digest_to_users.rb
Normal file
5
db/migrate/20160613150659_add_email_digest_to_users.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class AddEmailDigestToUsers < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :users, :email_digest, :boolean, default: true
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
class AddEmailOnDirectMessagesToUsers < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :users, :email_on_direct_message, :boolean, default: true
|
||||
end
|
||||
end
|
||||
55
db/schema.rb
55
db/schema.rb
@@ -11,7 +11,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20160518141543) do
|
||||
ActiveRecord::Schema.define(version: 20160614160949) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
@@ -168,6 +168,15 @@ ActiveRecord::Schema.define(version: 20160518141543) do
|
||||
|
||||
add_index "delayed_jobs", ["priority", "run_at"], name: "delayed_jobs_priority", using: :btree
|
||||
|
||||
create_table "direct_messages", force: :cascade do |t|
|
||||
t.integer "sender_id"
|
||||
t.integer "receiver_id"
|
||||
t.string "title"
|
||||
t.text "body"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
end
|
||||
|
||||
create_table "failed_census_calls", force: :cascade do |t|
|
||||
t.integer "user_id"
|
||||
t.string "document_number"
|
||||
@@ -260,6 +269,15 @@ ActiveRecord::Schema.define(version: 20160518141543) do
|
||||
|
||||
add_index "organizations", ["user_id"], name: "index_organizations_on_user_id", using: :btree
|
||||
|
||||
create_table "proposal_notifications", force: :cascade do |t|
|
||||
t.string "title"
|
||||
t.text "body"
|
||||
t.integer "author_id"
|
||||
t.integer "proposal_id"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
end
|
||||
|
||||
create_table "proposals", force: :cascade do |t|
|
||||
t.string "title", limit: 80
|
||||
t.text "description"
|
||||
@@ -393,30 +411,30 @@ ActiveRecord::Schema.define(version: 20160518141543) do
|
||||
add_index "tolk_translations", ["phrase_id", "locale_id"], name: "index_tolk_translations_on_phrase_id_and_locale_id", unique: true, using: :btree
|
||||
|
||||
create_table "users", force: :cascade do |t|
|
||||
t.string "email", default: ""
|
||||
t.string "encrypted_password", default: "", null: false
|
||||
t.string "email", default: ""
|
||||
t.string "encrypted_password", default: "", null: false
|
||||
t.string "reset_password_token"
|
||||
t.datetime "reset_password_sent_at"
|
||||
t.datetime "remember_created_at"
|
||||
t.integer "sign_in_count", default: 0, null: false
|
||||
t.integer "sign_in_count", default: 0, null: false
|
||||
t.datetime "current_sign_in_at"
|
||||
t.datetime "last_sign_in_at"
|
||||
t.string "current_sign_in_ip"
|
||||
t.string "last_sign_in_ip"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "confirmation_token"
|
||||
t.datetime "confirmed_at"
|
||||
t.datetime "confirmation_sent_at"
|
||||
t.string "unconfirmed_email"
|
||||
t.boolean "email_on_comment", default: false
|
||||
t.boolean "email_on_comment_reply", default: false
|
||||
t.string "phone_number", limit: 30
|
||||
t.boolean "email_on_comment", default: false
|
||||
t.boolean "email_on_comment_reply", default: false
|
||||
t.string "phone_number", limit: 30
|
||||
t.string "official_position"
|
||||
t.integer "official_level", default: 0
|
||||
t.integer "official_level", default: 0
|
||||
t.datetime "hidden_at"
|
||||
t.string "sms_confirmation_code"
|
||||
t.string "username", limit: 60
|
||||
t.string "username", limit: 60
|
||||
t.string "document_number"
|
||||
t.string "document_type"
|
||||
t.datetime "residence_verified_at"
|
||||
@@ -427,20 +445,23 @@ ActiveRecord::Schema.define(version: 20160518141543) do
|
||||
t.datetime "letter_requested_at"
|
||||
t.datetime "confirmed_hide_at"
|
||||
t.string "letter_verification_code"
|
||||
t.integer "failed_census_calls_count", default: 0
|
||||
t.integer "failed_census_calls_count", default: 0
|
||||
t.datetime "level_two_verified_at"
|
||||
t.string "erase_reason"
|
||||
t.datetime "erased_at"
|
||||
t.boolean "public_activity", default: true
|
||||
t.boolean "newsletter", default: true
|
||||
t.integer "notifications_count", default: 0
|
||||
t.boolean "registering_with_oauth", default: false
|
||||
t.boolean "public_activity", default: true
|
||||
t.boolean "newsletter", default: true
|
||||
t.integer "notifications_count", default: 0
|
||||
t.boolean "registering_with_oauth", default: false
|
||||
t.string "locale"
|
||||
t.string "oauth_email"
|
||||
t.integer "geozone_id"
|
||||
t.string "redeemable_code"
|
||||
t.string "gender", limit: 10
|
||||
t.string "gender", limit: 10
|
||||
t.datetime "date_of_birth"
|
||||
t.boolean "email_on_proposal_notification", default: true
|
||||
t.boolean "email_digest", default: true
|
||||
t.boolean "email_on_direct_message", default: true
|
||||
end
|
||||
|
||||
add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
|
||||
|
||||
@@ -74,3 +74,7 @@ Setting['banner-style.banner-style-three'] = "Banner style 3"
|
||||
Setting['banner-img.banner-img-one'] = "Banner image 1"
|
||||
Setting['banner-img.banner-img-two'] = "Banner image 2"
|
||||
Setting['banner-img.banner-img-three'] = "Banner image 3"
|
||||
|
||||
# Proposal notifications
|
||||
Setting['proposal_notification_minimum_interval_in_days '] = 3
|
||||
Setting['direct_message_max_per_day'] = 3
|
||||
14
lib/email_digest.rb
Normal file
14
lib/email_digest.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
class EmailDigest
|
||||
|
||||
def initialize
|
||||
end
|
||||
|
||||
def create
|
||||
User.email_digest.each do |user|
|
||||
if user.notifications.where(notifiable_type: "ProposalNotification").any?
|
||||
Mailer.proposal_notification_digest(user).deliver_later
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
9
lib/tasks/emails.rake
Normal file
9
lib/tasks/emails.rake
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace :emails do
|
||||
|
||||
desc "Sends email digest of proposal notifications to each user"
|
||||
task digest: :environment do
|
||||
email_digest = EmailDigest.new
|
||||
email_digest.create
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,5 +1,4 @@
|
||||
FactoryGirl.define do
|
||||
|
||||
sequence(:document_number) { |n| "#{n.to_s.rjust(8, '0')}X" }
|
||||
|
||||
factory :user do
|
||||
@@ -137,7 +136,7 @@ FactoryGirl.define do
|
||||
|
||||
factory :proposal do
|
||||
sequence(:title) { |n| "Proposal #{n} title" }
|
||||
summary 'In summary, what we want is...'
|
||||
sequence(:summary) { |n| "In summary, what we want is... #{n}" }
|
||||
description 'Proposal description'
|
||||
question 'Proposal question'
|
||||
external_url 'http://external_documention.es'
|
||||
@@ -323,7 +322,20 @@ FactoryGirl.define do
|
||||
style {["banner-style-one", "banner-style-two", "banner-style-three"].sample}
|
||||
image {["banner.banner-img-one", "banner.banner-img-two", "banner.banner-img-three"].sample}
|
||||
target_url {["/proposals", "/debates" ].sample}
|
||||
post_started_at Time.now - 7.days
|
||||
post_ended_at Time.now + 7.days
|
||||
post_started_at Time.now - 7.days
|
||||
post_ended_at Time.now + 7.days
|
||||
end
|
||||
|
||||
factory :proposal_notification do
|
||||
title "Thank you for supporting my proposal"
|
||||
body "Please let others know so we can make it happen"
|
||||
proposal
|
||||
end
|
||||
|
||||
factory :direct_message do
|
||||
title "Hey"
|
||||
body "How are You doing?"
|
||||
association :sender, factory: :user
|
||||
association :receiver, factory: :user
|
||||
end
|
||||
end
|
||||
|
||||
@@ -35,6 +35,8 @@ feature 'Account' do
|
||||
fill_in 'account_username', with: 'Larry Bird'
|
||||
check 'account_email_on_comment'
|
||||
check 'account_email_on_comment_reply'
|
||||
uncheck 'account_email_digest'
|
||||
uncheck 'account_email_on_direct_message'
|
||||
click_button 'Save changes'
|
||||
|
||||
expect(page).to have_content "Changes saved"
|
||||
@@ -42,8 +44,10 @@ feature 'Account' do
|
||||
visit account_path
|
||||
|
||||
expect(page).to have_selector("input[value='Larry Bird']")
|
||||
expect(page).to have_selector("input[id='account_email_on_comment'][value='1']")
|
||||
expect(page).to have_selector("input[id='account_email_on_comment_reply'][value='1']")
|
||||
expect(find("#account_email_on_comment")).to be_checked
|
||||
expect(find("#account_email_on_comment_reply")).to be_checked
|
||||
expect(find("#account_email_digest")).to_not be_checked
|
||||
expect(find("#account_email_on_direct_message")).to_not be_checked
|
||||
end
|
||||
|
||||
scenario 'Edit Organization' do
|
||||
@@ -53,6 +57,7 @@ feature 'Account' do
|
||||
fill_in 'account_organization_attributes_name', with: 'Google'
|
||||
check 'account_email_on_comment'
|
||||
check 'account_email_on_comment_reply'
|
||||
|
||||
click_button 'Save changes'
|
||||
|
||||
expect(page).to have_content "Changes saved"
|
||||
@@ -60,8 +65,8 @@ feature 'Account' do
|
||||
visit account_path
|
||||
|
||||
expect(page).to have_selector("input[value='Google']")
|
||||
expect(page).to have_selector("input[id='account_email_on_comment'][value='1']")
|
||||
expect(page).to have_selector("input[id='account_email_on_comment_reply'][value='1']")
|
||||
expect(find("#account_email_on_comment")).to be_checked
|
||||
expect(find("#account_email_on_comment_reply")).to be_checked
|
||||
end
|
||||
|
||||
scenario "Errors on edit" do
|
||||
|
||||
@@ -168,7 +168,10 @@ feature 'Commenting proposals' do
|
||||
|
||||
within "#comments" do
|
||||
expect(page).to have_content 'Have you thought about...?'
|
||||
expect(page).to have_content '(1)'
|
||||
end
|
||||
|
||||
within "#tab-comments-label" do
|
||||
expect(page).to have_content 'Comments (1)'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
122
spec/features/direct_messages_spec.rb
Normal file
122
spec/features/direct_messages_spec.rb
Normal file
@@ -0,0 +1,122 @@
|
||||
require 'rails_helper'
|
||||
|
||||
feature 'Direct messages' do
|
||||
|
||||
background do
|
||||
Setting[:direct_message_max_per_day] = 3
|
||||
end
|
||||
|
||||
scenario "Create" do
|
||||
sender = create(:user, :level_two)
|
||||
receiver = create(:user, :level_two)
|
||||
|
||||
login_as(sender)
|
||||
visit user_path(receiver)
|
||||
|
||||
click_link "Send private message"
|
||||
|
||||
expect(page).to have_content "Send private message to #{receiver.name}"
|
||||
|
||||
fill_in 'direct_message_title', with: "Hey!"
|
||||
fill_in 'direct_message_body', with: "How are you doing?"
|
||||
click_button "Send message"
|
||||
|
||||
expect(page).to have_content "You message has been sent successfully."
|
||||
expect(page).to have_content "Hey!"
|
||||
expect(page).to have_content "How are you doing?"
|
||||
end
|
||||
|
||||
context "Permissions" do
|
||||
|
||||
scenario "Do not display link to send message to myself" do
|
||||
sender = create(:user, :level_two)
|
||||
|
||||
login_as(sender)
|
||||
visit user_path(sender)
|
||||
|
||||
expect(page).to_not have_link "Send private message"
|
||||
end
|
||||
|
||||
scenario "Do not display link if direct message for user not allowed" do
|
||||
sender = create(:user, :level_two)
|
||||
receiver = create(:user, :level_two, email_on_direct_message: false)
|
||||
|
||||
login_as(sender)
|
||||
visit user_path(receiver)
|
||||
|
||||
expect(page).to have_content "This user doesn't accept private messages."
|
||||
expect(page).to_not have_link "Send private message"
|
||||
end
|
||||
|
||||
scenario "Unverified user" do
|
||||
sender = create(:user)
|
||||
receiver = create(:user)
|
||||
|
||||
login_as(sender)
|
||||
visit new_user_direct_message_path(receiver)
|
||||
|
||||
expect(page).to have_content "To send a private message verify your account"
|
||||
expect(page).to_not have_link "Send private message"
|
||||
end
|
||||
|
||||
scenario "User not logged in" do
|
||||
sender = create(:user)
|
||||
receiver = create(:user)
|
||||
|
||||
visit new_user_direct_message_path(receiver)
|
||||
|
||||
expect(page).to have_content "You must sign in or sign up to continue."
|
||||
expect(page).to_not have_link "Send private message"
|
||||
end
|
||||
|
||||
scenario "Accessing form directly" do
|
||||
sender = create(:user, :level_two)
|
||||
receiver = create(:user, :level_two, email_on_direct_message: false)
|
||||
|
||||
login_as(sender)
|
||||
visit new_user_direct_message_path(receiver)
|
||||
|
||||
expect(page).to have_content("This user has decided not to receive direct messages")
|
||||
expect(page).to_not have_css("#direct_message_title")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
scenario "Error messages" do
|
||||
author = create(:user)
|
||||
proposal = create(:proposal, author: author)
|
||||
|
||||
login_as(author)
|
||||
|
||||
visit new_proposal_notification_path(proposal_id: proposal.id)
|
||||
click_button "Send message"
|
||||
|
||||
expect(page).to have_content error_message
|
||||
end
|
||||
|
||||
context "Limits" do
|
||||
|
||||
scenario "Can only send a maximum number of direct messages per day" do
|
||||
sender = create(:user, :level_two)
|
||||
receiver = create(:user, :level_two)
|
||||
|
||||
3.times { create(:direct_message, sender: sender) }
|
||||
|
||||
login_as(sender)
|
||||
visit user_path(receiver)
|
||||
|
||||
click_link "Send private message"
|
||||
|
||||
expect(page).to have_content "Send private message to #{receiver.name}"
|
||||
|
||||
fill_in 'direct_message_title', with: "Hey!"
|
||||
fill_in 'direct_message_body', with: "How are you doing?"
|
||||
click_button "Send message"
|
||||
|
||||
expect(page).to have_content "You can only send a maximum of 3 direct messages per day"
|
||||
expect(page).to_not have_content "You message has been sent successfully."
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -148,4 +148,87 @@ feature 'Emails' do
|
||||
expect(email).to have_body_text(spending_proposal.feasible_explanation)
|
||||
end
|
||||
|
||||
context "Direct Message" do
|
||||
|
||||
scenario "Receiver email" do
|
||||
sender = create(:user, :level_two)
|
||||
receiver = create(:user, :level_two)
|
||||
|
||||
direct_message = create_direct_message(sender, receiver)
|
||||
|
||||
email = unread_emails_for(receiver.email).first
|
||||
|
||||
expect(email).to have_subject("You have received a new private message")
|
||||
expect(email).to have_body_text(direct_message.title)
|
||||
expect(email).to have_body_text(direct_message.body)
|
||||
expect(email).to have_body_text(direct_message.sender.name)
|
||||
expect(email).to have_body_text(/#{user_path(direct_message.sender_id)}/)
|
||||
end
|
||||
|
||||
scenario "Sender email" do
|
||||
sender = create(:user, :level_two)
|
||||
receiver = create(:user, :level_two)
|
||||
|
||||
direct_message = create_direct_message(sender, receiver)
|
||||
|
||||
email = unread_emails_for(sender.email).first
|
||||
|
||||
expect(email).to have_subject("You have send a new private message")
|
||||
expect(email).to have_body_text(direct_message.title)
|
||||
expect(email).to have_body_text(direct_message.body)
|
||||
expect(email).to have_body_text(direct_message.receiver.name)
|
||||
end
|
||||
|
||||
pending "In the copy sent to the sender, display the receiver's name"
|
||||
|
||||
end
|
||||
|
||||
context "Proposal notification digest" do
|
||||
|
||||
scenario "notifications for proposals that I have supported" do
|
||||
user = create(:user, email_digest: true)
|
||||
|
||||
proposal1 = create(:proposal)
|
||||
proposal2 = create(:proposal)
|
||||
proposal3 = create(:proposal)
|
||||
|
||||
create(:vote, votable: proposal1, voter: user)
|
||||
create(:vote, votable: proposal2, voter: user)
|
||||
|
||||
reset_mailer
|
||||
|
||||
notification1 = create_proposal_notification(proposal1)
|
||||
notification2 = create_proposal_notification(proposal2)
|
||||
notification3 = create_proposal_notification(proposal3)
|
||||
|
||||
email_digest = EmailDigest.new
|
||||
email_digest.create
|
||||
|
||||
email = open_last_email
|
||||
expect(email).to have_subject("Proposal notifications in Consul")
|
||||
expect(email).to deliver_to(user.email)
|
||||
|
||||
expect(email).to have_body_text(proposal1.title)
|
||||
expect(email).to have_body_text(notification1.notifiable.title)
|
||||
expect(email).to have_body_text(notification1.notifiable.body)
|
||||
expect(email).to have_body_text(proposal1.author.name)
|
||||
|
||||
expect(email).to have_body_text(/#{notification_path(notification1)}/)
|
||||
expect(email).to have_body_text(/#{proposal_path(proposal1, anchor: 'comments')}/)
|
||||
expect(email).to have_body_text(/#{proposal_path(proposal1, anchor: 'social-share')}/)
|
||||
|
||||
expect(email).to have_body_text(proposal2.title)
|
||||
expect(email).to have_body_text(notification2.notifiable.title)
|
||||
expect(email).to have_body_text(notification2.notifiable.body)
|
||||
expect(email).to have_body_text(/#{notification_path(notification2)}/)
|
||||
expect(email).to have_body_text(/#{proposal_path(proposal2, anchor: 'comments')}/)
|
||||
expect(email).to have_body_text(/#{proposal_path(proposal2, anchor: 'social-share')}/)
|
||||
expect(email).to have_body_text(proposal2.author.name)
|
||||
|
||||
expect(email).to_not have_body_text(proposal3.title)
|
||||
expect(email).to have_body_text(/#{account_path}/)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -149,6 +149,64 @@ feature "Notifications" do
|
||||
expect(page).to have_css ".notification", count: 0
|
||||
end
|
||||
|
||||
context "Proposal notification" do
|
||||
|
||||
scenario "Voters should receive a notification", :js do
|
||||
author = create(:user)
|
||||
|
||||
user1 = create(:user)
|
||||
user2 = create(:user)
|
||||
user3 = create(:user)
|
||||
|
||||
proposal = create(:proposal, author: author)
|
||||
|
||||
create(:vote, voter: user1, votable: proposal, vote_flag: true)
|
||||
create(:vote, voter: user2, votable: proposal, vote_flag: true)
|
||||
|
||||
login_as(author)
|
||||
visit root_path
|
||||
|
||||
visit new_proposal_notification_path(proposal_id: proposal.id)
|
||||
|
||||
fill_in 'proposal_notification_title', with: "Thank you for supporting my proposal"
|
||||
fill_in 'proposal_notification_body', with: "Please share it with others so we can make it happen!"
|
||||
click_button "Send message"
|
||||
|
||||
expect(page).to have_content "Your message has been sent correctly."
|
||||
|
||||
logout
|
||||
login_as user1
|
||||
visit root_path
|
||||
|
||||
find(".icon-notification").click
|
||||
|
||||
expect(page).to have_css ".notification", count: 1
|
||||
expect(page).to have_content "There is one new notification on #{proposal.title}"
|
||||
expect(page).to have_xpath "//a[@href='#{notification_path(Notification.last)}']"
|
||||
|
||||
logout
|
||||
login_as user2
|
||||
visit root_path
|
||||
|
||||
find(".icon-notification").click
|
||||
|
||||
expect(page).to have_css ".notification", count: 1
|
||||
expect(page).to have_content "There is one new notification on #{proposal.title}"
|
||||
expect(page).to have_xpath "//a[@href='#{notification_path(Notification.first)}']"
|
||||
|
||||
logout
|
||||
login_as user3
|
||||
visit root_path
|
||||
|
||||
find(".icon-no-notification").click
|
||||
|
||||
expect(page).to have_css ".notification", count: 0
|
||||
end
|
||||
|
||||
pending "group notifications for the same proposal"
|
||||
|
||||
end
|
||||
|
||||
context "mark as read" do
|
||||
|
||||
scenario "mark a single notification as read" do
|
||||
|
||||
109
spec/features/proposal_notifications_spec.rb
Normal file
109
spec/features/proposal_notifications_spec.rb
Normal file
@@ -0,0 +1,109 @@
|
||||
require 'rails_helper'
|
||||
|
||||
feature 'Proposal Notifications' do
|
||||
|
||||
scenario "Send a notification" do
|
||||
author = create(:user)
|
||||
proposal = create(:proposal, author: author)
|
||||
|
||||
login_as(author)
|
||||
visit root_path
|
||||
|
||||
click_link "My activity"
|
||||
|
||||
within("#proposal_#{proposal.id}") do
|
||||
click_link "Send notification"
|
||||
end
|
||||
|
||||
fill_in 'proposal_notification_title', with: "Thank you for supporting my proposal"
|
||||
fill_in 'proposal_notification_body', with: "Please share it with others so we can make it happen!"
|
||||
click_button "Send message"
|
||||
|
||||
expect(page).to have_content "Your message has been sent correctly."
|
||||
expect(page).to have_content "Thank you for supporting my proposal"
|
||||
expect(page).to have_content "Please share it with others so we can make it happen!"
|
||||
end
|
||||
|
||||
scenario "Show notifications" do
|
||||
proposal = create(:proposal)
|
||||
notification1 = create(:proposal_notification, proposal: proposal, title: "Hey guys", body: "Just wanted to let you know that...")
|
||||
notification2 = create(:proposal_notification, proposal: proposal, title: "Another update", body: "We are almost there please share with your peoples!")
|
||||
|
||||
visit proposal_path(proposal)
|
||||
|
||||
expect(page).to have_content "Hey guys"
|
||||
expect(page).to have_content "Just wanted to let you know that..."
|
||||
|
||||
expect(page).to have_content "Another update"
|
||||
expect(page).to have_content "We are almost there please share with your peoples!"
|
||||
end
|
||||
|
||||
scenario "Message about receivers" do
|
||||
author = create(:user)
|
||||
proposal = create(:proposal, author: author)
|
||||
|
||||
7.times { create(:vote, votable: proposal, vote_flag: true) }
|
||||
|
||||
login_as(author)
|
||||
visit new_proposal_notification_path(proposal_id: proposal.id)
|
||||
|
||||
expect(page).to have_content "This message will be send to 7 people and it will be visible in the proposal's page"
|
||||
expect(page).to have_link("the proposal's page", href: proposal_path(proposal, anchor: 'comments'))
|
||||
end
|
||||
|
||||
context "Permissions" do
|
||||
|
||||
scenario "Link to send the message" do
|
||||
user = create(:user)
|
||||
author = create(:user)
|
||||
proposal = create(:proposal, author: author)
|
||||
|
||||
login_as(author)
|
||||
visit user_path(author)
|
||||
|
||||
within("#proposal_#{proposal.id}") do
|
||||
expect(page).to have_link "Send notification"
|
||||
end
|
||||
|
||||
login_as(user)
|
||||
visit user_path(author)
|
||||
|
||||
within("#proposal_#{proposal.id}") do
|
||||
expect(page).to_not have_link "Send message"
|
||||
end
|
||||
end
|
||||
|
||||
scenario "Accessing form directly" do
|
||||
user = create(:user)
|
||||
author = create(:user)
|
||||
proposal = create(:proposal, author: author)
|
||||
|
||||
login_as(user)
|
||||
visit new_proposal_notification_path(proposal_id: proposal.id)
|
||||
|
||||
expect(current_path).to eq(proposals_path)
|
||||
expect(page).to have_content("You do not have permission to carry out the action")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
scenario "Error messages" do
|
||||
author = create(:user)
|
||||
proposal = create(:proposal, author: author)
|
||||
|
||||
login_as(author)
|
||||
|
||||
visit new_proposal_notification_path(proposal_id: proposal.id)
|
||||
click_button "Send message"
|
||||
|
||||
expect(page).to have_content error_message
|
||||
end
|
||||
|
||||
context "Limits" do
|
||||
|
||||
pending "Cannot send more than one notification within established interval"
|
||||
pending "use timecop to make sure notifications can be sent after time interval"
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
27
spec/helpers/application_helper_spec.rb
Normal file
27
spec/helpers/application_helper_spec.rb
Normal file
@@ -0,0 +1,27 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe ApplicationHelper do
|
||||
|
||||
describe "#author_of?" do
|
||||
it "should be true if user is the author" do
|
||||
user = create(:user)
|
||||
proposal = create(:proposal, author: user)
|
||||
expect(author_of?(proposal, user)).to eq true
|
||||
end
|
||||
|
||||
it "should be false if user is not the author" do
|
||||
user = create(:user)
|
||||
proposal = create(:proposal)
|
||||
expect(author_of?(proposal, user)).to eq false
|
||||
end
|
||||
|
||||
it "should be false if user or authorable is nil" do
|
||||
user = create(:user)
|
||||
proposal = create(:proposal)
|
||||
|
||||
expect(author_of?(nil, user)).to eq false
|
||||
expect(author_of?(proposal, nil)).to eq false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,25 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe NotificationsHelper do
|
||||
|
||||
describe "#notification_action" do
|
||||
let(:debate) { create :debate }
|
||||
let(:debate_comment) { create :comment, commentable: debate }
|
||||
|
||||
context "when action was comment on a debate" do
|
||||
it "returns correct text when someone comments on your debate" do
|
||||
notification = create :notification, notifiable: debate
|
||||
expect(notification_action(notification)).to eq "comments_on"
|
||||
end
|
||||
end
|
||||
|
||||
context "when action was comment on a debate" do
|
||||
it "returns correct text when someone replies to your comment" do
|
||||
notification = create :notification, notifiable: debate_comment
|
||||
expect(notification_action(notification)).to eq "replies_to"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
9
spec/lib/email_digests_spec.rb
Normal file
9
spec/lib/email_digests_spec.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe EmailDigest do
|
||||
|
||||
describe "create" do
|
||||
pending "only send unread notifications"
|
||||
end
|
||||
|
||||
end
|
||||
@@ -37,6 +37,10 @@ describe "Abilities::Common" do
|
||||
it { should_not be_able_to(:comment_as_administrator, proposal) }
|
||||
it { should_not be_able_to(:comment_as_moderator, proposal) }
|
||||
|
||||
it { should be_able_to(:new, DirectMessage) }
|
||||
it { should_not be_able_to(:create, DirectMessage) }
|
||||
it { should_not be_able_to(:show, DirectMessage) }
|
||||
|
||||
describe 'flagging content' do
|
||||
it { should be_able_to(:flag, debate) }
|
||||
it { should be_able_to(:unflag, debate) }
|
||||
@@ -85,6 +89,7 @@ describe "Abilities::Common" do
|
||||
|
||||
describe "when level 2 verified" do
|
||||
let(:own_spending_proposal) { create(:spending_proposal, author: user) }
|
||||
let(:own_direct_message) { create(:direct_message, sender: user) }
|
||||
before{ user.update(residence_verified_at: Time.now, confirmed_phone: "1") }
|
||||
|
||||
it { should be_able_to(:vote, Proposal) }
|
||||
@@ -93,10 +98,16 @@ describe "Abilities::Common" do
|
||||
it { should be_able_to(:create, SpendingProposal) }
|
||||
it { should_not be_able_to(:destroy, create(:spending_proposal)) }
|
||||
it { should_not be_able_to(:destroy, own_spending_proposal) }
|
||||
|
||||
it { should be_able_to(:new, DirectMessage) }
|
||||
it { should be_able_to(:create, DirectMessage) }
|
||||
it { should be_able_to(:show, own_direct_message) }
|
||||
it { should_not be_able_to(:show, create(:direct_message)) }
|
||||
end
|
||||
|
||||
describe "when level 3 verified" do
|
||||
let(:own_spending_proposal) { create(:spending_proposal, author: user) }
|
||||
let(:own_direct_message) { create(:direct_message, sender: user) }
|
||||
before{ user.update(verified_at: Time.now) }
|
||||
|
||||
it { should be_able_to(:vote, Proposal) }
|
||||
@@ -105,5 +116,10 @@ describe "Abilities::Common" do
|
||||
it { should be_able_to(:create, SpendingProposal) }
|
||||
it { should_not be_able_to(:destroy, create(:spending_proposal)) }
|
||||
it { should_not be_able_to(:destroy, own_spending_proposal) }
|
||||
|
||||
it { should be_able_to(:new, DirectMessage) }
|
||||
it { should be_able_to(:create, DirectMessage) }
|
||||
it { should be_able_to(:show, own_direct_message) }
|
||||
it { should_not be_able_to(:show, create(:direct_message)) }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -26,4 +26,6 @@ describe "Abilities::Everyone" do
|
||||
|
||||
it { should be_able_to(:index, SpendingProposal) }
|
||||
it { should_not be_able_to(:create, SpendingProposal) }
|
||||
end
|
||||
|
||||
pending "only authors can access new and create for ProposalNotifications"
|
||||
end
|
||||
85
spec/models/direct_message_spec.rb
Normal file
85
spec/models/direct_message_spec.rb
Normal file
@@ -0,0 +1,85 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe DirectMessage do
|
||||
|
||||
let(:direct_message) { build(:direct_message) }
|
||||
|
||||
before(:each) do
|
||||
Setting[:direct_message_max_per_day] = 3
|
||||
end
|
||||
|
||||
it "should be valid" do
|
||||
expect(direct_message).to be_valid
|
||||
end
|
||||
|
||||
it "should not be valid without a title" do
|
||||
direct_message.title = nil
|
||||
expect(direct_message).to_not be_valid
|
||||
end
|
||||
|
||||
it "should not be valid without a body" do
|
||||
direct_message.body = nil
|
||||
expect(direct_message).to_not be_valid
|
||||
end
|
||||
|
||||
it "should not be valid without an associated sender" do
|
||||
direct_message.sender = nil
|
||||
expect(direct_message).to_not be_valid
|
||||
end
|
||||
|
||||
it "should not be valid without an associated receiver" do
|
||||
direct_message.receiver = nil
|
||||
expect(direct_message).to_not be_valid
|
||||
end
|
||||
|
||||
describe "maximum number of direct messages per day" do
|
||||
|
||||
it "should not be valid if above maximum" do
|
||||
sender = create(:user)
|
||||
direct_message1 = create(:direct_message, sender: sender)
|
||||
direct_message2 = create(:direct_message, sender: sender)
|
||||
direct_message3 = create(:direct_message, sender: sender)
|
||||
|
||||
direct_message4 = build(:direct_message, sender: sender)
|
||||
expect(direct_message4).to_not be_valid
|
||||
end
|
||||
|
||||
it "should be valid if below maximum" do
|
||||
sender = create(:user)
|
||||
direct_message1 = create(:direct_message, sender: sender)
|
||||
direct_message2 = create(:direct_message, sender: sender)
|
||||
|
||||
direct_message3 = build(:direct_message)
|
||||
expect(direct_message).to be_valid
|
||||
end
|
||||
|
||||
it "should be valid if no direct_messages sent" do
|
||||
direct_message = build(:direct_message)
|
||||
|
||||
expect(direct_message).to be_valid
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "scopes" do
|
||||
|
||||
describe "today" do
|
||||
it "should return direct messages created today" do
|
||||
direct_message1 = create(:direct_message, created_at: Time.zone.now.beginning_of_day + 3.hours)
|
||||
direct_message2 = create(:direct_message, created_at: Time.zone.now)
|
||||
direct_message3 = create(:direct_message, created_at: Time.zone.now.end_of_day)
|
||||
|
||||
expect(DirectMessage.today.count).to eq 3
|
||||
end
|
||||
|
||||
it "should not return direct messages created another day" do
|
||||
direct_message1 = create(:direct_message, created_at: 1.day.ago)
|
||||
direct_message2 = create(:direct_message, created_at: 1.day.from_now)
|
||||
|
||||
expect(DirectMessage.today.count).to eq 0
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -47,4 +47,35 @@ describe Notification do
|
||||
end
|
||||
end
|
||||
|
||||
describe "#notification_action" do
|
||||
|
||||
context "when action was comment on a debate" do
|
||||
it "returns correct text when someone comments on your debate" do
|
||||
debate = create(:debate)
|
||||
notification = create :notification, notifiable: debate
|
||||
|
||||
expect(notification.notifiable_action).to eq "comments_on"
|
||||
end
|
||||
end
|
||||
|
||||
context "when action was comment on a debate" do
|
||||
it "returns correct text when someone replies to your comment" do
|
||||
debate = create(:debate)
|
||||
debate_comment = create :comment, commentable: debate
|
||||
notification = create :notification, notifiable: debate_comment
|
||||
|
||||
expect(notification.notifiable_action).to eq "replies_to"
|
||||
end
|
||||
end
|
||||
|
||||
context "when action was proposal notification" do
|
||||
it "returns correct text when the author created a proposal notification" do
|
||||
proposal_notification = create(:proposal_notification)
|
||||
notification = create :notification, notifiable: proposal_notification
|
||||
|
||||
expect(notification.notifiable_action).to eq "proposal_notification"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
59
spec/models/proposal_notification_spec.rb
Normal file
59
spec/models/proposal_notification_spec.rb
Normal file
@@ -0,0 +1,59 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe ProposalNotification do
|
||||
let(:notification) { build(:proposal_notification) }
|
||||
|
||||
it "should be valid" do
|
||||
expect(notification).to be_valid
|
||||
end
|
||||
|
||||
it "should not be valid without a title" do
|
||||
notification.title = nil
|
||||
expect(notification).to_not be_valid
|
||||
end
|
||||
|
||||
it "should not be valid without a body" do
|
||||
notification.body = nil
|
||||
expect(notification).to_not be_valid
|
||||
end
|
||||
|
||||
it "should not be valid without an associated proposal" do
|
||||
notification.proposal = nil
|
||||
expect(notification).to_not be_valid
|
||||
end
|
||||
|
||||
describe "minimum interval between notifications" do
|
||||
|
||||
before(:each) do
|
||||
Setting[:proposal_notification_minimum_interval_in_days] = 3
|
||||
end
|
||||
|
||||
it "should not be valid if below minium interval" do
|
||||
proposal = create(:proposal)
|
||||
|
||||
notification1 = create(:proposal_notification, proposal: proposal)
|
||||
notification2 = build(:proposal_notification, proposal: proposal)
|
||||
|
||||
proposal.reload
|
||||
expect(notification2).to_not be_valid
|
||||
end
|
||||
|
||||
it "should be valid if notifications above minium interval" do
|
||||
proposal = create(:proposal)
|
||||
|
||||
notification1 = create(:proposal_notification, proposal: proposal, created_at: 4.days.ago)
|
||||
notification2 = build(:proposal_notification, proposal: proposal)
|
||||
|
||||
proposal.reload
|
||||
expect(notification2).to be_valid
|
||||
end
|
||||
|
||||
it "should be valid if no notifications sent" do
|
||||
notification1 = build(:proposal_notification)
|
||||
|
||||
expect(notification1).to be_valid
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -83,6 +83,18 @@ describe User do
|
||||
expect(subject.newsletter).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'email_digest' do
|
||||
it 'should be true by default' do
|
||||
expect(subject.email_digest).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'email_on_direct_message' do
|
||||
it 'should be true by default' do
|
||||
expect(subject.email_on_direct_message).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "administrator?" do
|
||||
|
||||
@@ -196,4 +196,39 @@ module CommonActions
|
||||
tag_cloud.tags.map(&:name)
|
||||
end
|
||||
|
||||
def create_proposal_notification(proposal)
|
||||
login_as(proposal.author)
|
||||
visit root_path
|
||||
|
||||
click_link "My activity"
|
||||
|
||||
within("#proposal_#{proposal.id}") do
|
||||
click_link "Send notification"
|
||||
end
|
||||
|
||||
fill_in 'proposal_notification_title', with: "Thank you for supporting my proposal #{proposal.title}"
|
||||
fill_in 'proposal_notification_body', with: "Please share it with others so we can make it happen! #{proposal.summary}"
|
||||
click_button "Send message"
|
||||
|
||||
expect(page).to have_content "Your message has been sent correctly."
|
||||
Notification.last
|
||||
end
|
||||
|
||||
def create_direct_message(sender, receiver)
|
||||
login_as(sender)
|
||||
visit user_path(receiver)
|
||||
|
||||
click_link "Send private message"
|
||||
|
||||
expect(page).to have_content "Send private message to #{receiver.name}"
|
||||
|
||||
fill_in 'direct_message_title', with: "Hey #{receiver.name}!"
|
||||
fill_in 'direct_message_body', with: "How are you doing? This is #{sender.name}"
|
||||
|
||||
click_button "Send message"
|
||||
|
||||
expect(page).to have_content "You message has been sent successfully."
|
||||
DirectMessage.last
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user