zci 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4fb7673acae1967d9772186d2997a792219bf996
4
+ data.tar.gz: 7ff6daca96e2fd78ebebc4da556bac1fdc01dc42
5
+ SHA512:
6
+ metadata.gz: a277fb3302cde662f210b296a7e49e5df1d007130b02cb5c5e445e1f4cd8e0bdb3747bfac08220603060bf99c4e01eaf005533cd5deba5bcc279cb52ae30a08e
7
+ data.tar.gz: 2c1cb1046cb17af46a71b23caf6c90c5277aefab0f3608d0d4a7eba1399f13e5f3fb97324631977ca17439fa73a6e4dd45c4328ce5abfc5351017148db8635e1
@@ -0,0 +1,36 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+ /zci.yml
12
+
13
+ ## Specific to RubyMotion:
14
+ .dat*
15
+ .repl_history
16
+ build/
17
+
18
+ ## Documentation cache and generated files:
19
+ /.yardoc/
20
+ /_yardoc/
21
+ /doc/
22
+ /rdoc/
23
+
24
+ ## Environment normalisation:
25
+ /.bundle/
26
+ /vendor/bundle
27
+ /lib/bundler/man/
28
+
29
+ # for a library or gem, you might want to ignore these files since the code is
30
+ # intended to run in multiple environments; otherwise, check them in:
31
+ # Gemfile.lock
32
+ # .ruby-version
33
+ # .ruby-gemset
34
+
35
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
36
+ .rvmrc
@@ -0,0 +1 @@
1
+ zendesk_api
@@ -0,0 +1 @@
1
+ ruby-2.2.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ # gem 'zendesk_help_center_api', path: '../zendesk_help_center_api_client_rb'
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Anton Maminov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,122 @@
1
+ # ZCI
2
+
3
+ Zendesk and Crowdin integration Command Line Interface (CLI)
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```
10
+ gem 'zci'
11
+ ```
12
+
13
+ And then execute:
14
+ ```
15
+ $ bundle
16
+ ```
17
+
18
+ Or install it manually as:
19
+
20
+ ```
21
+ $ gem install zci
22
+ ```
23
+
24
+ ## Use
25
+
26
+ The simplest way to get started is to create a scaffold project:
27
+
28
+ ```
29
+ > zci init todo
30
+ ```
31
+
32
+ A new ./todo directory is created containing the sample config `zci.yml`. View the basic output of the scaffold with:
33
+
34
+ ```
35
+ > cd todo
36
+ > zci help
37
+ ```
38
+
39
+ Which will output:
40
+
41
+ ```
42
+ NAME
43
+ zci - is a command line tool that allows you to manage and synchronize your Zendesk localization with Crowdin project
44
+
45
+ SYNOPSIS
46
+ zci [global options] command [command options] [arguments...]
47
+
48
+ VERSION
49
+ 0.0.1
50
+
51
+ GLOBAL OPTIONS
52
+ -c, --config=<s> - Project-specific configuration file (default: /home/user/todo/zci.yml)
53
+ --version - Display the program version
54
+ -v, --verbose - Be verbose
55
+ --help - Show this message
56
+
57
+ COMMANDS
58
+ help - Shows a list of commands or help for one command
59
+ init:project - Create a new ZCI-based project
60
+ import:sources - Read categories/section/articles from Zendesk and upload resource files to Crowdin
61
+ download:translations - Build and download last exported translation resources from Crowdin
62
+ export:translations - Add or update localized resource files(sections and articles) in Zendesk
63
+ ```
64
+
65
+ ## Configuration
66
+
67
+ The scaffold project that was created in ./todo comes with a `zci.yml` shell.
68
+
69
+ ```
70
+ ---
71
+ # Crowdin API credentials
72
+ crowdin_project_id: '<%your-crowdin-project-id%>'
73
+ crowdin_api_key: '<%your-crowdin-api-key%>'
74
+ crowdin_base_url: 'https://api.crowdin.com'
75
+
76
+ # Zendesk API credentials
77
+ zendesk_base_url: 'https://<%subdomain%>.zendesk.com'
78
+ zendesk_username: '<%your-zendesk-username%>'
79
+ zendesk_password: '<%your-zendesk-password%>'
80
+
81
+ # Zendesk catogories
82
+ categories:
83
+ - zendesk_category: '<%zendesk-category-id%>'
84
+ translations:
85
+ -
86
+ crowdin_language_code: '<%crowdin-two-letters-code%>'
87
+ zendesk_locale: '<%zendesk-locale%>'
88
+ -
89
+ crowdin_language_code: '<%crowdin-two-letters-code%>'
90
+ zendesk_locale: '<%zendesk-locale%>'
91
+ - zendesk_category: '<%zendesk-category-id%>'
92
+ translations:
93
+ -
94
+ crowdin_language_code: '<%crowdin-two-letters-code%>'
95
+ zendesk_locale: '<%zendesk-locale%>'
96
+ -
97
+ crowdin_language_code: '<%crowdin-two-letters-code%>'
98
+ zendesk_locale: '<%zendesk-locale%>'
99
+ ```
100
+
101
+ ## Supported Rubies
102
+
103
+ Tested with the following Ruby versions:
104
+
105
+ - MRI 2.2.0
106
+ - JRuby 9.0.0.0.pre2
107
+
108
+ ## Contributing
109
+
110
+ 1. Fork it
111
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
112
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
113
+ 4. Push to the branch (`git push origin my-new-feature`)
114
+ 5. Create new Pull Request
115
+
116
+ ## License and Author
117
+
118
+ Author: Anton Maminov (anton.maminov@gmail.com)
119
+
120
+ Copyright: 2015 [crowdin.com](http://crowdin.com/)
121
+
122
+ This project is licensed under the MIT license, a copy of which can be found in the LICENSE file.
@@ -0,0 +1,44 @@
1
+ require 'rake/clean'
2
+ require 'rubygems'
3
+ require 'rubygems/package_task'
4
+ require 'rdoc/task'
5
+ require 'cucumber'
6
+ require 'cucumber/rake/task'
7
+ Rake::RDocTask.new do |rd|
8
+ rd.main = "README.rdoc"
9
+ rd.rdoc_files.include("README.rdoc","lib/**/*.rb","bin/**/*")
10
+ rd.title = 'Your application title'
11
+ end
12
+
13
+ spec = eval(File.read('zci.gemspec'))
14
+
15
+ Gem::PackageTask.new(spec) do |pkg|
16
+ end
17
+ CUKE_RESULTS = 'results.html'
18
+ CLEAN << CUKE_RESULTS
19
+ desc 'Run features'
20
+ Cucumber::Rake::Task.new(:features) do |t|
21
+ opts = "features --format html -o #{CUKE_RESULTS} --format progress -x"
22
+ opts += " --tags #{ENV['TAGS']}" if ENV['TAGS']
23
+ t.cucumber_opts = opts
24
+ t.fork = false
25
+ end
26
+
27
+ desc 'Run features tagged as work-in-progress (@wip)'
28
+ Cucumber::Rake::Task.new('features:wip') do |t|
29
+ tag_opts = ' --tags ~@pending'
30
+ tag_opts = ' --tags @wip'
31
+ t.cucumber_opts = "features --format html -o #{CUKE_RESULTS} --format pretty -x -s#{tag_opts}"
32
+ t.fork = false
33
+ end
34
+
35
+ task :cucumber => :features
36
+ task 'cucumber:wip' => 'features:wip'
37
+ task :wip => 'features:wip'
38
+ require 'rake/testtask'
39
+ Rake::TestTask.new do |t|
40
+ t.libs << "test"
41
+ t.test_files = FileList['test/*_test.rb']
42
+ end
43
+
44
+ task :default => [:test,:features]
data/bin/zci ADDED
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env ruby
2
+ require 'gli'
3
+ require 'zci'
4
+
5
+ include GLI::App
6
+ include ZCI
7
+
8
+ program_desc 'is a command line tool that allows you to manage and synchronize your Zendesk localization with Crowdin project'
9
+
10
+ sort_help :manually # help commands are ordered in the order declared
11
+
12
+ version ZCI::VERSION
13
+
14
+ subcommand_option_handling :normal
15
+ arguments :strict
16
+
17
+ desc 'Be verbose'
18
+ switch [:v, :verbose], negatable: false
19
+
20
+ desc 'Project-specific configuration file'
21
+ default_value File.join(Dir.pwd, 'zci.yml')
22
+ arg_name '<s>'
23
+ flag [:c, :config]
24
+
25
+ commands_from 'zci/commands'
26
+
27
+ pre do |global, command, options, args|
28
+ # Pre logic here
29
+ # Return true to proceed; false to abort and not call the
30
+ # chosen command
31
+ # Use skips_pre before a command to skip this block
32
+ # on that command only
33
+
34
+ unless File.exists?(global[:config])
35
+ exit_now! <<-EOS.strip_heredoc
36
+ Can't find configuration file (default `zci.yml').
37
+ Type `zci help` to know how to specify custom configuration file
38
+ EOS
39
+ end
40
+
41
+ # load project-specific configuration
42
+ begin
43
+ @cli_config = YAML.load(File.read(global[:config])) || {}
44
+ rescue Psych::SyntaxError => err
45
+ exit_now! <<-EOS
46
+ Could not parse YAML: #{err.message}
47
+ EOS
48
+ end
49
+
50
+ @zendesk = ZendeskAPI::Client.new do |config|
51
+ config.url = @cli_config['zendesk_base_url']
52
+ config.username = @cli_config['zendesk_username']
53
+ config.password = @cli_config['zendesk_password']
54
+
55
+ if global[:verbose]
56
+ require 'logger'
57
+ config.logger = Logger.new(STDOUT)
58
+ end
59
+ end
60
+
61
+ if global[:verbose]
62
+ Crowdin::API.log = Logger.new(STDOUT) if globals[:verbose]
63
+ end
64
+
65
+ @crowdin = Crowdin::API.new(api_key: @cli_config['crowdin_api_key'], project_id: @cli_config['crowdin_project_id'], base_url: @cli_config['crowdin_base_url'])
66
+
67
+ true
68
+ end
69
+
70
+ post do |global, command, options, args|
71
+ # Post logic here
72
+ # Use skips_post before a command to skip this
73
+ # block on that command only
74
+ end
75
+
76
+ on_error do |exception|
77
+ # Error logic here
78
+ # return false to skip default error handling
79
+ true
80
+ end
81
+
82
+ exit run(ARGV)
@@ -0,0 +1,8 @@
1
+ Feature: My bootstrapped app kinda works
2
+ In order to get going on coding my awesome app
3
+ I want to have aruba and cucumber setup
4
+ So I don't have to do it myself
5
+
6
+ Scenario: App just runs
7
+ When I get help for "zci"
8
+ Then the exit status should be 0
@@ -0,0 +1,6 @@
1
+ When /^I get help for "([^"]*)"$/ do |app_name|
2
+ @app_name = app_name
3
+ step %(I run `#{app_name} help`)
4
+ end
5
+
6
+ # Add more step definitions here
@@ -0,0 +1,15 @@
1
+ require 'aruba/cucumber'
2
+
3
+ ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
4
+ LIB_DIR = File.join(File.expand_path(File.dirname(__FILE__)),'..','..','lib')
5
+
6
+ Before do
7
+ # Using "announce" causes massive warnings on 1.9.2
8
+ @puts = true
9
+ @original_rubylib = ENV['RUBYLIB']
10
+ ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s
11
+ end
12
+
13
+ After do
14
+ ENV['RUBYLIB'] = @original_rubylib
15
+ end
@@ -0,0 +1,18 @@
1
+ require 'zci/version.rb'
2
+
3
+ require 'zendesk_api'
4
+ require 'zendesk_api/help_center'
5
+
6
+ require 'crowdin-api'
7
+ require 'nokogiri'
8
+ require 'zip'
9
+
10
+ # require 'byebug'
11
+
12
+ # Add requires for other files you add to your project here, so
13
+ # you just need to require this one file in your bin file
14
+ require 'zci/helpers.rb'
15
+ require 'zci/init.rb'
16
+ require 'zci/import.rb'
17
+ require 'zci/download.rb'
18
+ require 'zci/export.rb'
@@ -0,0 +1,28 @@
1
+ desc 'Create a new ZCI-based project'
2
+ arg 'project_name'
3
+ arg_name 'project_name'
4
+ skips_pre
5
+ command :'init:project' do |c|
6
+ c.desc 'Root dir of project'
7
+ c.long_desc <<-EOS.strip_heredoc
8
+ This is the directory where the project's directory will be made, so if you
9
+ specify a project name `foo` and the root dir of `.`, the directory
10
+ `./foo` will be created
11
+ EOS
12
+ c.flag :r, :root, :default_value => '.'
13
+
14
+ c.switch :force, :desc => 'Overwrite/ignore existing files and directories'
15
+
16
+ c.action do |global_options, options, args|
17
+ if args.length < 1
18
+ raise 'You must specify the name of your project'
19
+ end
20
+
21
+ root_dir = options[:root]
22
+ force = options[:force]
23
+ project_name = args.first
24
+
25
+ create_scaffold(root_dir, project_name, force)
26
+ end
27
+ end
28
+
@@ -0,0 +1,123 @@
1
+ desc 'Read categories/section/articles from Zendesk and upload resource files to Crowdin'
2
+ command :'import:sources' do |c|
3
+ c.desc 'Directory of resource files'
4
+ c.long_desc <<-EOS.strip_heredoc
5
+ This is the directory where the project's files will be store.
6
+ EOS
7
+ c.default_value 'resources'
8
+ c.arg_name 'dir'
9
+ c.flag [:resources_dir]
10
+
11
+ c.action do |global_options, options, args|
12
+ resources_dir = File.join(File.dirname(global_options[:config]), options[:resources_dir])
13
+
14
+ unless File.exists?(resources_dir)
15
+ FileUtils.mkdir(resources_dir)
16
+ end
17
+
18
+ @cli_config['categories'].each do |category|
19
+ # Source Category
20
+ source_category_id = category['zendesk_category'].to_i
21
+
22
+ # Check if Category exists in Zendesk
23
+ source_category = @zendesk.hc_categories.find(id: source_category_id)
24
+ raise('No such category') unless source_category.id == source_category_id
25
+
26
+ # Get category's sections in Zendesk
27
+ puts "[Zendesk] Get sections for Category with id #{source_category_id}"
28
+ sections = source_category.sections
29
+
30
+ sections_builder = []
31
+ sections.each do |section|
32
+ section_xml = build_section_xml(section)
33
+
34
+ unless section_xml.nil?
35
+ sections_builder << build_section_hash(section).merge({ xml: section_xml })
36
+ end
37
+ end
38
+
39
+ # Get articles for each section
40
+ articles_builder = []
41
+ sections.each do |section|
42
+ puts "[Zendesk] Get articles for Section with id #{section.id}"
43
+ articles = section.articles
44
+
45
+ articles.each do |article|
46
+ article_xml = build_article_xml(article)
47
+
48
+ unless article_xml.nil?
49
+ articles_builder << build_article_hash(article).merge({ xml: article_xml })
50
+ end
51
+ end
52
+ end
53
+
54
+ crowdin_project_info = @crowdin.project_info
55
+ remote_project_tree = get_remote_files_hierarchy(crowdin_project_info['files'])
56
+
57
+ resources_category_dir = File.join(resources_dir, source_category_id.to_s)
58
+ unless File.exists?(resources_category_dir)
59
+ FileUtils.mkdir(resources_category_dir)
60
+ end
61
+
62
+ # Create directory for Category on Crowdin if it does not exist yet
63
+ unless remote_project_tree[:dirs].include?("/#{source_category_id}")
64
+ puts "[Crowdin] Create directory `#{source_category_id}`"
65
+ @crowdin.add_directory(source_category_id.to_s)
66
+ @crowdin.change_directory(source_category_id.to_s, title: source_category.attributes[:name])
67
+ end
68
+
69
+ # Creates xml files for sections and upload to Crowdin
70
+ sections_builder.each do |section|
71
+ file_name = "section_#{section[:id]}.xml"
72
+
73
+ o = File.new(File.join(resources_category_dir, file_name), 'w')
74
+ o.write section[:xml].to_xml
75
+ o.close
76
+
77
+ files = [
78
+ {
79
+ source: File.join(resources_category_dir, file_name),
80
+ dest: File.join(source_category_id.to_s, file_name),
81
+ export_pattert: '/%two_letters_code%/%original_path%/%original_file_name%',
82
+ title: section[:name]
83
+ }
84
+ ]
85
+
86
+ if remote_project_tree[:files].include?("/#{source_category_id}/#{file_name}")
87
+ puts "[Crowdin] Update file `#{file_name}`"
88
+ @crowdin.update_file(files, type: 'webxml')
89
+ else
90
+ puts "[Crowdin] Add file `#{file_name}`"
91
+ @crowdin.add_file(files, type: 'webxml')
92
+ end
93
+ end
94
+
95
+ # Creates xml files for articles and upload to Crowdin
96
+ articles_builder.each do |article|
97
+ file_name = "article_#{article[:id]}.xml"
98
+
99
+ o = File.new(File.join(resources_category_dir, file_name), 'w')
100
+ o.write article[:xml].to_xml
101
+ o.close
102
+
103
+ files = [
104
+ {
105
+ source: File.join(resources_category_dir, file_name),
106
+ dest: File.join(source_category_id.to_s, file_name),
107
+ export_pattert: '/%two_letters_code%/%original_path%/%original_file_name%',
108
+ title: article[:title]
109
+ }
110
+ ]
111
+
112
+ if remote_project_tree[:files].include?("/#{source_category_id}/#{file_name}")
113
+ puts "[Crowdin] Update file `#{file_name}`"
114
+ @crowdin.update_file(files, type: 'webxml')
115
+ else
116
+ puts "[Crowdin] Add file `#{file_name}`"
117
+ @crowdin.add_file(files, type: 'webxml')
118
+
119
+ end
120
+ end
121
+ end # @cli_config['categories'].each
122
+ end
123
+ end
@@ -0,0 +1,31 @@
1
+ desc 'Build and download last exported translation resources from Crowdin'
2
+ command :'download:translations' do |c|
3
+ c.desc 'Directory of resource files'
4
+ c.long_desc <<-EOS.strip_heredoc
5
+ This is the directory where the project's files will be store.
6
+ EOS
7
+ c.default_value 'resources'
8
+ c.arg_name 'dir'
9
+ c.flag [:resources_dir]
10
+
11
+ c.action do |global_options, options, args|
12
+ resources_dir = File.join(File.dirname(global_options[:config]), options[:resources_dir])
13
+
14
+ language = 'all'
15
+ tempfile = Tempfile.new(language)
16
+ zipfile_name = tempfile.path
17
+
18
+ begin
19
+ export_translations!(@crowdin)
20
+
21
+ puts 'Downloading translations'
22
+ @crowdin.download_translation(language, output: zipfile_name)
23
+
24
+ unzip_file_with_translations(zipfile_name, resources_dir)
25
+ ensure
26
+ tempfile.close
27
+ tempfile.unlink # delete the tempfile
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,76 @@
1
+ desc 'Add or update localized resource files(sections and articles) in Zendesk'
2
+ command :'export:translations' do |c|
3
+ c.desc 'Directory of resource files'
4
+ c.long_desc <<-EOS.strip_heredoc
5
+ This is the directory where the project's files will be store.
6
+ EOS
7
+ c.default_value 'resources'
8
+ c.arg_name 'dir'
9
+ c.flag [:resources_dir]
10
+
11
+ c.action do |global_options, options, args|
12
+ resources_dir = File.join(File.dirname(global_options[:config]), options[:resources_dir])
13
+
14
+ @cli_config['categories'].each do |category|
15
+ @zendesk.locales.select { |locale| !locale.default? }.each do |locale|
16
+ if lang = category['translations'].detect { |tr| tr['zendesk_locale'] == locale.locale }
17
+ section_xml_files = Dir["#{resources_dir}/#{lang['crowdin_language_code']}/#{category['zendesk_category']}/section_*.xml"]
18
+ article_xml_files = Dir["#{resources_dir}/#{lang['crowdin_language_code']}/#{category['zendesk_category']}/article_*.xml"]
19
+
20
+ all_sections = []
21
+ all_articles = []
22
+
23
+ # Read sections from XML files
24
+ section_xml_files.each do |file|
25
+ section_xml_file = File.read(file)
26
+ section = parse_section_xml(section_xml_file)
27
+ all_sections << section
28
+ end
29
+
30
+ # Read articles from XML filse
31
+ article_xml_files.each do |file|
32
+ article_xml_file = File.read(file)
33
+ article = parse_article_xml(article_xml_file)
34
+ all_articles << article
35
+ end
36
+
37
+ ### Sections
38
+ #
39
+ sections = @zendesk.sections
40
+ all_sections.each do |section_hash|
41
+ if section = sections.find(id: section_hash[:id])
42
+ if section_tr = section.translations.detect { |tr| tr.locale == locale.locale }
43
+ puts "[Zendesk] Update `#{lang['crowdin_language_code']}` language translation for Section\##{section.id}."
44
+ section_tr = section_tr.update(name: section_hash[:name], description: section_hash[:description])
45
+ section_tr.save
46
+ else
47
+ puts "[Zendesk] Create `#{lang['crowdin_language_code']}` language translation for Section\##{section.id}."
48
+ section_tr = section.translations.build(locale: locale.locale, name: section_hash[:name], description: section_hash[:description])
49
+ section_tr.save
50
+ end
51
+ end
52
+ end
53
+
54
+ ### Articles
55
+ #
56
+ all_articles.each do |article_hash|
57
+ if section = sections.find(id: article_hash[:section_id])
58
+ if article = section.articles.find(id: article_hash[:id])
59
+ if article_tr = article.translations.detect { |tr| tr.locale == locale.locale }
60
+ puts "[Zendesk] Update `#{lang['crowdin_language_code']}` language translation for Article\##{article.id}."
61
+ article_tr = article_tr.update(title: article_hash[:title], body: article_hash[:body])
62
+ article_tr.save
63
+ else
64
+ puts "[Zendesk] Create `#{lang['crowdin_language_code']}` language translation for Article\##{article.id}."
65
+ article_tr = article.translations.build(locale: locale.locale, title: article_hash[:title], body: article_hash[:body])
66
+ article_tr.save
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ end
73
+ end # @zendesk.locales
74
+ end # @cli_config['categories'].each
75
+ end
76
+ end
@@ -0,0 +1,31 @@
1
+ module ZCI
2
+ def unzip_file_with_translations(zipfile_name, dest_path)
3
+ # overwrite files if they already exist inside of the extracted path
4
+ Zip.on_exists_proc = true
5
+
6
+ Zip::File.open(zipfile_name) do |zip_file|
7
+ zip_file.select { |zip_entry| zip_entry.file? }.each do |f|
8
+ # `f' - relative path in archive
9
+ fpath = File.join(dest_path, f.name)
10
+ FileUtils.mkdir_p(File.dirname(fpath))
11
+ puts "Extracting: `#{dest_path}/#{f.name}'"
12
+ zip_file.extract(f, fpath)
13
+ end
14
+ end
15
+ end
16
+
17
+ # use export API method before to download the most recent translations
18
+ def export_translations!(crowdin)
19
+ print 'Building ZIP archive with the latest translations '
20
+ export_translations = crowdin.export_translations
21
+ if export_translations['success']
22
+ if export_translations['success']['status'] == 'built'
23
+ puts "- OK"
24
+ elsif export_translations['success']['status'] == 'skipped'
25
+ puts "- Skipped"
26
+ puts "Warning: Export was skipped. Please note that this method can be invoked only once per 30 minutes."
27
+ end
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,32 @@
1
+ module ZCI
2
+ def parse_section_xml(xml_file)
3
+ doc = Nokogiri::XML.parse(xml_file)
4
+ section_xml = doc.xpath("//section").first
5
+
6
+ section = {
7
+ id: section_xml[:id],
8
+ category_id: section_xml[:category_id],
9
+ name: section_xml.xpath('name').text,
10
+ description: section_xml.xpath('description').text,
11
+ position: section_xml[:position],
12
+ }
13
+
14
+ return section
15
+ end
16
+
17
+ def parse_article_xml(xml_file)
18
+ doc = Nokogiri::XML.parse(xml_file)
19
+ article_xml = doc.xpath('//article').first
20
+
21
+ article = {
22
+ id: article_xml[:id],
23
+ section_id: article_xml[:section_id],
24
+ title: article_xml.xpath('title').text,
25
+ body: article_xml.xpath('body').text,
26
+ position: article_xml[:position],
27
+ }
28
+
29
+ return article
30
+ end
31
+
32
+ end
@@ -0,0 +1,24 @@
1
+ class String
2
+ def strip_heredoc
3
+ indent = scan(/^[ \t]*(?=\S)/).min.size || 0
4
+ gsub(/^[ \t]{#{indent}}/, '')
5
+ end
6
+ end
7
+
8
+ # Return +hierarchy+ of directories and files in Crowdin project
9
+ #
10
+ # +files+ - basically, it's project files details from API method `project_info`
11
+ #
12
+ def get_remote_files_hierarchy(files, root = '/', hierarchy = { dirs: [], files: [] })
13
+ files.each do |node|
14
+ case node['node_type']
15
+ when 'directory'
16
+ hierarchy[:dirs] << "#{root}#{node['name']}"
17
+ get_remote_files_hierarchy(node['files'], root + node['name'] + '/', hierarchy)
18
+ when 'file'
19
+ hierarchy[:files] << "#{root}#{node['name']}"
20
+ end
21
+ end
22
+
23
+ return hierarchy
24
+ end
@@ -0,0 +1,60 @@
1
+ module ZCI
2
+ def build_section_xml(section)
3
+ section_xml = Nokogiri::XML::Builder.new do |xml|
4
+ xml.root {
5
+ # id - id of the original section
6
+ # category_id - id of the original category
7
+ xml.section(id: section.id, category_id: section.category_id, position: section.position, identifier: 'section', type: 'document') {
8
+ xml.name {
9
+ xml.cdata section.name
10
+ }
11
+ xml.description {
12
+ xml.cdata section.description
13
+ }
14
+ }
15
+ }
16
+ end
17
+
18
+ return section_xml
19
+ end
20
+
21
+ def build_section_hash(section)
22
+ return {
23
+ id: section.id,
24
+ category_id: section.category_id,
25
+ position: section.position,
26
+ name: section.name,
27
+ description: section.description,
28
+ }
29
+ end
30
+
31
+ def build_article_xml(article)
32
+ article_xml = Nokogiri::XML::Builder.new do |xml|
33
+ xml.root {
34
+ # id - id of the original acticle
35
+ # section_id - id of the original section
36
+ xml.article(id: article.id, section_id: article.section_id, position: article.position, identifier: 'article', type: 'document') {
37
+ xml.title {
38
+ xml.cdata article.title
39
+ }
40
+ xml.body {
41
+ xml.cdata article.body
42
+ }
43
+ }
44
+ }
45
+ end
46
+
47
+ return article_xml
48
+ end
49
+
50
+ def build_article_hash(article)
51
+ return {
52
+ id: article.id,
53
+ section_id: article.section_id,
54
+ position: article.position,
55
+ title: article.title,
56
+ body: article.body,
57
+ }
58
+ end
59
+
60
+ end
@@ -0,0 +1,71 @@
1
+ require 'fileutils'
2
+
3
+ module ZCI
4
+ def create_scaffold(root_dir, project_name, force)
5
+ dir = File.join(root_dir, project_name)
6
+
7
+ if mkdir(dir, force)
8
+ mk_config(root_dir, project_name)
9
+ end
10
+ end
11
+
12
+ def mkdir(dir, force)
13
+ exists = false
14
+ if !force
15
+ if File.exist? dir
16
+ raise "#{dir} exists; use --force to override"
17
+ exists = true
18
+ end
19
+ end
20
+
21
+ if !exists
22
+ puts "Creating dir #{dir}..."
23
+ FileUtils.mkdir_p dir
24
+ else
25
+ puts "Exiting..."
26
+ false
27
+ end
28
+
29
+ true
30
+ end
31
+
32
+ def mk_config(root_dir, project_name)
33
+ config = <<-EOS.strip_heredoc
34
+ ---
35
+ # Crowdin API credentials
36
+ crowdin_project_id: '<%your-crowdin-project-id%>'
37
+ crowdin_api_key: '<%your-crowdin-api-key%>'
38
+ crowdin_base_url: 'https://api.crowdin.com'
39
+
40
+ # Zendesk API credentials
41
+ zendesk_base_url: 'https://<%subdomain%>.zendesk.com'
42
+ zendesk_username: '<%your-zendesk-username%>'
43
+ zendesk_password: '<%your-zendesk-password%>'
44
+
45
+ # Zendesk catogories
46
+ categories:
47
+ - zendesk_category: '<%zendesk-category-id%>'
48
+ translations:
49
+ -
50
+ crowdin_language_code: '<%crowdin-two-letters-code%>'
51
+ zendesk_locale: '<%zendesk-locale%>'
52
+ -
53
+ crowdin_language_code: '<%crowdin-two-letters-code%>'
54
+ zendesk_locale: '<%zendesk-locale%>'
55
+ - zendesk_category: '<%zendesk-category-id%>'
56
+ translations:
57
+ -
58
+ crowdin_language_code: '<%crowdin-two-letters-code%>'
59
+ zendesk_locale: '<%zendesk-locale%>'
60
+ -
61
+ crowdin_language_code: '<%crowdin-two-letters-code%>'
62
+ zendesk_locale: '<%zendesk-locale%>'
63
+ EOS
64
+
65
+ File.open("#{root_dir}/#{project_name}/zci.yml", 'w') do |file|
66
+ file << config
67
+ end
68
+
69
+ puts "Created #{root_dir}/#{project_name}/zci.yml"
70
+ end
71
+ end
@@ -0,0 +1,3 @@
1
+ module ZCI
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,14 @@
1
+ require 'test_helper'
2
+
3
+ class DefaultTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ end
7
+
8
+ def teardown
9
+ end
10
+
11
+ def test_the_truth
12
+ assert true
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ require 'test/unit'
2
+
3
+ # Add test libraries you want to use here, e.g. mocha
4
+
5
+ class Test::Unit::TestCase
6
+
7
+ # Add global extensions to the test case class here
8
+ end
@@ -0,0 +1,28 @@
1
+ # Ensure we require the local version and not one we might have installed already
2
+ require File.join([File.dirname(__FILE__),'lib','zci','version.rb'])
3
+ spec = Gem::Specification.new do |s|
4
+ s.name = 'zci'
5
+ s.version = ZCI::VERSION
6
+ s.author = 'Anton Maminov'
7
+ s.email = 'anton.maminov@gmail.com'
8
+ s.homepage = 'https://github.com/mamantoha/zci'
9
+ s.platform = Gem::Platform::RUBY
10
+ s.summary = 'Zendesk and Crowdin integration Command Line Interface (CLI)'
11
+ s.files = `git ls-files`.split("\n")
12
+ s.require_paths << 'lib'
13
+ # s.has_rdoc = true
14
+ # s.extra_rdoc_files = ['README.rdoc','zci.rdoc']
15
+ # s.rdoc_options << '--title' << 'zci' << '--main' << 'README.rdoc' << '-ri'
16
+ s.bindir = 'bin'
17
+ s.executables << 'zci'
18
+ s.add_runtime_dependency('nokogiri')
19
+ s.add_runtime_dependency('rubyzip')
20
+ s.add_runtime_dependency('crowdin-api')
21
+ s.add_runtime_dependency('zendesk_api')
22
+ s.add_runtime_dependency('zendesk_help_center_api')
23
+ s.add_runtime_dependency('gli','2.13.0')
24
+ s.add_development_dependency('rake')
25
+ s.add_development_dependency('rdoc')
26
+ s.add_development_dependency('aruba')
27
+ s.add_development_dependency('byebug')
28
+ end
@@ -0,0 +1,29 @@
1
+ ---
2
+ # Crowdin API credentials
3
+ crowdin_project_id: '<%your-crowdin-project-id%>'
4
+ crowdin_api_key: '<%your-crowdin-api-key%>'
5
+ crowdin_base_url: 'https://api.crowdin.com'
6
+
7
+ # Zendesk API credentials
8
+ zendesk_base_url: 'https://<%subdomain%>.zendesk.com'
9
+ zendesk_username: '<%your-zendesk-username%>'
10
+ zendesk_password: '<%your-zendesk-password%>'
11
+
12
+ # Zendesk catogories
13
+ categories:
14
+ - zendesk_category: '<%zendesk-category-id%>'
15
+ translations:
16
+ -
17
+ crowdin_language_code: '<%crowdin-two-letters-code%>'
18
+ zendesk_locale: '<%zendesk-locale%>'
19
+ -
20
+ crowdin_language_code: '<%crowdin-two-letters-code%>'
21
+ zendesk_locale: '<%zendesk-locale%>'
22
+ - zendesk_category: '<%zendesk-category-id%>'
23
+ translations:
24
+ -
25
+ crowdin_language_code: '<%crowdin-two-letters-code%>'
26
+ zendesk_locale: '<%zendesk-locale%>'
27
+ -
28
+ crowdin_language_code: '<%crowdin-two-letters-code%>'
29
+ zendesk_locale: '<%zendesk-locale%>'
metadata ADDED
@@ -0,0 +1,210 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zci
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Anton Maminov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rubyzip
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: crowdin-api
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: zendesk_api
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: zendesk_help_center_api
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: gli
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '='
88
+ - !ruby/object:Gem::Version
89
+ version: 2.13.0
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '='
95
+ - !ruby/object:Gem::Version
96
+ version: 2.13.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rdoc
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: aruba
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: byebug
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ description:
154
+ email: anton.maminov@gmail.com
155
+ executables:
156
+ - zci
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - ".gitignore"
161
+ - ".ruby-gemset"
162
+ - ".ruby-version"
163
+ - Gemfile
164
+ - LICENSE
165
+ - README.md
166
+ - Rakefile
167
+ - bin/zci
168
+ - features/fci.feature
169
+ - features/step_definitions/fci_steps.rb
170
+ - features/support/env.rb
171
+ - lib/zci.rb
172
+ - lib/zci/commands/01_init.rb
173
+ - lib/zci/commands/02_import.rb
174
+ - lib/zci/commands/03_download.rb
175
+ - lib/zci/commands/04_export.rb
176
+ - lib/zci/download.rb
177
+ - lib/zci/export.rb
178
+ - lib/zci/helpers.rb
179
+ - lib/zci/import.rb
180
+ - lib/zci/init.rb
181
+ - lib/zci/version.rb
182
+ - test/default_test.rb
183
+ - test/test_helper.rb
184
+ - zci.gemspec
185
+ - zci.yml.example
186
+ homepage: https://github.com/mamantoha/zci
187
+ licenses: []
188
+ metadata: {}
189
+ post_install_message:
190
+ rdoc_options: []
191
+ require_paths:
192
+ - lib
193
+ - lib
194
+ required_ruby_version: !ruby/object:Gem::Requirement
195
+ requirements:
196
+ - - ">="
197
+ - !ruby/object:Gem::Version
198
+ version: '0'
199
+ required_rubygems_version: !ruby/object:Gem::Requirement
200
+ requirements:
201
+ - - ">="
202
+ - !ruby/object:Gem::Version
203
+ version: '0'
204
+ requirements: []
205
+ rubyforge_project:
206
+ rubygems_version: 2.4.7
207
+ signing_key:
208
+ specification_version: 4
209
+ summary: Zendesk and Crowdin integration Command Line Interface (CLI)
210
+ test_files: []