xat_support 1.29.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +176 -0
- data/README.md +33 -0
- data/config/locales/en.yml +85 -0
- data/config/locales/translations/zendesk_apps_support.yml +210 -0
- data/lib/xat_support.rb +30 -0
- data/lib/zendesk_apps_support/app_file.rb +38 -0
- data/lib/zendesk_apps_support/app_requirement.rb +5 -0
- data/lib/zendesk_apps_support/app_version.rb +64 -0
- data/lib/zendesk_apps_support/assets/default_styles.scss +3 -0
- data/lib/zendesk_apps_support/assets/default_template.html.erb +10 -0
- data/lib/zendesk_apps_support/assets/installed.js.erb +17 -0
- data/lib/zendesk_apps_support/assets/src.js.erb +39 -0
- data/lib/zendesk_apps_support/build_translation.rb +50 -0
- data/lib/zendesk_apps_support/engine.rb +9 -0
- data/lib/zendesk_apps_support/i18n.rb +28 -0
- data/lib/zendesk_apps_support/installation.rb +20 -0
- data/lib/zendesk_apps_support/installed.rb +22 -0
- data/lib/zendesk_apps_support/location.rb +29 -0
- data/lib/zendesk_apps_support/package.rb +339 -0
- data/lib/zendesk_apps_support/product.rb +15 -0
- data/lib/zendesk_apps_support/sass_functions.rb +20 -0
- data/lib/zendesk_apps_support/stylesheet_compiler.rb +23 -0
- data/lib/zendesk_apps_support/validations/banner.rb +33 -0
- data/lib/zendesk_apps_support/validations/manifest.rb +214 -0
- data/lib/zendesk_apps_support/validations/marketplace.rb +20 -0
- data/lib/zendesk_apps_support/validations/requirements.rb +105 -0
- data/lib/zendesk_apps_support/validations/source.rb +95 -0
- data/lib/zendesk_apps_support/validations/stylesheets.rb +29 -0
- data/lib/zendesk_apps_support/validations/templates.rb +18 -0
- data/lib/zendesk_apps_support/validations/translations.rb +64 -0
- data/lib/zendesk_apps_support/validations/validation_error.rb +101 -0
- metadata +229 -0
data/lib/xat_support.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module ZendeskAppsSupport
|
2
|
+
require 'zendesk_apps_support/sass_functions'
|
3
|
+
require 'zendesk_apps_support/engine'
|
4
|
+
require 'execjs'
|
5
|
+
|
6
|
+
autoload :AppFile, 'zendesk_apps_support/app_file'
|
7
|
+
autoload :BuildTranslation, 'zendesk_apps_support/build_translation'
|
8
|
+
autoload :I18n, 'zendesk_apps_support/i18n'
|
9
|
+
autoload :Location, 'zendesk_apps_support/location'
|
10
|
+
autoload :Product, 'zendesk_apps_support/product'
|
11
|
+
autoload :Package, 'zendesk_apps_support/package'
|
12
|
+
autoload :Installed, 'zendesk_apps_support/installed'
|
13
|
+
autoload :Installation, 'zendesk_apps_support/installation'
|
14
|
+
autoload :AppRequirement, 'zendesk_apps_support/app_requirement'
|
15
|
+
autoload :AppVersion, 'zendesk_apps_support/app_version'
|
16
|
+
autoload :StylesheetCompiler, 'zendesk_apps_support/stylesheet_compiler'
|
17
|
+
|
18
|
+
module Validations
|
19
|
+
autoload :ValidationError, 'zendesk_apps_support/validations/validation_error'
|
20
|
+
autoload :Manifest, 'zendesk_apps_support/validations/manifest'
|
21
|
+
autoload :Marketplace, 'zendesk_apps_support/validations/marketplace'
|
22
|
+
autoload :Source, 'zendesk_apps_support/validations/source'
|
23
|
+
autoload :Templates, 'zendesk_apps_support/validations/templates'
|
24
|
+
autoload :Translations, 'zendesk_apps_support/validations/translations'
|
25
|
+
autoload :ESLintValidationError, 'zendesk_apps_support/validations/validation_error'
|
26
|
+
autoload :Stylesheets, 'zendesk_apps_support/validations/stylesheets'
|
27
|
+
autoload :Requirements, 'zendesk_apps_support/validations/requirements'
|
28
|
+
autoload :Banner, 'zendesk_apps_support/validations/banner'
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module ZendeskAppsSupport
|
2
|
+
class AppFile
|
3
|
+
attr_reader :relative_path
|
4
|
+
attr_reader :absolute_path
|
5
|
+
|
6
|
+
def initialize(package, relative_path)
|
7
|
+
@relative_path = relative_path
|
8
|
+
@file = File.new(package.path_to(relative_path))
|
9
|
+
@absolute_path = File.absolute_path @file.path
|
10
|
+
end
|
11
|
+
|
12
|
+
def read
|
13
|
+
File.read @file.path
|
14
|
+
end
|
15
|
+
|
16
|
+
def =~(regex)
|
17
|
+
relative_path =~ regex
|
18
|
+
end
|
19
|
+
|
20
|
+
def match(regex)
|
21
|
+
self =~ regex
|
22
|
+
end
|
23
|
+
|
24
|
+
alias_method :to_s, :relative_path
|
25
|
+
|
26
|
+
def method_missing(sym, *args, &block)
|
27
|
+
if @file.respond_to?(sym)
|
28
|
+
@file.call(sym, *args, &block)
|
29
|
+
else
|
30
|
+
super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def respond_to_missing?(sym, include_private = false)
|
35
|
+
@file.send(:respond_to_missing?, sym, include_private) || super
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module ZendeskAppsSupport
|
4
|
+
# At any point in time, we support up to three versions:
|
5
|
+
# * deprecated -- we will still serve apps targeting the deprecated version,
|
6
|
+
# but newly created or updated apps CANNOT target it
|
7
|
+
# * current -- we will serve apps targeting the current version;
|
8
|
+
# newly created or updated apps SHOULD target it
|
9
|
+
# * future -- we will serve apps targeting the future version;
|
10
|
+
# newly created or updates apps MAY target it, but it
|
11
|
+
# may change without notice
|
12
|
+
class AppVersion
|
13
|
+
DEPRECATED = '0.5'.freeze
|
14
|
+
CURRENT = '1.0'.freeze
|
15
|
+
FUTURE = '2.0'.freeze
|
16
|
+
|
17
|
+
TO_BE_SERVED = [DEPRECATED, CURRENT, FUTURE].compact.freeze
|
18
|
+
VALID_FOR_UPDATE = [CURRENT, FUTURE].compact.freeze
|
19
|
+
|
20
|
+
def initialize(version)
|
21
|
+
@version = version.to_s
|
22
|
+
@version.freeze
|
23
|
+
freeze
|
24
|
+
end
|
25
|
+
|
26
|
+
def servable?
|
27
|
+
TO_BE_SERVED.include?(@version)
|
28
|
+
end
|
29
|
+
|
30
|
+
def valid_for_update?
|
31
|
+
VALID_FOR_UPDATE.include?(@version)
|
32
|
+
end
|
33
|
+
|
34
|
+
def deprecated?
|
35
|
+
@version == DEPRECATED
|
36
|
+
end
|
37
|
+
|
38
|
+
def obsolete?
|
39
|
+
!servable?
|
40
|
+
end
|
41
|
+
|
42
|
+
def blank?
|
43
|
+
@version.nil? || @version == ''
|
44
|
+
end
|
45
|
+
|
46
|
+
def present?
|
47
|
+
!blank?
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_s
|
51
|
+
@version
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_json(*)
|
55
|
+
@version.inspect
|
56
|
+
end
|
57
|
+
|
58
|
+
def ==(other)
|
59
|
+
@version == other.to_s
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
AppVersion.freeze
|
64
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
(function() {
|
2
|
+
<% appsjs.each do |appjs| %>
|
3
|
+
<%= appjs %>
|
4
|
+
<% end %>
|
5
|
+
|
6
|
+
<% installations.each do |installation| %>
|
7
|
+
if (ZendeskApps[<%= installation.app_name.to_json %>]) {
|
8
|
+
ZendeskApps[<%= installation.app_name.to_json %>].install(<%= installation.to_json %>);
|
9
|
+
}
|
10
|
+
<% end %>
|
11
|
+
|
12
|
+
<% installation_orders.each do |loc_name, installation_list| %>
|
13
|
+
ZendeskApps.sortAppsForSite(<%= loc_name.to_json %>, <%= installation_list.to_json %>);
|
14
|
+
<% end %>
|
15
|
+
}());
|
16
|
+
|
17
|
+
ZendeskApps.trigger && ZendeskApps.trigger('ready');
|
@@ -0,0 +1,39 @@
|
|
1
|
+
<% unless iframe_only %>
|
2
|
+
with( ZendeskApps.AppScope.create() ) {
|
3
|
+
require.modules = {
|
4
|
+
<% modules.each do |path, content| %>
|
5
|
+
<%= path.to_json %>: function(exports, require, module) {
|
6
|
+
<%= content %>
|
7
|
+
},
|
8
|
+
<% end %>
|
9
|
+
<% if is_es2015 %>
|
10
|
+
'_root.js': function(exports, require, module) {
|
11
|
+
<%= source %>
|
12
|
+
}
|
13
|
+
<% else %>
|
14
|
+
eom: undefined
|
15
|
+
<% end %>
|
16
|
+
};
|
17
|
+
|
18
|
+
var source = <%= source %>;
|
19
|
+
}
|
20
|
+
<% end %>
|
21
|
+
var app = ZendeskApps.defineApp(<%= iframe_only ? 'null' : 'source' %>)
|
22
|
+
.reopenClass(<%= app_settings.to_json %>)
|
23
|
+
.reopen({
|
24
|
+
appName: <%= name.to_json %>,
|
25
|
+
appVersion: <%= version.to_json %>,
|
26
|
+
assetUrlPrefix: <%= asset_url_prefix.to_json %>,
|
27
|
+
appClassName: <%= app_class_name.to_json %>,
|
28
|
+
author: {
|
29
|
+
name: <%= author['name'].to_json %>,
|
30
|
+
email: <%= author['email'].to_json %>
|
31
|
+
},
|
32
|
+
<% unless iframe_only %>
|
33
|
+
translations: <%= translations.to_json %>,
|
34
|
+
templates: <%= templates.to_json %>,
|
35
|
+
<% end %>
|
36
|
+
frameworkVersion: <%= framework_version.to_json %>
|
37
|
+
});
|
38
|
+
|
39
|
+
ZendeskApps[<%= name.to_json %>] = app;
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ZendeskAppsSupport
|
2
|
+
module BuildTranslation
|
3
|
+
I18N_TITLE_KEY = 'title'
|
4
|
+
I18N_VALUE_KEY = 'value'
|
5
|
+
I18N_KEYS = [I18N_TITLE_KEY, I18N_VALUE_KEY]
|
6
|
+
|
7
|
+
def to_flattened_namespaced_hash(hash, target_key = nil, prefix = nil)
|
8
|
+
hash.inject({}) do |result, (key, value)|
|
9
|
+
key = [prefix, key].compact.join('.')
|
10
|
+
if value.is_a?(Hash)
|
11
|
+
if target_key && is_translation_hash?(value)
|
12
|
+
result[key] = value[target_key]
|
13
|
+
else
|
14
|
+
result.update(to_flattened_namespaced_hash(value, target_key, key))
|
15
|
+
end
|
16
|
+
else
|
17
|
+
result[key] = value
|
18
|
+
end
|
19
|
+
result
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def remove_zendesk_keys(scope, translations = {})
|
24
|
+
scope.each_key do |key|
|
25
|
+
context = scope[key]
|
26
|
+
|
27
|
+
if context.is_a?(Hash)
|
28
|
+
|
29
|
+
if is_translation_hash?(context)
|
30
|
+
translations[key] = context[I18N_VALUE_KEY]
|
31
|
+
else
|
32
|
+
translations[key] ||= {}
|
33
|
+
translations[key] = remove_zendesk_keys(context, translations[key])
|
34
|
+
end
|
35
|
+
|
36
|
+
else
|
37
|
+
translations[key] = context
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
translations
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def is_translation_hash?(hash)
|
47
|
+
hash.keys.sort == I18N_KEYS
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module ZendeskAppsSupport
|
2
|
+
module I18n
|
3
|
+
class << self
|
4
|
+
def t(key, *args)
|
5
|
+
i18n.t(key, *args)
|
6
|
+
end
|
7
|
+
|
8
|
+
def set_locale(locale)
|
9
|
+
i18n.locale = locale
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def i18n
|
15
|
+
@i18n ||= begin
|
16
|
+
require 'i18n'
|
17
|
+
::I18n.load_path += locale_files
|
18
|
+
::I18n
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def locale_files
|
23
|
+
Dir[File.expand_path('../../../config/locales/*.yml', __FILE__)] -
|
24
|
+
Dir[File.expand_path('../../../config/locales/*.zendesk.yml', __FILE__)]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ZendeskAppsSupport
|
2
|
+
class Installation
|
3
|
+
|
4
|
+
attr_accessor :id, :app_id, :app_name, :requirements, :settings, :enabled, :updated_at, :created_at
|
5
|
+
|
6
|
+
def initialize(options)
|
7
|
+
options.each do |k, v|
|
8
|
+
public_send("#{k}=", v)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_json
|
13
|
+
hash = {}
|
14
|
+
self.instance_variables.each do |var|
|
15
|
+
hash[var.to_s.sub('@', '')] = self.instance_variable_get var
|
16
|
+
end
|
17
|
+
hash.to_json
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'erubis'
|
2
|
+
|
3
|
+
module ZendeskAppsSupport
|
4
|
+
class Installed
|
5
|
+
INSTALLED_TEMPLATE = Erubis::Eruby.new(File.read(File.expand_path('../assets/installed.js.erb', __FILE__)))
|
6
|
+
|
7
|
+
def initialize(appsjs, installations = [])
|
8
|
+
@appsjs = appsjs
|
9
|
+
@installations = installations
|
10
|
+
end
|
11
|
+
|
12
|
+
def compile_js(options = {})
|
13
|
+
installation_order = options.fetch(:installation_orders, {})
|
14
|
+
|
15
|
+
INSTALLED_TEMPLATE.result(
|
16
|
+
appsjs: @appsjs,
|
17
|
+
installations: @installations,
|
18
|
+
installation_orders: installation_order
|
19
|
+
)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module ZendeskAppsSupport
|
2
|
+
module Location
|
3
|
+
# the numbers below match the enum values on the database, do not change them!
|
4
|
+
LOCATIONS_AVAILABLE = {
|
5
|
+
'zendesk' => {
|
6
|
+
'top_bar' => { 'id' => 1, 'orderable' => true },
|
7
|
+
'nav_bar' => { 'id' => 2, 'orderable' => true },
|
8
|
+
'ticket_sidebar' => { 'id' => 3, 'orderable' => true },
|
9
|
+
'new_ticket_sidebar' => { 'id' => 4, 'orderable' => true },
|
10
|
+
'user_sidebar' => { 'id' => 5, 'orderable' => true },
|
11
|
+
'organization_sidebar' => { 'id' => 6, 'orderable' => true },
|
12
|
+
'background' => { 'id' => 7, 'orderable' => false }
|
13
|
+
},
|
14
|
+
'zopim' => {
|
15
|
+
'chat_sidebar' => { 'id' => 8, 'orderable' => false }
|
16
|
+
}
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def hosts
|
21
|
+
LOCATIONS_AVAILABLE.keys
|
22
|
+
end
|
23
|
+
|
24
|
+
def names_for(host)
|
25
|
+
LOCATIONS_AVAILABLE[host.to_s].keys
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,339 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'erubis'
|
3
|
+
require 'json'
|
4
|
+
require 'babel/transpiler'
|
5
|
+
|
6
|
+
module ZendeskAppsSupport
|
7
|
+
class Package
|
8
|
+
include ZendeskAppsSupport::BuildTranslation
|
9
|
+
|
10
|
+
REQUIREMENTS_FILENAME = "requirements.json"
|
11
|
+
|
12
|
+
DEFAULT_LAYOUT = Erubis::Eruby.new(File.read(File.expand_path('../assets/default_template.html.erb', __FILE__)))
|
13
|
+
DEFAULT_SCSS = File.read(File.expand_path('../assets/default_styles.scss', __FILE__))
|
14
|
+
SRC_TEMPLATE = Erubis::Eruby.new(File.read(File.expand_path('../assets/src.js.erb', __FILE__)))
|
15
|
+
|
16
|
+
LEGACY_URI_STUB = '_legacy'
|
17
|
+
|
18
|
+
attr_reader :lib_root, :root, :warnings
|
19
|
+
|
20
|
+
def initialize(dir, is_cached = true)
|
21
|
+
@root = Pathname.new(File.expand_path(dir))
|
22
|
+
@lib_root = Pathname.new(File.join(root, 'lib'))
|
23
|
+
|
24
|
+
@is_cached = is_cached # disabled by ZAT for development
|
25
|
+
@warnings = []
|
26
|
+
end
|
27
|
+
|
28
|
+
def validate(marketplace: true)
|
29
|
+
[].tap do |errors|
|
30
|
+
errors << Validations::Marketplace.call(self) if marketplace
|
31
|
+
|
32
|
+
errors << Validations::Manifest.call(self)
|
33
|
+
|
34
|
+
if has_manifest?
|
35
|
+
errors << Validations::Source.call(self)
|
36
|
+
errors << Validations::Translations.call(self)
|
37
|
+
|
38
|
+
unless manifest_json['requirementsOnly']
|
39
|
+
errors << Validations::Templates.call(self)
|
40
|
+
errors << Validations::Stylesheets.call(self)
|
41
|
+
end
|
42
|
+
|
43
|
+
if has_requirements?
|
44
|
+
errors << Validations::Requirements.call(self)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
if has_banner?
|
49
|
+
errors << Validations::Banner.call(self)
|
50
|
+
end
|
51
|
+
|
52
|
+
errors.flatten!.compact!
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def validate!(marketplace: true)
|
57
|
+
errors = validate(marketplace: marketplace)
|
58
|
+
if errors.any?
|
59
|
+
raise errors.first
|
60
|
+
end
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
64
|
+
def assets
|
65
|
+
@assets ||= Dir.chdir(root) do
|
66
|
+
Dir['assets/**/*'].select { |f| File.file?(f) }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def path_to(file)
|
71
|
+
File.join(root, file)
|
72
|
+
end
|
73
|
+
|
74
|
+
def requirements_path
|
75
|
+
path_to(REQUIREMENTS_FILENAME)
|
76
|
+
end
|
77
|
+
|
78
|
+
def locales
|
79
|
+
translations.keys
|
80
|
+
end
|
81
|
+
|
82
|
+
def files
|
83
|
+
files = []
|
84
|
+
Dir[root.join('**/**')].each do |f|
|
85
|
+
next unless File.file?(f)
|
86
|
+
relative_file_name = f.sub(/#{root}\/?/, '')
|
87
|
+
next if relative_file_name =~ /^tmp\//
|
88
|
+
files << AppFile.new(self, relative_file_name)
|
89
|
+
end
|
90
|
+
files
|
91
|
+
end
|
92
|
+
|
93
|
+
def js_files
|
94
|
+
@js_files ||= files.select { |f| f.to_s == 'app.js' || ( f.to_s.start_with?('lib/') && f.to_s.end_with?('.js') ) }
|
95
|
+
end
|
96
|
+
|
97
|
+
def lib_files
|
98
|
+
@lib_files ||= js_files.select { |f| f =~ /^lib\// }
|
99
|
+
end
|
100
|
+
|
101
|
+
def template_files
|
102
|
+
files.select { |f| f =~ /^templates\/.*\.hdbs$/ }
|
103
|
+
end
|
104
|
+
|
105
|
+
def translation_files
|
106
|
+
files.select { |f| f =~ /^translations\// }
|
107
|
+
end
|
108
|
+
|
109
|
+
def compile_js(options)
|
110
|
+
begin
|
111
|
+
app_id = options.fetch(:app_id)
|
112
|
+
asset_url_prefix = options.fetch(:assets_dir)
|
113
|
+
name = options.fetch(:app_name)
|
114
|
+
rescue KeyError => e
|
115
|
+
raise ArgumentError, e.message
|
116
|
+
end
|
117
|
+
|
118
|
+
locale = options.fetch(:locale, 'en')
|
119
|
+
|
120
|
+
source = iframe_only? ? nil : app_js
|
121
|
+
version = manifest_json['version']
|
122
|
+
app_class_name = "app-#{app_id}"
|
123
|
+
author = manifest_json['author']
|
124
|
+
framework_version = manifest_json['frameworkVersion']
|
125
|
+
single_install = manifest_json['singleInstall'] || false
|
126
|
+
templates = is_no_template ? {} : compiled_templates(app_id, asset_url_prefix)
|
127
|
+
|
128
|
+
app_settings = {
|
129
|
+
location: locations,
|
130
|
+
noTemplate: no_template_locations,
|
131
|
+
singleInstall: single_install
|
132
|
+
}.select { |_k, v| !v.nil? }
|
133
|
+
|
134
|
+
SRC_TEMPLATE.result(
|
135
|
+
name: name,
|
136
|
+
version: version,
|
137
|
+
is_es2015: !!manifest_json['es2015'],
|
138
|
+
source: source,
|
139
|
+
app_settings: app_settings,
|
140
|
+
asset_url_prefix: asset_url_prefix,
|
141
|
+
app_class_name: app_class_name,
|
142
|
+
author: author,
|
143
|
+
translations: translations_for(locale),
|
144
|
+
framework_version: framework_version,
|
145
|
+
templates: templates,
|
146
|
+
modules: commonjs_modules,
|
147
|
+
iframe_only: iframe_only?
|
148
|
+
)
|
149
|
+
end
|
150
|
+
|
151
|
+
def manifest_json
|
152
|
+
@manifest ||= read_json('manifest.json')
|
153
|
+
end
|
154
|
+
|
155
|
+
def requirements_json
|
156
|
+
return nil unless has_requirements?
|
157
|
+
@requirements ||= read_json('requirements.json')
|
158
|
+
end
|
159
|
+
|
160
|
+
def is_no_template
|
161
|
+
if manifest_json['noTemplate'].is_a?(Array)
|
162
|
+
false
|
163
|
+
else
|
164
|
+
!!manifest_json['noTemplate']
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def no_template_locations
|
169
|
+
if manifest_json['noTemplate'].is_a?(Array)
|
170
|
+
manifest_json['noTemplate']
|
171
|
+
else
|
172
|
+
!!manifest_json['noTemplate']
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def compiled_templates(app_id, asset_url_prefix)
|
177
|
+
compiled_css = ZendeskAppsSupport::StylesheetCompiler.new(DEFAULT_SCSS + app_css, app_id, asset_url_prefix).compile
|
178
|
+
|
179
|
+
layout = templates['layout'] || DEFAULT_LAYOUT.result
|
180
|
+
|
181
|
+
templates.tap do |templates|
|
182
|
+
templates['layout'] = "<style>\n#{compiled_css}</style>\n#{layout}"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def market_translations!(locale)
|
187
|
+
result = translations[locale].fetch('app', {})
|
188
|
+
result.delete('name')
|
189
|
+
result.delete('description')
|
190
|
+
result.delete('long_description')
|
191
|
+
result.delete('installation_instructions')
|
192
|
+
result
|
193
|
+
end
|
194
|
+
|
195
|
+
def has_location?
|
196
|
+
manifest_json['location']
|
197
|
+
end
|
198
|
+
|
199
|
+
def has_file?(path)
|
200
|
+
File.exist?(path_to(path))
|
201
|
+
end
|
202
|
+
|
203
|
+
def app_css
|
204
|
+
css_file = path_to('app.css')
|
205
|
+
scss_file = path_to('app.scss')
|
206
|
+
File.exist?(scss_file) ? File.read(scss_file) : ( File.exist?(css_file) ? File.read(css_file) : '' )
|
207
|
+
end
|
208
|
+
|
209
|
+
def locations
|
210
|
+
locations = manifest_json['location']
|
211
|
+
case locations
|
212
|
+
when Hash
|
213
|
+
locations
|
214
|
+
when Array
|
215
|
+
{ 'zendesk' => Hash[locations.map { |location| [ location, LEGACY_URI_STUB ] }] }
|
216
|
+
when String
|
217
|
+
{ 'zendesk' => { locations => LEGACY_URI_STUB } }
|
218
|
+
else # NilClass
|
219
|
+
{ 'zendesk' => {} }
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def iframe_only?
|
224
|
+
!legacy_non_iframe_app?
|
225
|
+
end
|
226
|
+
|
227
|
+
private
|
228
|
+
|
229
|
+
def legacy_non_iframe_app?
|
230
|
+
iframe_urls = locations.values.flat_map(&:values)
|
231
|
+
iframe_urls.all? { |l| l == LEGACY_URI_STUB }
|
232
|
+
end
|
233
|
+
|
234
|
+
def templates
|
235
|
+
templates_dir = File.join(root, 'templates')
|
236
|
+
Dir["#{templates_dir}/*.hdbs"].inject({}) do |memo, file|
|
237
|
+
str = File.read(file)
|
238
|
+
str.chomp!
|
239
|
+
memo[File.basename(file, File.extname(file))] = str
|
240
|
+
memo
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def translations_for(locale)
|
245
|
+
trans = translations
|
246
|
+
return trans[locale] if trans[locale]
|
247
|
+
trans[self.manifest_json['defaultLocale']]
|
248
|
+
end
|
249
|
+
|
250
|
+
def translations
|
251
|
+
return @translations if @is_cached && @translations
|
252
|
+
|
253
|
+
@translations = begin
|
254
|
+
translation_dir = File.join(root, 'translations')
|
255
|
+
return {} unless File.directory?(translation_dir)
|
256
|
+
|
257
|
+
locale_path = "#{translation_dir}/#{self.manifest_json['defaultLocale']}.json"
|
258
|
+
default_translations = process_translations(locale_path)
|
259
|
+
|
260
|
+
Dir["#{translation_dir}/*.json"].inject({}) do |memo, path|
|
261
|
+
locale = File.basename(path, File.extname(path))
|
262
|
+
|
263
|
+
locale_translations = if locale == self.manifest_json['defaultLocale']
|
264
|
+
default_translations
|
265
|
+
else
|
266
|
+
deep_merge_hash(default_translations, process_translations(path))
|
267
|
+
end
|
268
|
+
|
269
|
+
memo[locale] = locale_translations
|
270
|
+
memo
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
def process_translations(locale_path)
|
276
|
+
translations = File.exist?(locale_path) ? JSON.parse(File.read(locale_path)) : {}
|
277
|
+
translations['app'].delete('package') if translations.has_key?('app')
|
278
|
+
remove_zendesk_keys(translations)
|
279
|
+
end
|
280
|
+
|
281
|
+
def has_lib_js?
|
282
|
+
lib_files.any?
|
283
|
+
end
|
284
|
+
|
285
|
+
def has_manifest?
|
286
|
+
has_file?('manifest.json')
|
287
|
+
end
|
288
|
+
|
289
|
+
def has_requirements?
|
290
|
+
has_file?('requirements.json')
|
291
|
+
end
|
292
|
+
|
293
|
+
def has_banner?
|
294
|
+
has_file?('assets/banner.png')
|
295
|
+
end
|
296
|
+
|
297
|
+
def app_js
|
298
|
+
transpile(read_file('app.js'))
|
299
|
+
end
|
300
|
+
|
301
|
+
def transpile(code)
|
302
|
+
manifest_json['es2015'] ? Babel::Transpiler.transform(code)['code'] : code
|
303
|
+
end
|
304
|
+
|
305
|
+
def commonjs_modules
|
306
|
+
return {} unless has_lib_js?
|
307
|
+
|
308
|
+
lib_files.each_with_object({}) do |file, modules|
|
309
|
+
raise Validations::ValidationError.new('reserved_filename') if file.relative_path == 'lib/_root.js'
|
310
|
+
name = file.relative_path.gsub(/^lib\//, '')
|
311
|
+
content = file.read
|
312
|
+
modules[name] = transpile(content)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
def deep_merge_hash(h, another_h)
|
317
|
+
result_h = h.dup
|
318
|
+
another_h.each do |key, value|
|
319
|
+
if h.has_key?(key) && h[key].is_a?(Hash) && value.is_a?(Hash)
|
320
|
+
result_h[key] = deep_merge_hash(h[key], value)
|
321
|
+
else
|
322
|
+
result_h[key] = value
|
323
|
+
end
|
324
|
+
end
|
325
|
+
result_h
|
326
|
+
end
|
327
|
+
|
328
|
+
def read_file(path)
|
329
|
+
File.read(path_to(path))
|
330
|
+
end
|
331
|
+
|
332
|
+
def read_json(path)
|
333
|
+
file = read_file(path)
|
334
|
+
unless file.nil?
|
335
|
+
JSON.parse(file)
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|