zci 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []