Merge branch 'master' into dev/2.8/prepare-2.8.0

This commit is contained in:
scott
2020-02-04 13:49:55 -07:00
32 changed files with 469 additions and 141 deletions

View File

@@ -9,24 +9,24 @@ AllCops:
TargetRubyVersion: 2.3
DisabledByDefault: true
Rails:
Enabled: true
# Rails:
# Enabled: true
# TODO: Resolve the following RuboCop violations
# Offense count: 1
# Configuration parameters: Include.
# Include: app/models/**/*.rb
Rails/HasAndBelongsToMany:
Exclude:
- 'app/models/user.rb'
# Rails/HasAndBelongsToMany:
# Exclude:
# - 'app/models/user.rb'
# Offense count: 2
# Configuration parameters: Include.
# Include: app/**/*.rb, config/**/*.rb, db/**/*.rb, lib/**/*.rb
Rails/Output:
Exclude:
- 'config/unicorn.rb'
# Rails/Output:
# Exclude:
# - 'config/unicorn.rb'
# Offense count: 1
# Configuration parameters: AllowSafeAssignment.
@@ -35,7 +35,7 @@ Lint/AssignmentInCondition:
- 'bin/spring'
# Offense count: 2
Lint/HandleExceptions:
Lint/SuppressedException:
Exclude:
- 'bin/rails'
- 'bin/rake'

View File

@@ -11,7 +11,7 @@ GIT
GIT
remote: https://github.com/helpyio/helpy_onboarding
revision: dd7c96a7b9638ce089f5fac2213e15ffde2ae67b
revision: bb75cec5a680fdc919115b8d162be6c7863f6394
branch: master
specs:
helpy_onboarding (2.0.1)
@@ -72,14 +72,10 @@ GEM
attachinary (1.3.1)
cloudinary (~> 1.1.0)
rails (>= 3.2)
autoprefixer-rails (9.7.2)
autoprefixer-rails (9.7.4)
execjs
awesome_print (1.8.0)
aws_cf_signer (0.1.3)
axiom-types (0.1.1)
descendants_tracker (~> 0.0.4)
ice_nine (~> 0.11.0)
thread_safe (~> 0.3, >= 0.3.1)
bcrypt (3.1.13)
best_in_place (3.1.1)
actionpack (>= 3.2)
@@ -88,7 +84,7 @@ GEM
coderay (>= 1.0.0)
erubi (>= 1.0.0)
rack (>= 0.9.0)
bootstrap-datepicker-rails (1.8.0.1)
bootstrap-datepicker-rails (1.9.0.1)
railties (>= 3.0)
bootstrap-sass (3.4.1)
autoprefixer-rails (>= 5.2.1)
@@ -97,7 +93,7 @@ GEM
bootstrap-switch-rails (3.3.3)
bootstrap_form (2.7.0)
brakeman (4.7.2)
builder (3.2.3)
builder (3.2.4)
bulk_insert (1.7.0)
activerecord (>= 3.2.0)
bundler-audit (0.6.1)
@@ -120,7 +116,7 @@ GEM
mime-types (>= 1.16)
celluloid (0.16.0)
timers (~> 4.0.0)
chartkick (3.3.0)
chartkick (3.3.1)
childprocess (3.0.0)
chromedriver-helper (2.1.1)
archive-zip (~> 0.10)
@@ -140,8 +136,6 @@ GEM
codemirror-rails (5.16.0)
railties (>= 3.0, < 6.0)
coderay (1.1.2)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
coffee-rails (4.2.2)
coffee-script (>= 2.2.0)
railties (>= 4.0.0)
@@ -154,8 +148,8 @@ GEM
config (1.1.1)
activesupport (>= 3.0)
deep_merge (~> 1.0.1)
crass (1.0.5)
css_parser (1.7.0)
crass (1.0.6)
css_parser (1.7.1)
addressable
daemons (1.3.1)
debug_inspector (0.0.3)
@@ -165,8 +159,6 @@ GEM
polyglot
rails (>= 4.1)
rainbow (>= 2.1.0)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
devise (4.7.1)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
@@ -174,19 +166,39 @@ GEM
responders
warden (~> 1.2.3)
devise-bootstrap-views (1.1.0)
devise-i18n (1.8.2)
devise (>= 4.6)
devise-i18n (1.9.0)
devise (>= 4.7.1)
devise_invitable (1.7.5)
actionmailer (>= 4.1.0)
devise (>= 4.0.0)
docile (1.3.2)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
dry-configurable (0.9.0)
concurrent-ruby (~> 1.0)
dry-core (~> 0.4, >= 0.4.7)
dry-container (0.7.2)
concurrent-ruby (~> 1.0)
dry-configurable (~> 0.1, >= 0.1.3)
dry-core (0.4.9)
concurrent-ruby (~> 1.0)
dry-equalizer (0.3.0)
dry-inflector (0.2.0)
dry-logic (1.0.5)
concurrent-ruby (~> 1.0)
dry-core (~> 0.2)
dry-equalizer (~> 0.2)
dry-types (1.2.2)
concurrent-ruby (~> 1.0)
dry-container (~> 0.3)
dry-core (~> 0.4, >= 0.4.4)
dry-equalizer (~> 0.3)
dry-inflector (~> 0.1, >= 0.1.2)
dry-logic (~> 1.0, >= 1.0.2)
email_reply_trimmer (0.1.12)
equalizer (0.0.11)
erubi (1.9.0)
erubis (2.7.0)
excon (0.69.1)
excon (0.72.0)
execjs (2.7.0)
factory_bot (5.1.1)
activesupport (>= 4.2.0)
@@ -195,17 +207,17 @@ GEM
railties (>= 4.2.0)
faker (2.2.1)
i18n (>= 0.8)
faraday (0.17.0)
faraday (1.0.0)
multipart-post (>= 1.2, < 3)
ffi (1.11.3)
ffi (1.12.2)
fog-aws (3.5.2)
fog-core (~> 2.1)
fog-json (~> 1.1)
fog-xml (~> 0.1)
ipaddress (~> 0.8)
fog-core (2.1.2)
fog-core (2.2.0)
builder
excon (~> 0.58)
excon (~> 0.71)
formatador (~> 0.2)
mime-types
fog-json (1.2.0)
@@ -214,7 +226,7 @@ GEM
fog-xml (0.1.3)
fog-core
nokogiri (>= 1.5.11, < 2.0.0)
font-awesome-sass (5.11.2)
font-awesome-sass (5.12.0)
sassc (>= 1.11)
formatador (0.2.5)
foundation_emails (2.2.1.0)
@@ -231,13 +243,13 @@ GEM
activerecord (>= 3.2.0, < 5)
globalize (>= 3.0.4, < 6)
paper_trail (>= 3.0.0, < 5)
grape (1.2.1)
grape (1.3.0)
activesupport
builder
dry-types (>= 1.1)
mustermann-grape (~> 1.0.0)
rack (>= 1.3.0)
rack-accept
virtus (>= 1.0.0)
grape-entity (0.7.1)
activesupport (>= 4.0)
multi_json (>= 1.3.2)
@@ -246,7 +258,7 @@ GEM
kaminari
grape-swagger (0.33.0)
grape (>= 0.16.2)
grape-swagger-entity (0.3.3)
grape-swagger-entity (0.3.4)
grape-entity (>= 0.5.0)
grape-swagger (>= 0.31.0)
grape-swagger-rails (0.3.1)
@@ -285,7 +297,6 @@ GEM
i18n-country-translations (1.3.0)
i18n (~> 0.9.3)
railties (~> 4.0)
ice_nine (0.11.2)
inky-rb (1.3.7.5)
foundation_emails (~> 2)
nokogiri
@@ -312,27 +323,27 @@ GEM
railties (>= 3.2.16)
js_regex (1.2.3)
regexp_parser (>= 0.3.6, <= 0.5.0)
json (2.2.0)
json (2.3.0)
jwt (2.2.1)
kaminari (1.1.1)
kaminari (1.2.0)
activesupport (>= 4.1.0)
kaminari-actionview (= 1.1.1)
kaminari-activerecord (= 1.1.1)
kaminari-core (= 1.1.1)
kaminari-actionview (1.1.1)
kaminari-actionview (= 1.2.0)
kaminari-activerecord (= 1.2.0)
kaminari-core (= 1.2.0)
kaminari-actionview (1.2.0)
actionview
kaminari-core (= 1.1.1)
kaminari-activerecord (1.1.1)
kaminari-core (= 1.2.0)
kaminari-activerecord (1.2.0)
activerecord
kaminari-core (= 1.1.1)
kaminari-core (1.1.1)
kaminari-core (= 1.2.0)
kaminari-core (1.2.0)
kaminari-grape (1.0.1)
grape
kaminari-core (~> 1.0)
kaminari-i18n (0.5.0)
kaminari
rails
kgio (2.11.2)
kgio (2.11.3)
launchy (2.4.3)
addressable (~> 2.3)
less (2.6.0)
@@ -359,31 +370,32 @@ GEM
mail (>= 2.0.3)
maildir (>= 0.5.0)
method_source (0.9.2)
mime-types (3.3)
mime-types (3.3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2019.1009)
mini_magick (4.9.5)
mini_magick (4.10.1)
mini_mime (1.0.2)
mini_portile2 (2.4.0)
minitest (5.13.0)
minitest (5.14.0)
minitest-reporters (1.4.2)
ansi
builder
minitest (>= 5.0)
ruby-progressbar
minitest-retry (0.1.9)
minitest-retry (0.2.0)
minitest (>= 5.0)
multi_json (1.14.1)
multi_xml (0.6.0)
multipart-post (2.1.1)
mustermann (1.0.3)
mustermann-grape (1.0.0)
mustermann (~> 1.0.0)
mustermann (1.1.1)
ruby2_keywords (~> 0.0.1)
mustermann-grape (1.0.1)
mustermann (>= 1.0.0)
netrc (0.11.0)
nokogiri (1.10.5)
nokogiri (1.10.7)
mini_portile2 (~> 2.4.0)
oauth (0.5.4)
oauth2 (1.4.2)
oauth2 (1.4.3)
faraday (>= 0.8, < 2.0)
jwt (>= 1.0, < 3.0)
multi_json (~> 1.3)
@@ -392,7 +404,7 @@ GEM
omniauth (1.9.0)
hashie (>= 3.4.6, < 3.7.0)
rack (>= 1.6.2, < 3)
omniauth-facebook (5.0.0)
omniauth-facebook (6.0.0)
omniauth-oauth2 (~> 1.2)
omniauth-github (1.3.0)
omniauth (~> 1.5)
@@ -419,7 +431,7 @@ GEM
activesupport (>= 3.0, < 6.0)
request_store (~> 1.1)
parallel (1.19.1)
parser (2.6.5.0)
parser (2.7.0.2)
ast (~> 2.4.0)
pg (0.20.0)
pg_search (2.3.0)
@@ -439,8 +451,8 @@ GEM
pry-byebug (3.6.0)
byebug (~> 10.0)
pry (~> 0.10)
public_suffix (4.0.1)
rack (1.6.11)
public_suffix (4.0.3)
rack (1.6.12)
rack-accept (0.4.5)
rack (>= 0.4)
rack-cors (1.0.6)
@@ -481,20 +493,20 @@ GEM
thor (>= 0.18.1, < 2.0)
rainbow (2.2.2)
rake
raindrops (0.19.0)
raindrops (0.19.1)
rake (13.0.1)
ranked-model (0.4.1)
activerecord (>= 3.1.12)
rb-fsevent (0.10.3)
rb-inotify (0.10.0)
rb-inotify (0.10.1)
ffi (~> 1.0)
rb-readline (0.5.5)
rdiscount (2.2.0.1)
rdoc (6.2.0)
rdoc (6.2.1)
recaptcha (2.3.0)
json
regexp_parser (0.5.0)
request_store (1.4.1)
request_store (1.5.0)
rack (>= 1.4)
responders (2.4.1)
actionpack (>= 4.2.0, < 6.0)
@@ -504,21 +516,22 @@ GEM
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
roo (2.8.2)
roo (2.8.3)
nokogiri (~> 1)
rubyzip (>= 1.2.1, < 2.0.0)
rubyzip (>= 1.3.0, < 3.0.0)
route_translator (4.4.1)
actionpack (>= 3.2, < 5.0)
activesupport (>= 3.2, < 5.0)
rubocop (0.76.0)
rubocop (0.79.0)
jaro_winkler (~> 1.5.1)
parallel (~> 1.10)
parser (>= 2.6)
parser (>= 2.7.0.1)
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 1.7)
ruby-progressbar (1.10.1)
rubyzip (1.3.0)
ruby2_keywords (0.0.2)
rubyzip (2.2.0)
sass (3.4.25)
sass-rails (5.0.7)
railties (>= 4.0.0, < 6)
@@ -528,7 +541,7 @@ GEM
tilt (>= 1.1, < 3)
sassc (2.2.1)
ffi (~> 1.9)
scout_apm (2.6.4)
scout_apm (2.6.6)
parser
scss-lint (0.38.0)
rainbow (~> 2.0)
@@ -536,7 +549,7 @@ GEM
sdoc (1.0.0)
rdoc (>= 5.0)
selectize-rails (0.12.6)
selenium-webdriver (3.142.6)
selenium-webdriver (3.142.7)
childprocess (>= 0.5, < 4.0)
rubyzip (>= 1.2.2)
shoulda (3.5.0)
@@ -548,11 +561,10 @@ GEM
simple_form (3.5.1)
actionpack (> 4, < 5.2)
activemodel (> 4, < 5.2)
simplecov (0.17.1)
simplecov (0.18.1)
docile (~> 1.1)
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
simplecov-html (0.10.2)
simplecov-html (~> 0.11.0)
simplecov-html (0.11.0)
spring (2.0.2)
activesupport (>= 4.2)
sprockets (3.7.2)
@@ -585,22 +597,17 @@ GEM
less-rails (>= 2.5.0)
railties (>= 3.1)
twitter-bootstrap-rails-confirm (2.0.1)
tzinfo (1.2.5)
tzinfo (1.2.6)
thread_safe (~> 0.1)
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.6)
unicode-display_width (1.6.0)
unicorn (5.5.1)
unicode-display_width (1.6.1)
unicorn (5.5.3)
kgio (~> 2.6)
raindrops (~> 0.7)
virtus (1.0.5)
axiom-types (~> 0.1)
coercible (~> 1.0)
descendants_tracker (~> 0.0, >= 0.0.3)
equalizer (~> 0.0, >= 0.0.9)
warden (1.2.7)
rack (>= 1.0)
web-console (3.3.0)

View File

@@ -494,21 +494,21 @@ Helpy.validateFiles = function (inputFile, allowedExtension, blockedExtension) {
var extError = false;
$.each(inputFile.files, function () {
if (this.size && maxFileSize && this.size > parseInt(maxFileSize)) { sizeExceeded = true; };
if (this.size && maxFileSize && this.size > parseInt(maxFileSize)) { sizeExceeded = true; }
extName = this.name.split('.').pop();
if (allowedExtension.length > 0 && $.inArray(extName, allowedExtension) == -1) { extError = true; };
if (blockedExtension.length > 0 && $.inArray(extName, blockedExtension) != -1) { extError = true; };
if (allowedExtension.length > 0 && $.inArray(extName, allowedExtension) == -1) { extError = true; }
if (blockedExtension.length > 0 && $.inArray(extName, blockedExtension) != -1) { extError = true; }
});
if (sizeExceeded) {
window.alert(maxExceededMessage);
$(inputFile).val('');
};
}
if (extError) {
window.alert(extErrorMessage);
$(inputFile).val('');
};
}
}
};

View File

@@ -61,3 +61,7 @@
top: 0;
}
}
#topic-url {
display: none;
}

View File

@@ -104,7 +104,7 @@ class Admin::BaseController < ApplicationController
else
topics_raw = topics_raw.where(current_status: @status)
end
@topics = topics_raw.page params[:page]
@topics = topics_raw.page(params[:page]).per(15)
end
@@ -129,7 +129,7 @@ class Admin::BaseController < ApplicationController
def set_categories_and_non_featured
@public_categories = Category.roots.publicly.featured.ordered.includes(:docs)
@public_nonfeatured_categories = Category.publicly.unfeatured.alpha.includes(:docs)
@public_nonfeatured_categories = Category.roots.publicly.unfeatured.alpha.includes(:docs)
@internal_categories = Category.only_internally.ordered.includes(:docs)
end

View File

@@ -1,7 +1,6 @@
class Admin::SettingsController < Admin::BaseController
before_action :verify_admin, except: ['profile']
before_action :validate_site_url, only: :update_general
#before_action :verify_agent, only: ['index']
skip_before_action :verify_authenticity_token
@@ -199,15 +198,4 @@ class Admin::SettingsController < Admin::BaseController
end
end
private
def validate_site_url
return true if params['settings.site_url'].blank?
unless URI::regexp.match(params['settings.site_url'])
flash[:error] = 'The given url is invalid'
redirect_to admin_general_settings_path
end
true
end
end

View File

@@ -565,19 +565,19 @@ class Admin::TopicsController < Admin::BaseController
case @status
# when 'all'
# @topics = Topic.all.page params[:page]
# @topics = Topic.all.page(params[:page]).per(15)
when 'new'
@topics = Topic.unread.page params[:page]
@topics = Topic.unread.page(params[:page]).per(15)
when 'active'
@topics = Topic.active.page params[:page]
@topics = Topic.active.page(params[:page]).per(15)
# when 'unread'
# @topics = Topic.unread.all.page params[:page]
# @topics = Topic.unread.all.page(params[:page]).per(15)
when 'mine'
@topics = Topic.mine(current_user.id).page params[:page]
@topics = Topic.mine(current_user.id).page(params[:page]).per(15)
when 'pending'
@topics = Topic.pending.mine(current_user.id).page params[:page]
@topics = Topic.pending.mine(current_user.id).page(params[:page]).per(15)
else
@topics = Topic.where(current_status: @status).page params[:page]
@topics = Topic.where(current_status: @status).page(params[:page]).per(15)
end
end

View File

@@ -47,7 +47,7 @@
class Admin::UsersController < Admin::BaseController
before_action :verify_agent
before_action :verify_admin, only: ['invite','invite_users','scrub','destroy']
before_action :verify_admin, only: ['invite','invite_users','scrub','destroy','new','create']
before_action :fetch_counts, :only => ['show']
before_action :get_all_teams
respond_to :html, :js
@@ -79,6 +79,35 @@ class Admin::UsersController < Admin::BaseController
tracker("Agent: #{current_user.name}", "Viewed User Profile", @user.name)
end
def new
@user = User.new
@user.role = 'user'
end
def create
@user = User.new(user_params)
fetch_counts
# Set the password if it is not provided
@user.password = params[:user][:password].blank? ? User.create_password : params[:user][:password]
@user.update_attribute(:role, params[:user][:role]) if params[:user][:role].present?
if @user.save
tracker("Agent: #{current_user.name}", "Created User Profile", @user.name)
flash[:notice] = "#{@user.name} has been saved"
@user.invite! if params[:user_invite]
respond_to do |format|
format.html {
redirect_to edit_admin_user_path(@user)
}
end
else
render :new
end
end
def edit
@user = User.where(id: params[:id]).first
tracker("Agent: #{current_user.name}", "Editing User Profile", @user.name)

View File

@@ -18,6 +18,7 @@ module API
mount API::V1::Search
mount API::V1::Settings
mount API::V1::Flags
mount API::V1::Tags
add_swagger_documentation \
info: {

View File

@@ -0,0 +1,81 @@
module API
module V1
class Tags < Grape::API
before do
authenticate!
restrict_to_role %w(admin)
end
include API::V1::Defaults
resource :tags, desc: "Manage tags." do
# throttle max: 200, per: 1.minute
# LIST ALL TAGS
desc "List all tags", {
entity: Entity::Tag,
notes: "List all Tags"
}
get "", root: :tags do
tag_ids = ActsAsTaggableOn::Tagging.all.where(context: "tags", taggable_type: "Topic").includes(:tag).map {|tagging| tagging.tag.id }.uniq
tags = ActsAsTaggableOn::Tag.where("id IN (?)", tag_ids).order(:name)
present tags, with: Entity::Tag
end
# SHOW ONE TAG
desc "Show one tag", {
entity: Entity::Tag,
notes: "Returns details of one tag"
}
params do
requires :id, type: String, desc: "ID of the tag"
end
get ":id", root: :tag do
tag = ActsAsTaggableOn::Tag.find(permitted_params[:id])
present tag, with: Entity::Tag
end
# CREATE NEW TAG
desc "Create a new tag", {
entity: Entity::Tag,
notes: "Create a new tag"
}
params do
requires :name, type: String, desc: "The name of the tag"
requires :description, type: String, desc: "The description of the tag"
optional :color, type: String, desc: "The color hex code used for the tag, e.g. #ffcc33"
end
post "", root: :tags do
tag = ActsAsTaggableOn::Tag.new(permitted_params.slice(:name, :description, :color))
tag.save!
ActsAsTaggableOn::Tagging.create!(tag_id: tag.id, taggable_type: 'Topic', context: "tags")
present tag, with: Entity::Tag
end
# UPDATE EXISTING TAG
desc "Update a tag", {
entity: Entity::Tag,
notes: "Update a tag"
}
params do
requires :id, type: Integer, desc: "The ID of the tag you are updating"
requires :name, type: String, desc: "The name of the tag"
requires :description, type: String, desc: "The description of the tag"
optional :color, type: String, desc: "The color hex code used for the tag, e.g. #ffcc33"
end
patch ":id", root: :tags do
tag = ActsAsTaggableOn::Tag.find(permitted_params[:id])
tag.update!(
name: permitted_params[:name],
description: permitted_params[:description],
color: permitted_params[:color]
)
present tag, with: Entity::Tag
end
end
end
end
end

View File

@@ -15,8 +15,6 @@ module API
# PRIVATE TICKET ENDPOINTS
resource :tickets, desc: "Create and Manage private discussions" do
paginate per_page: 20
# LIST BY STATUS
desc "List all PRIVATE tickets by status", {
entity: Entity::Topic,

View File

@@ -13,7 +13,6 @@ module API
resource :users, desc: "View and edit users" do
# throttle max: 200, per: 1.minute
paginate per_page: 20
# LIST ALL USERS
desc "List all users", {

View File

@@ -47,9 +47,9 @@ class TopicsController < ApplicationController
@forum = Forum.ispublic.where(id: params[:forum_id]).first
if @forum
if @forum.allow_topic_voting == true
@topics = @forum.topics.ispublic.by_popularity.page params[:page]
@topics = @forum.topics.ispublic.by_popularity.page(params[:page]).per(15)
else
@topics = @forum.topics.ispublic.chronologic.page params[:page]
@topics = @forum.topics.ispublic.chronologic.page(params[:page]).per(15)
end
@page_title = @forum.name
add_breadcrumb t(:community, default: "Community"), forums_path
@@ -63,7 +63,7 @@ class TopicsController < ApplicationController
end
def tickets
@topics = current_user.topics.isprivate.undeleted.external.chronologic.page params[:page]
@topics = current_user.topics.isprivate.undeleted.external.chronologic.page(params[:page]).per(15)
@page_title = t(:tickets, default: 'Tickets')
add_breadcrumb @page_title
respond_to do |format|
@@ -122,6 +122,11 @@ class TopicsController < ApplicationController
associate_with_doc
if params[:topic][:url].present?
initialize_new_ticket_form_vars
return render :new
end
if recaptcha_enabled? && !user_signed_in?
unless verify_recaptcha(model: @topic)
initialize_new_ticket_form_vars

8
app/models/entity/tag.rb Normal file
View File

@@ -0,0 +1,8 @@
module Entity
class Tag < Base
expose :id
expose :name, documentation: {type: 'String', desc: 'The name of the tag'}
expose :description, documentation: {type: 'String', desc: 'A description for the tag'}
expose :color, documentation: {type: 'String', desc: 'A color hex code used for the tag'}
end
end

View File

@@ -43,8 +43,6 @@ class Topic < ActiveRecord::Base
has_many :votes, :as => :voteable
has_attachments :screenshots, accept: [:jpg, :png, :gif, :pdf, :txt, :rtf, :doc, :docx, :ppt, :pptx, :xls, :xlsx, :zip]
paginates_per 15
include PgSearch::Model
multisearchable :against => [:id, :name, :post_cache],
:if => :public?

View File

@@ -3,7 +3,7 @@
<%= content_tag :span, link_to(cat_nav_item.name, admin_category_path(cat_nav_item.id)), class: 'less-important' %>
</div>
</li>
<% cat_nav_item.children.publicly.featured.active.each do |child| %>
<% cat_nav_item.children.publicly.active.each do |child| %>
<div style="margin-left: 10px;">
<%= render partial: 'admin/categories/cat_nav_item', object: child %>
</div>

View File

@@ -13,7 +13,7 @@
<h4 class="settings-subsection">Self Service Site</h4>
<%= f.text_field 'settings.site_name', value: AppSettings['settings.site_name'], label: t('site_name', default: "Site Name") %>
<%= f.text_field 'settings.site_url', value: AppSettings['settings.site_url'], label: t('site_url', default: "Site URL"), pattern: '^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&\'\(\)\*\+,;=.]+$' %>
<%= f.text_field 'settings.site_url', value: AppSettings['settings.site_url'], label: t('site_url', default: "Site URL"), type: 'url', placeholder: t('site_url_placeholder', default: 'The URL of your Helpy (https://support.yourdomain.com)') %>
<%= f.text_field 'settings.parent_site', value: AppSettings['settings.parent_site'], label: t('parent_site', default: "Parent Site") %>
<%= f.text_field 'settings.parent_company', value: AppSettings['settings.parent_company'], label: t('parent_company', default: "Parent Company") %>
<%= f.text_field 'settings.site_tagline', value: AppSettings['settings.site_tagline'], label: t('site_tagline', default: "Site Tagline") %>

View File

@@ -17,11 +17,13 @@
<div id="user-contact-info" class="user-info">
<div class="user-section">
<%= f.select(:role, roles_collection, prompt: t('select_role')) if current_user.is_admin? && params[:controller] != 'admin/settings' %>
<%= f.email_field :email %>
<%= f.text_field :name %>
<%= f.text_field :company %>
<%= f.text_field :street %>
<%= f.text_field :city, inline: true %> <%= f.text_field :state, inline: true %> <%= f.text_field :zip, inline: true %>
<%#= @topic.user.country.titleize unless @topic.user.country.nil? %>
<%= f.select(:role, roles_collection, prompt: t('select_role')) if current_user.is_admin? && params[:controller] != 'admin/settings' %>
<%= f.select 'team_list', options_for_select(@all_teams.map {|t| [t, t.downcase]}, @user.team_list.map {|g| g.downcase}),
{ label: t(:assign_to, default: "Assign to group:") }, { class: "selectpicker", multiple: true } if teams? && current_user.is_admin? && @user.is_agent? && params[:controller] != 'admin/settings' %>
<%= f.select(:priority, priority_collection) if current_user.is_admin? && params[:controller] != 'admin/settings' %>
@@ -42,7 +44,6 @@
<div class="tiny-header"><%= t(:contact_info, default: "Contact") %>:</div>
<%= f.text_field :work_phone %>
<%= f.text_field :cell_phone %>
<%= f.email_field :email %>
</div>
<div class="user-section">
<div class="tiny-header"><%= t(:social_info, default: "Social") %>:</div>

View File

@@ -2,7 +2,7 @@
<%= show_responsive_nav %>
<%= t(:users, default: "Users") %><%= ": " + role_label(params[:role]).html_safe if params[:role].present? %>
<div class="pull-right nav-new-button">
<%= link_to t(:invite_users, default: "Invite New Users"), admin_invite_path,
<%= link_to t(:create_user, default: "Create New User"), new_admin_user_path,
remote: false, class: 'btn btn-primary' if current_user.is_admin? %>
</div>
<% end %>

View File

@@ -0,0 +1,58 @@
<% title "#{admin_title} #{t(:discussions, default: "Discussions")}" %>
<div class="row">
<div id="ticket-stats" class="col-md-3 col-lg-2 hidden-sm hidden-xs">
<%= render 'admin/users/user_filter' %>
</div>
<div class="col-md-9 col-lg-10 col-xs-12 main-panel" id="tickets">
<%= content_tag :h3, class: 'view-header' do %>
<%= t(:create_user, default: "Create New User") %>
<div class="pull-right nav-new-button">
<%= link_to t(:invite_users, default: "Bulk Invite New Users"), admin_invite_path,
remote: false, class: 'btn btn-primary' if current_user.is_admin? %>
</div>
<% end %>
<div id="user-info">
<% content_for :page_title do %>
<h1><%= I18n.t('devise.registrations.new.sign_up') %></h1>
<% end %>
<%= simple_form_for [:admin, @user], method: :post do |f| %>
<%= f.error_notification %>
<%= f.input :role, collection: roles_collection %>
<%= f.input :email, input_html: { autocomplete: 'new-password', autofill: 'off' } %>
<%= f.input :name, input_html: { autocomplete: 'new-password', autofill: 'off' } %>
<div>
<%= check_box_tag 'user_invite', '1', true %> <%= t(:send_invite) %>
<br/>
<br/>
</div>
<div class="user_password_container" style="display: none;">
<%= f.input :password, input_html: { autocomplete: 'new-password', autofill: 'off' } %>
</div>
<br/>
<%= f.submit t(:save_changes), class: 'btn btn-warning' %>
<% end %>
<script>
$(document).ready(function(){
$('#user_invite').on('click', function(){
$invite = $('#user_invite');
if ($invite.is(':checked')) {
$('.user_password_container').hide();
} else {
$('.user_password_container').show();
}
});
});
</script>
</div>
</div>
</div>

View File

@@ -3,7 +3,7 @@
<columns class="callout panel">
<spacer size="16"></spacer>
<p><%= t("devise.mailer.invitation_instructions.hello", email: @resource.email) %></p>
<p><%= t("devise.mailer.invitation_instructions.hello", email: (@resource.name || @resource.email)) %></p>
<p><%= t("devise.mailer.invitation_instructions.someone_invited_you", url: AppSettings['settings.site_name']) %></p>

View File

@@ -84,6 +84,8 @@
</div>
<% end %>
<%= url_field 'topic', 'url', value: '', placeholder: 'http://', id: 'topic-url' %>
<%= hidden_field_tag :client_id %>
<%= f.hidden_field :doc_id, value: params[:doc_id] if params[:doc_id] %>
<%= hidden_field_tag :from, 'widget' if params[:controller] == 'widget' || params[:from] == 'widget' %>

View File

@@ -1,5 +1,5 @@
Kaminari.configure do |config|
config.default_per_page = 10
config.default_per_page = 20
# config.max_per_page = nil
# config.window = 4
# config.outer_window = 0

View File

@@ -191,6 +191,7 @@ de:
# User Admin
users: &users Benutzer
# create_user: Create New User
account: Benutzerkonto
contact_info: Kontakt
social_info: Soziales

View File

@@ -195,6 +195,7 @@ en:
# User Admin
users: &users Users
create_user: Create New User
account: Account
contact_info: Contact
social_info: Social
@@ -275,7 +276,7 @@ en:
other: "%{count} users were found named %{query}"
# Invite users
invite_users: "Invite New Users"
invite_users: "Bulk Invite New Users"
invite_emails: "Email Addresses"
invite_emails_placeholder: "Type one or more email addresses to send an invitation to"
invite_message: "Message"
@@ -392,6 +393,7 @@ en:
# General
site_name: Site Name
site_url: Site URL
site_url_placeholder: The URL of your Helpy (https://support.yourdomain.com)
parent_site: Parent Site
parent_company: Parent Company
site_tagline: Site Tagline

View File

@@ -191,6 +191,7 @@ es:
# User Admin
users: Usuarios
# create_user: Create New User
# account: Account
contact_info: Contacto
social_info: Social

View File

@@ -193,6 +193,7 @@ fr:
# User Admin
users: Utilisateurs
create_user: Créer un nouvel utilisateur
account: Compte
contact_info: Informations de contact
social_info: Réseaux sociaux

View File

@@ -193,6 +193,7 @@ pt-br:
# User Admin
users: &users "Usuários"
# create_user: Create New User
account: Conta
contact_info: "Contato"
social_info: "Social"

View File

@@ -59,19 +59,6 @@ class Admin::SettingsControllerTest < ActionController::TestCase
assert_redirected_to admin_general_settings_path
end
test 'the site url provided is invalid' do
sign_in users(:admin)
put :update_general,
'settings.site_name' => 'Helpy Support 2',
'settings.site_url' => 'invalid_url',
'settings.parent_site' => 'http://helpy.io/2',
'settings.parent_company' => 'Helpy 2',
'settings.site_tagline' => 'Support',
'settings.google_analytics_id' => 'UA-0000-21'
assert_equal 'The given url is invalid', flash[:error]
assert_redirected_to admin_general_settings_path
end
test 'an admin should be able to modify design' do
sign_in users(:admin)
put :update_design,

View File

@@ -97,6 +97,43 @@ class Admin::UsersControllerTest < ActionController::TestCase
assert "Anonymous User", User.find(3).name
end
test "an admin should be able to add a new user without an invite" do
sign_in users(:admin)
get :new
assert_difference 'User.count', 1 do
assert_difference "ActionMailer::Base.deliveries.size", 0 do
post :create, {
user: {
name: 'some name',
email: 'some@email.test',
password: 'some_password',
role: 'agent'
},
locale: :en
}
end
end
end
test "an admin should be able to add a new user and send an invite" do
sign_in users(:admin)
get :new
assert_difference 'User.count', 1 do
assert_difference "ActionMailer::Base.deliveries.size", 1 do
post :create, {
user: {
name: 'some name',
email: 'some@email.test',
password: 'some_password',
role: 'agent'
},
locale: :en,
user_invite: true
}
end
end
end
# admin/agents
%w(admin agent).each do |admin|

View File

@@ -0,0 +1,103 @@
# == Schema Information
#
# Table name: tags
#
# id :integer not null, primary key
# name :string
# description :text
# color :string
#
require 'test_helper'
class API::V1::TagsTest < ActiveSupport::TestCase
include Rack::Test::Methods
def app
Rails.application
end
setup do
set_default_settings
@user = users(:admin)
@api_key = ApiKey.create(name: "Test Tags Key", user: @user)
@default_params = { token: @api_key.access_token }
@simple_tag = ActsAsTaggableOn::Tag.new(name: 'simple', description: nil, color: nil)
@full_tag = ActsAsTaggableOn::Tag.new(name: 'full', description: 'description', color: '#336699')
[@simple_tag, @full_tag].each do |tag|
tag.save!
ActsAsTaggableOn::Tagging.create!(tag_id: tag.id, taggable_type: 'Topic', context: 'tags')
end
end
test "an unauthenticated user should receive an unauthorized message" do
get '/api/v1/tags.json'
# Check not authorized
assert_equal 401, last_response.status
end
test "an API user should be able to return tags" do
get '/api/v1/tags.json', @default_params
# Check OK
assert last_response.ok?, "Response was #{last_response.status}, expected 200"
# Check returned value
objects = JSON.parse(last_response.body)
assert objects.length == 2, "Only #{objects.length} returned out of 2 tags"
end
test "an API user should be able to return a specific tag" do
get "/api/v1/tags/#{@simple_tag.id}.json", @default_params
# Check OK
assert last_response.ok?, "Response was #{last_response.status}, expected 200"
# Check returned value
object = JSON.parse(last_response.body)
assert object['id'] == @simple_tag.id
assert object['name'] == @simple_tag.name
end
test "an API user should be able to create a tag" do
params = {
name: Faker::Company.catch_phrase,
description: Faker::Lorem.paragraph,
color: Faker::Color.hex_color
}
post '/api/v1/tags.json', @default_params.merge(params)
object = JSON.parse(last_response.body)
assert_equal params[:name], object['name']
end
test "an API user should not be able to create an invalid tag" do
params = {
name: nil,
description: Faker::Lorem.paragraph
}
post '/api/v1/tags.json', @default_params.merge(params)
assert_equal 422, last_response.status
end
test "an API user should be able to update a tag" do
params = {
name: "Not simple anymore",
description: Faker::Lorem.paragraph,
color: "#ffcc99"
}
patch "/api/v1/tags/#{@simple_tag.id}.json", @default_params.merge(params)
object = JSON.parse(last_response.body)
assert_equal "Not simple anymore", object['name']
end
end

View File

@@ -346,6 +346,22 @@ class TopicsControllerTest < ActionController::TestCase
end
end
test 'a browsing user should not be able to create a new public topic when they fall for the honeypot' do
get :new, locale: :en
assert_response :success
assert_difference 'User.count', 0, 'No user should have been created' do
assert_difference 'Topic.count', 0, 'No topic should have been created' do
assert_difference 'Post.count', 0, 'No new post should have been created' do
post :create, topic: { user: { name: 'a user', email: 'anon@test.com', private: false }, name: 'some new private topic', body: 'some body text', forum_id: 3, posts_attributes: {:"0" => {body: "this is the body"}}, url: 'http://spamy.spam'}, locale: :en
end
end
end
assert_response :success
assert_select "#new_topic"
end
test 'a browsing user should be able to create a new public topic without signing in when recaptcha enable' do
# Make sure recaptcha site_key is set