Skip to content

Commit e82fe97

Browse files
authored
Merge pull request #1495 from scott/dev/2.7/prepare-2.7.0
Dev/2.7/prepare 2.7.0
2 parents 197a2a6 + 47acfa6 commit e82fe97

34 files changed

+159
-29
lines changed

CHANGELOG.md

+44
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,47 @@
1+
## VERSION 2.7.0
2+
3+
The 2.7 release of Helpy is here, with several great new features to help you b etter provide great customer support.
4+
5+
New Features:
6+
7+
- New nested categories feature lets you create sub categories with a drag and drop UI to group and administer them.
8+
- New API setting for default reply type
9+
- Site URL is now validated
10+
- New Anti-spam setting to filter any ticket from matching email addresses to spam
11+
- Address a ticket is sent to is now captured
12+
13+
Bug Fixes:
14+
15+
- Webhooks preference did not always save
16+
- Customers list view does not always load
17+
18+
See UPDATING.md for details on how to update.
19+
20+
## VERSION 2.6.0
21+
22+
This release fixes several edge case bugs and adds a couple new API features and an all new Ukrainian language translation. Specifically:
23+
24+
- New Ukrainian locale #1378 @Serg2294
25+
26+
Bug Fixes:
27+
28+
- Translated versions are now properly included in the search index #1375
29+
- The CC address is now persisted if you change the assignment of a ticket while editing the reply #1387
30+
- Agents can now create an unassigned ticket #1317
31+
- A rare bug that docs unclickable on mobile has been resolved #1371
32+
33+
API:
34+
35+
- A new endpoint has been added to get specifics of the currently logged in user #1242 @trevormerritt
36+
- Categories can now be filtered by visibility (public/private/internal) #1269
37+
- Forum topics now respect a limit param #1357 @elguitar
38+
- The User entity is now included with topics and posts #1318
39+
40+
Misc:
41+
42+
- Can now specify a theme environment var when running test suite #1253 @azul
43+
44+
145
## VERSION 2.5.0
246

347
This release adds important new functionality for managing incoming spam email tickets (particularly from parse webhook services such as Sendgrid or Mandrill).

app/controllers/admin/dashboard_controller.rb

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ def index
1111
redirect_to admin_topics_path
1212
elsif current_user.is_editor? && knowledgebase?
1313
redirect_to admin_categories_path
14+
elsif (current_user.is_admin? || current_user.is_agent?)
15+
redirect_to admin_users_path
1416
else
15-
redirect_to root_url
17+
redirect_to admin_blank_dashboard_path
1618
end
1719
end
1820

app/controllers/application_controller.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ def url_options
1818

1919
def after_sign_in_path_for(_resource)
2020
# If the user is an agent, redirect to admin panel
21-
redirect_url = current_user.is_agent? ? admin_root_url : root_url
22-
oauth_url = current_user.is_agent? ? admin_root_url : request.env['omniauth.origin']
21+
redirect_url = current_user.is_editor? ? admin_root_url : root_url
22+
oauth_url = current_user.is_editor? ? admin_root_url : request.env['omniauth.origin']
2323
oauth_url || redirect_url
2424
end
2525

app/controllers/home_controller.rb

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ class HomeController < ApplicationController
44
respond_to :html
55

66
def index
7+
redirect_to new_user_session_path if !tickets? && !knowledgebase?
8+
79
@topics = Topic.by_popularity.ispublic.front
810
@rss = Topic.chronologic.active
911
@articles = Doc.all_public_popular.with_translations(I18n.locale).includes(:tags)

app/helpers/admin/topics_helper.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def ticket_status_label
4141

4242
def new_ticket_button
4343
content_tag :span, class: 'hidden-xs pull-right' do
44-
link_to t(:open_new_discussion, default: 'Open Discussion'), new_admin_topic_path, remote: true, class: 'btn btn-primary'
44+
link_to t(:open_new_discussion, default: 'Open Discussion'), new_admin_topic_path, remote: true, class: 'btn btn-primary' if tickets?
4545
end
4646
end
4747

app/helpers/admin/users_helper.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def user_header
99

1010
def new_user_ticket_button
1111
content_tag :span, class: "hidden-xs pull-right" do
12-
link_to t(:open_new_discussion, default: 'Open Discussion'), new_admin_topic_path(user_id: @user.id), remote: true, class: 'btn btn-primary'
12+
link_to t(:open_new_discussion, default: 'Open Discussion'), new_admin_topic_path(user_id: @user.id), remote: true, class: 'btn btn-primary' if tickets?
1313
end
1414
end
1515

app/models/entity/post.rb

+1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ class Post < Base
1313
expose :cc, documentation: { type: "String", desc: "Comma separated list of emails to CC" }
1414
expose :bcc, documentation: { type: "String", desc: "Comma separated list of emails to BCC" }
1515
expose :raw_email, documentation: { desc: "The original full raw email body" }
16+
expose :email_to_address, documentation: { desc: "The address a ticket was sent to" }
1617
end
1718
end

app/models/topic.rb

+8
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ class Topic < ActiveRecord::Base
8282
# may want to get rid of this filter:
8383
# before_save :check_for_private
8484
before_create :add_locale
85+
before_create :reject_blacklisted_email_addresses
8586

8687
before_save :cache_user_name
8788
acts_as_taggable_on :tags, :teams
@@ -277,6 +278,13 @@ def posts_in_last_minute
277278

278279
private
279280

281+
# Send any tickets created by a blacklisted email to spam
282+
def reject_blacklisted_email_addresses
283+
if AppSettings['email.email_blacklist'].split(",").any? { |s| self.user.email.downcase.include?(s.downcase) }
284+
self.current_status = "spam"
285+
end
286+
end
287+
280288
def cache_user_name
281289
return if self.user.nil?
282290
if self.user.name.present?

app/models/user.rb

-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ class User < ActiveRecord::Base
7979
validates :name, presence: true
8080
validates :email, presence: true
8181

82-
8382
include Gravtastic
8483
mount_uploader :profile_image, ProfileImageUploader
8584

app/views/admin/dashboard/blank.html.erb

Whitespace-only changes.

app/views/admin/settings/email.html.erb

+3-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@
2424
</div>
2525
</div>
2626
<div class="settings-section spam-protection" data-hook="spam_protection">
27-
<%= f.text_field 'email.spam_assassin_reject', label: 'Reject incoming email SpamAssassin threshold', value: AppSettings['email.spam_assassin_reject'] %>
28-
<%= f.text_field 'email.spam_assassin_filter', label: 'Filter to Spam incoming email SA threshold ', value: AppSettings['email.spam_assassin_filter'] %>
27+
<%= f.text_field 'email.spam_assassin_reject', label: t('spam_assassin_reject'), value: AppSettings['email.spam_assassin_reject'] %>
28+
<%= f.text_field 'email.spam_assassin_filter', label: t('spam_assassin_filter'), value: AppSettings['email.spam_assassin_filter'] %>
29+
<%= f.text_area 'email.email_blacklist', label: t('email_blacklist'), value: AppSettings['email.email_blacklist'] %>
2930
</div>
3031
<div class="submit-section">
3132
<%= f.submit "Save Settings", class: 'btn btn-warning' %>

app/views/admin/topics/_ticket_nav_dropdown.html.erb

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
<% end %>
4444
</li>
4545
<li class="mailbox tiny-header">
46-
<%= link_to t(:open_new_discussion, default: "Create New Ticket"), new_admin_topic_path, remote: true, class: 'btn btn-default' %>
46+
<%= link_to t(:open_new_discussion, default: "Create New Ticket"), new_admin_topic_path, remote: true, class: 'btn btn-default' if tickets? %>
4747
</li>
4848
</ul>
4949
</div>

app/views/admin/topics/_tickets.html.erb

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
data: { confirm: t(:empty_trash_warning, default: "Are you sure you want to empty the trash? This will permanently delete all tickets in the trash.") },
1010
class: 'btn btn-default', remote: true if params[:status] == 'trash' && current_user.is_admin? %>
1111
<%= link_to t(:open_new_discussion, default: "Open Discussion"), new_admin_topic_path,
12-
remote: true, class: 'btn btn-primary' %>
12+
remote: true, class: 'btn btn-primary' if tickets? %>
1313
</div>
1414
<% end %>
1515
<% if @topics.present? %>

app/views/admin/topics/_topic_options.html.erb

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<% if @topic.user.present? %>
1111
<li class="dropdown-header"><%= @topic.user.name %></li>
1212
<li><%= link_to t(:tickets, default: 'Tickets'), remote: true %></li>
13-
<li><%= link_to t(:open_new_discussion, default: 'Open Discussion'), new_admin_topic_path(user_id: @topic.user.id), remote: true, class: '' %></li>
13+
<li><%= link_to t(:open_new_discussion, default: 'Open Discussion'), new_admin_topic_path(user_id: @topic.user.id), remote: true, class: '' if tickets? %></li>
1414
<li><%= link_to t(:edit_user, default: 'Edit User'), edit_admin_user_path(@topic.user), remote: false if @topic.user.can_be_edited? current_user %></li>
1515
<% end %>
1616
</ul>

app/views/admin/users/_tickets.html.erb

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
</div>
77
<div class="pull-right nav-new-button" style="margin-top:18px;">
88
<%= link_to t(:open_new_discussion, default: "Open Discussion"), new_admin_topic_path(user_id: @user.id),
9-
remote: true, class: 'btn btn-primary' %>
9+
remote: true, class: 'btn btn-primary' if tickets? %>
1010
</div>
1111

1212
</div>

app/views/admin/users/_user.html.erb

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
<div class="btn-group">
2323
<span id="row-<%= user.id %>" data-toggle="dropdown" aria-expanded="false" class='btn dropdown-toggle fas fa-ellipsis-v'></span>
2424
<ul class="dropdown-menu dropdown-menu-right" role="menu">
25-
<li><%= link_to t(:open_new_discussion, default: 'Open Discussion'), new_admin_topic_path(user_id: user.id), remote: true, class: '' %></li>
25+
<li><%= link_to t(:open_new_discussion, default: 'Open Discussion'), new_admin_topic_path(user_id: user.id), remote: true, class: '' if tickets? %></li>
2626
<li><%= link_to t(:discussions, default: 'Discussions'), admin_user_path(user), remote: true %></li>
2727
<li><%= link_to t(:edit_user, default: 'Edit User'), edit_admin_user_path(user, mode: 'edit'), remote: false if user.can_be_edited? current_user%></li>
2828
</ul>

app/views/admin/users/_user_context_menu.html.erb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<span class="btn-group left-col-dropdown" style="margin-top:18px;">
22
<%= link_to content_tag(:small, '', class: 'fas fa-ellipsis-v ticket-ellipsis btn'), '#', class: 'dropdown-toggle', data: { toggle: 'dropdown' }%>
33
<ul class="dropdown-menu dropdown-menu-right" role="menu">
4-
<li><%= link_to t(:open_new_discussion, default: 'Open Discussion'), new_admin_topic_path(user_id: user.id), remote: true, class: '' %></li>
4+
<li><%= link_to t(:open_new_discussion, default: 'Open Discussion'), new_admin_topic_path(user_id: user.id), remote: true, class: '' if tickets? %></li>
55
<li><%= link_to t(:edit_user, default: 'Edit User'), edit_admin_user_path(user), remote: false if user.can_be_edited? current_user %></li>
66
<li><%= link_to t(:scrub_user, default: 'Anonymize User'), admin_scrub_user_path(user), remote: true,
77
method: :post,

app/views/admin/users/_user_info.html.erb

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<span class="btn-group left-col-dropdown">
77
<%= link_to content_tag(:small, '', class: 'fas fa-ellipsis-v ticket-ellipsis btn'), '#', class: 'dropdown-toggle', data: { toggle: 'dropdown' }%>
88
<ul class="dropdown-menu dropdown-menu-right" role="menu">
9-
<li><%= link_to t(:open_new_discussion, default: 'Open Discussion'), new_admin_topic_path(user_id: @user.id), remote: true, class: '' %></li>
9+
<li><%= link_to t(:open_new_discussion, default: 'Open Discussion'), new_admin_topic_path(user_id: @user.id), remote: true, class: '' if tickets? %></li>
1010
<li><%= link_to t(:edit_user, default: 'Edit User'), edit_admin_user_path(@user), remote: false %></li>
1111
<li><%= link_to t(:scrub_user, default: 'Anonymize User'), admin_scrub_user_path(@user), remote: true,
1212
method: :post,

app/views/layouts/_admin_header.html.erb

+2-2
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ a.navbar-brand {
7878
<div class="navbar-collapse collapse">
7979
<ul class="nav navbar-nav navbar-right pull-right hidden-xs" data-hook='admin-nav-right'>
8080
<%= content_tag(:li, '' , class: "visible-lg visible-md visible-sm hidden-xs click-loader new-discussion #{new_active_class}") do %>
81-
<%= navbar_expanding_link(new_admin_topic_path, "fas fa-plus", t(:new_ticket, default: "New Ticket"), "", (params[:controller] == 'admin/topics')) %>
81+
<%= navbar_expanding_link(new_admin_topic_path, "fas fa-plus", t(:new_ticket, default: "New Ticket"), "", (params[:controller] == 'admin/topics')) if tickets? %>
8282
<% end if current_user.is_agent? %>
8383
<%= help_menu %>
8484
<%= admin_avatar_menu %>
@@ -90,7 +90,7 @@ a.navbar-brand {
9090
<%= helpcenter_menu_or_item %>
9191
<%= content_tag(:li, link_to(t(:content, default: "Content"), admin_categories_path), class:'kblink') if knowledgebase? && current_user.role == 'editor' %>
9292
<%#= content_tag(:li, link_to(t(:app_store, default: "App Store"), "http://helpy.io/store/"), class: "hidden-sm hidden-xs") if current_user.is_agent? %>
93-
<%= content_tag(:li, link_to(t(:open_new_discussion, default: "New Ticket"), new_admin_topic_path), class: 'visible-xs hidden-lg hidden-md hidden-sm') if current_user.is_agent? %>
93+
<%= content_tag(:li, link_to(t(:open_new_discussion, default: "New Ticket"), new_admin_topic_path), class: 'visible-xs hidden-lg hidden-md hidden-sm') if current_user.is_agent? && tickets? %>
9494
<%= content_tag(:li, link_to(t(:settings, default: "Settings"), admin_settings_path), class: 'visible-xs hidden-lg hidden-md hidden-sm') if current_user.is_admin? %>
9595
<%= content_tag(:li, link_to(t(:get_help, default: "Get Help"), "http://support.helpy.io/"), class: 'visible-xs hidden-lg hidden-md hidden-sm', target: 'blank') %>
9696
</ul>

config/environment.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
Rails.application.initialize!
66

77
# Get the current tag version
8-
VERSION = '2.6.0'
8+
VERSION = '2.7.0'
99
REVISION = `git log --pretty=format:'%h' -n 1`
1010
APP_VERSION = "#{VERSION}:#{REVISION}"

config/initializers/default_settings.rb

+1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
AppSettings.defaults["email.mail_domain"]= Settings.mail_domain
8787
AppSettings.defaults["email.spam_assassin_reject"]= 4
8888
AppSettings.defaults["email.spam_assassin_filter"]= 2
89+
AppSettings.defaults["email.email_blacklist"] = ""
8990

9091
# notifications
9192

config/locales/de.yml

+3
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,9 @@ de:
444444
# mail_smtp: SMTP Server
445445
# mail_port: SMTP Port
446446
# mail_domain: SMTP Domain
447+
# spam_assassin_reject: SpamAssassin Threshold for rejecting incoming email tickets
448+
# spam_assassin_filter: SpamAssassin Threshold for marking incoming tickets spam
449+
# email_blacklist: Automatically mark tickets from these addresses as spam (separate multiple with commas)
447450

448451
# API Key Managmeent
449452
api_keys: "API Keys"

config/locales/en.yml

+3
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,9 @@ en:
452452
mail_smtp: SMTP Server
453453
mail_port: SMTP Port
454454
mail_domain: SMTP Domain
455+
spam_assassin_reject: SpamAssassin Threshold for rejecting incoming email tickets
456+
spam_assassin_filter: SpamAssassin Threshold for marking incoming tickets spam
457+
email_blacklist: Automatically mark tickets from these addresses as spam (separate multiple with commas)
455458

456459
# API Key Managmeent
457460
api_keys: "API Keys"

config/locales/es.yml

+4-1
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,10 @@ es:
444444
# mail_smtp: SMTP Server
445445
# mail_port: SMTP Port
446446
# mail_domain: SMTP Domain
447-
447+
# spam_assassin_reject: SpamAssassin Threshold for rejecting incoming email tickets
448+
# spam_assassin_filter: SpamAssassin Threshold for marking incoming tickets spam
449+
# email_blacklist: Automatically mark tickets from these addresses as spam (separate multiple with commas)
450+
448451
# API Key Managmeent
449452
# api_keys: "API Keys"
450453
# key_name: "Key Name"

config/locales/fr.yml

+3
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,9 @@ fr:
446446
mail_smtp: "Serveur SMTP"
447447
mail_port: "Port SMTP"
448448
mail_domain: "Nom de domaine SMTP"
449+
# spam_assassin_reject: SpamAssassin Threshold for rejecting incoming email tickets
450+
# spam_assassin_filter: SpamAssassin Threshold for marking incoming tickets spam
451+
# email_blacklist: Automatically mark tickets from these addresses as spam (separate multiple with commas)
449452

450453
# API Key Managmeent
451454
api_keys: "Clés API"

config/locales/pt_BR.yml

+3
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,9 @@ pt-br:
449449
mail_smtp: "Servidor SMTP"
450450
mail_port: "Porta SMTP"
451451
mail_domain: "Domínio SMTP"
452+
# spam_assassin_reject: SpamAssassin Threshold for rejecting incoming email tickets
453+
# spam_assassin_filter: SpamAssassin Threshold for marking incoming tickets spam
454+
# email_blacklist: Automatically mark tickets from these addresses as spam (separate multiple with commas)
452455

453456
# API Keys
454457
api_keys: "Chaves da API"

config/routes.rb

+2
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@
176176
get '/reports/groups' => 'reports#groups', as: :group_reports
177177
get '/reports' => 'reports#index', as: :reports
178178

179+
get '/dashboard/blank' => 'dashboard#blank', as: :blank_dashboard
180+
179181
root to: 'dashboard#index'
180182
end
181183

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class AddEmailSentToPosts < ActiveRecord::Migration
2+
def change
3+
add_column :posts, :email_to_address, :string, default: ''
4+
end
5+
end

db/schema.rb

+7-6
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#
1212
# It's strongly recommended that you check this file into your version control system.
1313

14-
ActiveRecord::Schema.define(version: 20190716202013) do
14+
ActiveRecord::Schema.define(version: 20191005134018) do
1515

1616
# These are extensions that must be enabled in order to support this database
1717
enable_extension "plpgsql"
@@ -188,14 +188,15 @@
188188
t.integer "user_id"
189189
t.text "body"
190190
t.string "kind"
191-
t.boolean "active", default: true
192-
t.datetime "created_at", null: false
193-
t.datetime "updated_at", null: false
194-
t.integer "points", default: 0
195-
t.string "attachments", default: [], array: true
191+
t.boolean "active", default: true
192+
t.datetime "created_at", null: false
193+
t.datetime "updated_at", null: false
194+
t.integer "points", default: 0
195+
t.string "attachments", default: [], array: true
196196
t.string "cc"
197197
t.string "bcc"
198198
t.text "raw_email"
199+
t.string "email_to_address", default: ""
199200
end
200201

201202
create_table "searches", force: :cascade do |t|

lib/email_processor.rb

+6-3
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ def self.create_new_ticket_from_email(email, email_address, email_name, subject,
118118
raw_email: raw,
119119
user_id: @user.id,
120120
kind: "first",
121-
cc: cc
121+
cc: cc,
122+
email_to_address: to
122123
)
123124
# Push array of attachments and send to Cloudinary
124125
EmailProcessor.handle_attachments(email, post)
@@ -167,7 +168,8 @@ def self.create_forwarded_message_from_email(email, subject, raw, message, token
167168
raw_email: raw,
168169
user_id: @user.id,
169170
kind: 'first',
170-
cc: cc
171+
cc: cc,
172+
email_to_address: to
171173
)
172174

173175
# Push array of attachments and send to Cloudinary
@@ -204,7 +206,8 @@ def self.create_reply_from_email(email, email_address, email_name, subject, raw,
204206
raw_email: raw,
205207
user_id: @user.id,
206208
kind: "reply",
207-
cc: cc
209+
cc: cc,
210+
email_to_address: to
208211
)
209212

210213
# Push array of attachments and send to Cloudinary

test/factories/factories.rb

+8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@
1010
spam_report { '' }
1111
end
1212

13+
factory :blacklist_email, class: OpenStruct do
14+
to { [{ full: '[email protected]', email: '[email protected]', token: 'to_user', host: 'email.com', name: nil }] }
15+
from { ({ token: 'blacklist', host: 'email.com', email: '[email protected]', full: 'blacklist <[email protected]>', name: 'blacklist User' }) }
16+
subject { 'spam email subject' }
17+
header {}
18+
body { 'Spam' }
19+
end
20+
1321
factory :spam_from_unknown, class: OpenStruct do
1422
to { [{ full: '[email protected]', email: '[email protected]', token: 'to_user', host: 'email.com', name: nil }] }
1523
from { ({ token: 'spam_user', host: 'email.com', email: '[email protected]', full: 'spammer <[email protected]>', name: 'Spam User' }) }

test/integration/sign_in_flow_test.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def sign_out
4242

4343
test "an editor should be able to sign in and be shown the categories page" do
4444
sign_in("[email protected]")
45-
assert_equal '/en', current_path
45+
assert_equal '/admin/categories', current_path
4646
sign_out
4747
end
4848

0 commit comments

Comments
 (0)