zendesk_apps_support 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/LICENSE +13 -0
  2. data/README.md +3 -0
  3. data/config/locales/en.yml +26 -0
  4. data/config/locales/translations/zendesk_apps_support.yml +54 -0
  5. data/lib/zendesk_apps_support.rb +20 -0
  6. data/lib/zendesk_apps_support/app_file.rb +42 -0
  7. data/lib/zendesk_apps_support/app_version.rb +68 -0
  8. data/lib/zendesk_apps_support/default_styles.scss +21 -0
  9. data/lib/zendesk_apps_support/engine.rb +11 -0
  10. data/lib/zendesk_apps_support/i18n.rb +28 -0
  11. data/lib/zendesk_apps_support/package.rb +100 -0
  12. data/lib/zendesk_apps_support/sass_functions.rb +22 -0
  13. data/lib/zendesk_apps_support/src.js.erb +24 -0
  14. data/lib/zendesk_apps_support/stylesheet_compiler.rb +25 -0
  15. data/lib/zendesk_apps_support/validations/manifest.rb +48 -0
  16. data/lib/zendesk_apps_support/validations/source.rb +43 -0
  17. data/lib/zendesk_apps_support/validations/templates.rb +20 -0
  18. data/lib/zendesk_apps_support/validations/translations.rb +38 -0
  19. data/lib/zendesk_apps_support/validations/validation_error.rb +103 -0
  20. data/spec/app/app.css +6 -0
  21. data/spec/app/app.js +12 -0
  22. data/spec/app/assets/logo-small.png +0 -0
  23. data/spec/app/assets/logo.png +0 -0
  24. data/spec/app/manifest.json +11 -0
  25. data/spec/app/templates/layout.hdbs +11 -0
  26. data/spec/app/translations/en.json +5 -0
  27. data/spec/app_file_spec.rb +27 -0
  28. data/spec/app_version_spec.rb +61 -0
  29. data/spec/i18n_spec.rb +22 -0
  30. data/spec/package_spec.rb +84 -0
  31. data/spec/validations/jshint_error_spec.rb +17 -0
  32. data/spec/validations/manifest_spec.rb +39 -0
  33. data/spec/validations/source_spec.rb +21 -0
  34. data/spec/validations/templates_spec.rb +13 -0
  35. data/spec/validations/translations_spec.rb +58 -0
  36. data/spec/validations/validation_serialization_spec.rb +96 -0
  37. metadata +212 -0
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2012 Zendesk
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -0,0 +1,3 @@
1
+ ## Zendesk Apps Support
2
+
3
+ Classes to manage and validate zendesk apps
@@ -0,0 +1,26 @@
1
+ # This is a generated file. Please do not edit it.
2
+ ---
3
+ en:
4
+ txt:
5
+ apps:
6
+ admin:
7
+ error:
8
+ app_build:
9
+ jshint:
10
+ one: ! 'JSHint error in %{file}: %{errors}'
11
+ other: ! 'JSHint errors in %{file}: %{errors}'
12
+ manifest_not_json: manifest is not proper JSON. %{errors}
13
+ missing_manifest: Could not find manifest.json
14
+ manifest_keys:
15
+ missing:
16
+ one: ! 'Missing required field in manifest: %{missing_keys}'
17
+ other: ! 'Missing required fields in manifest: %{missing_keys}'
18
+ missing_source: Could not find app.js
19
+ style_in_template: <style> tag in %{template}. Use an app.css file instead.
20
+ invalid_default_locale: ! '%{defaultLocale} is not a valid defaultLocale.
21
+ Only two- and three-letter ISO 639 language codes are allowed.'
22
+ translation:
23
+ invalid_locale: ! '%{file} is not a valid locale. Only two- and three-letter
24
+ ISO 639 language codes are allowed.'
25
+ not_json: ! '%{file} is not valid JSON. %{errors}'
26
+ not_json_object: ! '%{file} is not a JSON object.'
@@ -0,0 +1,54 @@
1
+ # This file is contains translations in Zendesk's custom format. The rake task
2
+ # `i18n:standardize` will generate a version that is compatible with the i18n
3
+ # gem (and thus Rails). Both versions should be checked in.
4
+ title: "Apps Support"
5
+
6
+ parts:
7
+ - translation:
8
+ key: "txt.apps.admin.error.app_build.jshint.one"
9
+ title: "App builder job: JSHint error message"
10
+ value: "JSHint error in %{file}: %{errors}"
11
+ - translation:
12
+ key: "txt.apps.admin.error.app_build.jshint.other"
13
+ title: "App builder job: JSHint error messages"
14
+ value: "JSHint errors in %{file}: %{errors}"
15
+ - translation:
16
+ key: "txt.apps.admin.error.app_build.manifest_not_json"
17
+ title: "App builder job: manifest is invalid JSON error"
18
+ value: "manifest is not proper JSON. %{errors}"
19
+ - translation:
20
+ key: "txt.apps.admin.error.app_build.missing_manifest"
21
+ title: "App builder job: missing manifest error"
22
+ value: "Could not find manifest.json"
23
+ - translation:
24
+ key: "txt.apps.admin.error.app_build.manifest_keys.missing.one"
25
+ title: "App builder job: missing manifest fields error"
26
+ value: "Missing required field in manifest: %{missing_keys}"
27
+ - translation:
28
+ key: "txt.apps.admin.error.app_build.manifest_keys.missing.other"
29
+ title: "App builder job: missing manifest fields error"
30
+ value: "Missing required fields in manifest: %{missing_keys}"
31
+ - translation:
32
+ key: "txt.apps.admin.error.app_build.missing_source"
33
+ title: "App builder job: missing app.js error"
34
+ value: "Could not find app.js"
35
+ - translation:
36
+ key: "txt.apps.admin.error.app_build.style_in_template"
37
+ title: "App builder job: <style> tags in template error"
38
+ value: "<style> tag in %{template}. Use an app.css file instead."
39
+ - translation:
40
+ key: "txt.apps.admin.error.app_build.invalid_default_locale"
41
+ title: "App builder job: invalid default locale"
42
+ value: "%{defaultLocale} is not a valid default locale. Only two- and three-letter ISO 639 language codes are allowed."
43
+ - translation:
44
+ key: "txt.apps.admin.error.app_build.translation.invalid_locale"
45
+ title: "App builder job: invalid locale file name"
46
+ value: "%{file} is not a valid locale. Only two- and three-letter ISO 639 language codes are allowed."
47
+ - translation:
48
+ key: "txt.apps.admin.error.app_build.translation.not_json"
49
+ title: "App builder job: translation file is invalid json"
50
+ value: "%{file} is not valid JSON. %{errors}"
51
+ - translation:
52
+ key: "txt.apps.admin.error.app_build.translation.not_json_object"
53
+ title: "App builder job: translation file is not a JSON object"
54
+ value: "%{file} is not a JSON object."
@@ -0,0 +1,20 @@
1
+ module ZendeskAppsSupport
2
+
3
+ require 'zendesk_apps_support/sass_functions'
4
+ require 'zendesk_apps_support/engine'
5
+
6
+ autoload :AppFile, 'zendesk_apps_support/app_file'
7
+ autoload :I18n, 'zendesk_apps_support/i18n'
8
+ autoload :Package, 'zendesk_apps_support/package'
9
+ autoload :AppVersion, 'zendesk_apps_support/app_version'
10
+ autoload :StylesheetCompiler, 'zendesk_apps_support/stylesheet_compiler'
11
+
12
+ module Validations
13
+ autoload :ValidationError, 'zendesk_apps_support/validations/validation_error'
14
+ autoload :Manifest, 'zendesk_apps_support/validations/manifest'
15
+ autoload :Source, 'zendesk_apps_support/validations/source'
16
+ autoload :Templates, 'zendesk_apps_support/validations/templates'
17
+ autoload :Translations, 'zendesk_apps_support/validations/translations'
18
+ autoload :JSHintValidationError, 'zendesk_apps_support/validations/validation_error'
19
+ end
20
+ end
@@ -0,0 +1,42 @@
1
+ module ZendeskAppsSupport
2
+
3
+ class AppFile
4
+
5
+ attr_reader :relative_path
6
+
7
+ def initialize(package, relative_path)
8
+ @relative_path = relative_path
9
+ @file = File.new(package.root.join(relative_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
+ alias_method :to_s, :relative_path
21
+
22
+ def method_missing(sym, *args, &block)
23
+ if @file.respond_to?(sym)
24
+ @file.call(sym, *args, &block)
25
+ else
26
+ super
27
+ end
28
+ end
29
+
30
+ # Unless Ruby 1.9
31
+ def respond_to?(sym, include_private = false)
32
+ @file.respond_to?(sym, include_private) || super
33
+ end
34
+
35
+ # If Ruby 1.9
36
+ def respond_to_missing?(sym, include_private = false)
37
+ @file.respond_to_missing?(sym, include_private) || super
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,68 @@
1
+ require 'multi_json'
2
+
3
+ module ZendeskAppsSupport
4
+
5
+ # At any point in time, we support up to three versions:
6
+ # * deprecated -- we will still serve apps targeting the deprecated version,
7
+ # but newly created or updated apps CANNOT target it
8
+ # * current -- we will serve apps targeting the current version;
9
+ # newly created or updated apps SHOULD target it
10
+ # * future -- we will serve apps targeting the future version;
11
+ # newly created or updates apps MAY target it, but it
12
+ # may change without notice
13
+ class AppVersion
14
+
15
+ DEPRECATED = '0.4'.freeze
16
+ CURRENT = '0.5'.freeze
17
+ FUTURE = nil
18
+
19
+ TO_BE_SERVED = [ DEPRECATED, CURRENT, FUTURE ].compact.freeze
20
+ VALID_FOR_UPDATE = [ CURRENT, FUTURE ].compact.freeze
21
+
22
+ def initialize(version)
23
+ @version = version.to_s
24
+ @version.freeze
25
+ freeze
26
+ end
27
+
28
+ def servable?
29
+ TO_BE_SERVED.include?(@version)
30
+ end
31
+
32
+ def valid_for_update?
33
+ VALID_FOR_UPDATE.include?(@version)
34
+ end
35
+
36
+ def deprecated?
37
+ @version == DEPRECATED
38
+ end
39
+
40
+ def obsolete?
41
+ !servable?
42
+ end
43
+
44
+ def blank?
45
+ @version.nil? || @version == ''
46
+ end
47
+
48
+ def present?
49
+ !blank?
50
+ end
51
+
52
+ def to_s
53
+ @version
54
+ end
55
+
56
+ def to_json(*options)
57
+ MultiJson.encode(@version)
58
+ end
59
+
60
+ def ==(other)
61
+ @version == other.to_s
62
+ end
63
+
64
+ end
65
+
66
+ AppVersion.freeze
67
+
68
+ end
@@ -0,0 +1,21 @@
1
+ header {
2
+ border-bottom: 1px dotted #CCC; margin-bottom: 12px;
3
+
4
+ h3 { line-height: 30px; }
5
+ hr { margin-top: 0; }
6
+
7
+ .logo {
8
+ background: transparent app-asset-url("logo-small.png") no-repeat;
9
+ background-size: 25px 25px; float: right; height: 25px; width: 25px;
10
+ }
11
+
12
+ .app-warning-icon {
13
+ cursor: pointer;
14
+ float: right;
15
+ margin-left: 2px;
16
+ padding: 5px;
17
+ }
18
+ }
19
+
20
+ h3 { font-size: 14px; }
21
+ footer { background: none; border: 0 }
@@ -0,0 +1,11 @@
1
+ if Object.const_defined?(:Rails) && Rails.const_defined?(:Engine)
2
+
3
+ module ZendeskAppsSupport
4
+
5
+ class Engine < Rails::Engine
6
+ engine_name 'zendesk_apps_support'
7
+ end
8
+
9
+ end
10
+
11
+ 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,100 @@
1
+ require 'pathname'
2
+ require 'erubis'
3
+ require 'json'
4
+
5
+ module ZendeskAppsSupport
6
+ class Package
7
+
8
+ DEFAULT_SCSS = File.read(File.expand_path('../default_styles.scss', __FILE__))
9
+ SRC_TEMPLATE = Erubis::Eruby.new( File.read(File.expand_path('../src.js.erb', __FILE__)) )
10
+
11
+ attr_reader :root
12
+
13
+ def initialize(dir)
14
+ @root = Pathname.new(File.expand_path(dir))
15
+ end
16
+
17
+ def validate
18
+ Validations::Manifest.call(self) +
19
+ Validations::Source.call(self) +
20
+ Validations::Templates.call(self) +
21
+ Validations::Translations.call(self)
22
+ end
23
+
24
+ def files
25
+ non_tmp_files
26
+ end
27
+
28
+ def template_files
29
+ files.select { |f| f =~ /^templates\/.*\.hdbs$/ }
30
+ end
31
+
32
+ def translation_files
33
+ files.select { |f| f =~ /^translations\// }
34
+ end
35
+
36
+ def manifest_json
37
+ JSON.parse(File.read(File.join(root, "manifest.json")), :symbolize_names => true)
38
+ end
39
+
40
+ def readified_js(app_name, app_id, asset_url_prefix, settings={})
41
+ manifest = manifest_json
42
+ source = File.read(File.join(root, "app.js"))
43
+ name = app_name || manifest[:name] || 'Local App'
44
+ location = manifest[:location]
45
+ app_class_name = "app-#{app_id}"
46
+ author = manifest[:author]
47
+ translations = JSON.parse(File.read(File.join(root, "translations/en.json")))
48
+ framework_version = manifest[:frameworkVersion]
49
+ templates = compiled_templates(app_id, asset_url_prefix)
50
+
51
+ settings["title"] = name
52
+
53
+ SRC_TEMPLATE.result(
54
+ :name => name,
55
+ :source => source,
56
+ :location => location,
57
+ :asset_url_prefix => asset_url_prefix,
58
+ :app_class_name => app_class_name,
59
+ :author => author,
60
+ :translations => translations,
61
+ :framework_version => framework_version,
62
+ :templates => templates,
63
+ :settings => settings,
64
+ :app_id => app_id
65
+ )
66
+ end
67
+
68
+ private
69
+
70
+ def compiled_templates(app_id, asset_url_prefix)
71
+ css_file = File.join(root, "app.css")
72
+ customer_css = File.exist?(css_file) ? File.read(css_file) : ""
73
+ compiled_css = ZendeskAppsSupport::StylesheetCompiler.new(DEFAULT_SCSS + customer_css, app_id, asset_url_prefix).compile
74
+
75
+ templates = begin
76
+ Dir["#{root.to_s}/templates/*.hdbs"].inject({}) do |h, file|
77
+ str = File.read(file)
78
+ str.chomp!
79
+ h[File.basename(file, File.extname(file))] = str
80
+ h
81
+ end
82
+ end
83
+
84
+ templates.tap do |templates|
85
+ templates['layout'] = "<style>\n#{compiled_css}</style>\n#{templates['layout']}"
86
+ end
87
+ end
88
+
89
+ def non_tmp_files
90
+ files = []
91
+ Dir[ root.join('**/**') ].each do |f|
92
+ next unless File.file?(f)
93
+ relative_file_name = f.sub(/#{root}\/?/, '')
94
+ next if relative_file_name =~ /^tmp\//
95
+ files << AppFile.new(self, relative_file_name)
96
+ end
97
+ files
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,22 @@
1
+ require 'sass'
2
+
3
+ module Sass::Script::Functions
4
+
5
+ module AppAssetUrl
6
+ def app_asset_url(name)
7
+ assert_type name, :String
8
+ result = %Q{url("#{app_asset_url_helper(name)}")}
9
+ Sass::Script::String.new(result)
10
+ end
11
+
12
+ private
13
+
14
+ def app_asset_url_helper(name)
15
+ url_builder = options[:app_asset_url_builder]
16
+ url_builder.app_asset_url(name.value)
17
+ end
18
+ end
19
+
20
+ include AppAssetUrl
21
+
22
+ end
@@ -0,0 +1,24 @@
1
+ (function() {
2
+ with( require('apps/framework/app_scope') ) {
3
+
4
+ var source = <%= source %>;
5
+
6
+ ZendeskApps[<%= name.to_json %>] = ZendeskApps.defineApp(source)
7
+ .reopenClass({ location: <%= location.to_json %> })
8
+ .reopen({
9
+ assetUrlPrefix: <%= asset_url_prefix.to_json %>,
10
+ appClassName: <%= app_class_name.to_json %>,
11
+ author: {
12
+ name: <%= author[:name].to_json %>,
13
+ email: <%= author[:email].to_json %>
14
+ },
15
+ translations: <%= translations.to_json %>,
16
+ templates: <%= templates.to_json %>,
17
+ frameworkVersion: <%= framework_version.to_json %>
18
+ });
19
+
20
+ }
21
+
22
+ ZendeskApps[<%= name.to_json %>].install({"id": <%= app_id %>, "app_id": <%= app_id %>, "settings": <%= settings.to_json %>});
23
+
24
+ }());