bookwatch 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +7 -0
  2. data/bookwatch.gemspec +40 -0
  3. data/install_bin/bookwatch +5 -0
  4. data/lib/bookwatch/cli.rb +109 -0
  5. data/lib/bookwatch/code_example_reader.rb +95 -0
  6. data/lib/bookwatch/colorizer.rb +16 -0
  7. data/lib/bookwatch/commands/bind.rb +119 -0
  8. data/lib/bookwatch/commands/collection.rb +181 -0
  9. data/lib/bookwatch/commands/components/bind/directory_preparer.rb +33 -0
  10. data/lib/bookwatch/commands/components/bind/layout_preparer.rb +27 -0
  11. data/lib/bookwatch/commands/components/command_options.rb +45 -0
  12. data/lib/bookwatch/commands/components/imprint/directory_preparer.rb +24 -0
  13. data/lib/bookwatch/commands/generate.rb +92 -0
  14. data/lib/bookwatch/commands/imprint.rb +55 -0
  15. data/lib/bookwatch/commands/punch.rb +33 -0
  16. data/lib/bookwatch/commands/update_local_doc_repos.rb +42 -0
  17. data/lib/bookwatch/commands/watch.rb +100 -0
  18. data/lib/bookwatch/config/checkers/archive_menu_checker.rb +29 -0
  19. data/lib/bookwatch/config/checkers/ditamap_presence_checker.rb +27 -0
  20. data/lib/bookwatch/config/checkers/duplicate_section_name_checker.rb +33 -0
  21. data/lib/bookwatch/config/checkers/products_checker.rb +34 -0
  22. data/lib/bookwatch/config/checkers/repository_name_presence_checker.rb +34 -0
  23. data/lib/bookwatch/config/checkers/required_keys_checker.rb +18 -0
  24. data/lib/bookwatch/config/checkers/section_presence_checker.rb +15 -0
  25. data/lib/bookwatch/config/configuration.rb +119 -0
  26. data/lib/bookwatch/config/configuration_decorator.rb +54 -0
  27. data/lib/bookwatch/config/dita_config_generator.rb +61 -0
  28. data/lib/bookwatch/config/fetcher.rb +64 -0
  29. data/lib/bookwatch/config/imprint/configuration.rb +24 -0
  30. data/lib/bookwatch/config/product_config.rb +34 -0
  31. data/lib/bookwatch/config/section_config.rb +85 -0
  32. data/lib/bookwatch/config/validator.rb +33 -0
  33. data/lib/bookwatch/config/yaml_loader.rb +34 -0
  34. data/lib/bookwatch/css_link_checker.rb +67 -0
  35. data/lib/bookwatch/directory_helpers.rb +15 -0
  36. data/lib/bookwatch/dita_command_creator.rb +95 -0
  37. data/lib/bookwatch/dita_html_for_middleman_formatter.rb +45 -0
  38. data/lib/bookwatch/errors/programmer_mistake.rb +5 -0
  39. data/lib/bookwatch/html_document_manipulator.rb +21 -0
  40. data/lib/bookwatch/ingest/cloner_factory.rb +26 -0
  41. data/lib/bookwatch/ingest/destination_directory.rb +21 -0
  42. data/lib/bookwatch/ingest/git_accessor.rb +102 -0
  43. data/lib/bookwatch/ingest/git_cloner.rb +36 -0
  44. data/lib/bookwatch/ingest/local_filesystem_cloner.rb +66 -0
  45. data/lib/bookwatch/ingest/missing_working_copy.rb +27 -0
  46. data/lib/bookwatch/ingest/repo_identifier.rb +45 -0
  47. data/lib/bookwatch/ingest/section_repository.rb +49 -0
  48. data/lib/bookwatch/ingest/update_failure.rb +15 -0
  49. data/lib/bookwatch/ingest/update_success.rb +12 -0
  50. data/lib/bookwatch/ingest/working_copy.rb +36 -0
  51. data/lib/bookwatch/local_filesystem_accessor.rb +122 -0
  52. data/lib/bookwatch/middleman_runner.rb +48 -0
  53. data/lib/bookwatch/postprocessing/link_checker.rb +125 -0
  54. data/lib/bookwatch/postprocessing/redirection.rb +38 -0
  55. data/lib/bookwatch/preprocessing/dita_html_preprocessor.rb +91 -0
  56. data/lib/bookwatch/preprocessing/dita_pdf_preprocessor.rb +48 -0
  57. data/lib/bookwatch/preprocessing/link_to_site_gen_dir.rb +39 -0
  58. data/lib/bookwatch/preprocessing/preprocessor.rb +26 -0
  59. data/lib/bookwatch/server_director.rb +30 -0
  60. data/lib/bookwatch/sheller.rb +52 -0
  61. data/lib/bookwatch/streams/colorized_stream.rb +25 -0
  62. data/lib/bookwatch/streams/filter_stream.rb +22 -0
  63. data/lib/bookwatch/subnav/navigation_entries_from_html_toc.rb +59 -0
  64. data/lib/bookwatch/subnav/navigation_entries_from_markdown_root.rb +116 -0
  65. data/lib/bookwatch/subnav/pdf_config_creator.rb +50 -0
  66. data/lib/bookwatch/subnav/subnav_generator.rb +28 -0
  67. data/lib/bookwatch/subnav/subnav_generator_factory.rb +29 -0
  68. data/lib/bookwatch/subnav/template_creator.rb +71 -0
  69. data/lib/bookwatch/terminal.rb +19 -0
  70. data/lib/bookwatch/values/output_locations.rb +91 -0
  71. data/lib/bookwatch/values/product_info.rb +11 -0
  72. data/lib/bookwatch/values/section.rb +58 -0
  73. data/lib/bookwatch/values/subnav_template.rb +4 -0
  74. data/lib/bookwatch/values/user_message.rb +15 -0
  75. data/master_middleman/archive_drop_down_menu.rb +50 -0
  76. data/master_middleman/bookwatch_helpers.rb +259 -0
  77. data/master_middleman/compass_runner.rb +0 -0
  78. data/master_middleman/config.rb +34 -0
  79. data/master_middleman/quicklinks_renderer.rb +80 -0
  80. data/master_middleman/source/javascripts/all.js +2 -0
  81. data/master_middleman/source/javascripts/book.js +1 -0
  82. data/master_middleman/source/javascripts/bookwatch.js +103 -0
  83. data/master_middleman/source/layouts/_additional-scripts.erb +0 -0
  84. data/master_middleman/source/layouts/_book-footer.erb +0 -0
  85. data/master_middleman/source/layouts/_book-search.erb +0 -0
  86. data/master_middleman/source/layouts/_book-title.erb +3 -0
  87. data/master_middleman/source/layouts/_header.erb +34 -0
  88. data/master_middleman/source/layouts/_local-header.erb +0 -0
  89. data/master_middleman/source/layouts/_page-footer.erb +1 -0
  90. data/master_middleman/source/layouts/_title.erb +5 -0
  91. data/master_middleman/source/layouts/layout.erb +69 -0
  92. data/master_middleman/source/stylesheets/all.css.scss +3 -0
  93. data/master_middleman/source/stylesheets/base.scss +380 -0
  94. data/master_middleman/source/stylesheets/book-styles.css.scss +0 -0
  95. data/master_middleman/source/stylesheets/layout-styles.scss +0 -0
  96. data/master_middleman/source/stylesheets/partials/_book-base-values.scss +0 -0
  97. data/master_middleman/source/stylesheets/partials/_book-vars.scss +0 -0
  98. data/master_middleman/source/stylesheets/partials/_default.scss +300 -0
  99. data/master_middleman/source/stylesheets/partials/_footer.scss +64 -0
  100. data/master_middleman/source/stylesheets/partials/_header.scss +419 -0
  101. data/master_middleman/source/stylesheets/partials/_layout-vars.scss +0 -0
  102. data/master_middleman/source/stylesheets/partials/_mixins.scss +53 -0
  103. data/master_middleman/source/stylesheets/partials/_reset.scss +233 -0
  104. data/master_middleman/source/stylesheets/partials/_search.scss +78 -0
  105. data/master_middleman/source/stylesheets/partials/_sidenav.scss +191 -0
  106. data/master_middleman/source/stylesheets/partials/_syntax-highlight.scss +64 -0
  107. data/master_middleman/source/stylesheets/partials/_vars.scss +64 -0
  108. data/master_middleman/source/stylesheets/print.css.scss +58 -0
  109. data/master_middleman/source/subnavs/_default.erb +0 -0
  110. data/master_middleman/source/subnavs/_nav-links.erb +10 -0
  111. data/master_middleman/source/subnavs/_subnav_template.erb +8 -0
  112. data/master_middleman/subdirectory_aware_assets.rb +47 -0
  113. data/template_app/Gemfile +10 -0
  114. data/template_app/Gemfile.lock +43 -0
  115. data/template_app/config.ru +9 -0
  116. data/template_app/lib/rack_static_if_exists.rb +19 -0
  117. data/template_app/lib/search/handler.rb +47 -0
  118. data/template_app/lib/search/hit.rb +21 -0
  119. data/template_app/lib/search/query.rb +74 -0
  120. data/template_app/lib/search/renderer.rb +29 -0
  121. data/template_app/lib/server.rb +52 -0
  122. data/template_app/mail_sender.rb +69 -0
  123. data/template_app/rack_app.rb +110 -0
  124. data/template_app/search-results.html.erb +75 -0
  125. data/template_app/search.yml +22 -0
  126. metadata +491 -0
@@ -0,0 +1,29 @@
1
+ require_relative 'template_creator'
2
+ require_relative 'pdf_config_creator'
3
+
4
+ module Bookwatch
5
+ module Subnav
6
+ class SubnavGeneratorFactory
7
+ def initialize(fs, output_locations)
8
+ @fs = fs
9
+ @output_locations = output_locations
10
+ end
11
+
12
+ def produce(json_generator)
13
+ SubnavGenerator.new(json_generator, template_creator, pdf_config_creator, output_locations)
14
+ end
15
+
16
+ attr_reader :fs, :output_locations
17
+
18
+ private
19
+
20
+ def template_creator
21
+ @template_creator ||= TemplateCreator.new(fs, output_locations)
22
+ end
23
+
24
+ def pdf_config_creator
25
+ @pdf_config_creator ||= PdfConfigCreator.new(fs, output_locations)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,71 @@
1
+ require 'erb'
2
+ require 'rack/utils'
3
+
4
+ module Bookwatch
5
+ module Subnav
6
+ class TemplateCreator
7
+ def initialize(fs, output_locations)
8
+ @fs = fs
9
+ @output_locations = output_locations
10
+ end
11
+
12
+ def create(navigation_entries, subnav_spec)
13
+ template_content = fs.read(template_path)
14
+ links_template = ERB.new(fs.read(subnavs_path.join('_nav-links.erb')))
15
+
16
+ populated_nav = ERB.new(template_content).result(LinkHolder.new(navigation_entries, links_template).get_binding)
17
+
18
+ fs.write(text: populated_nav, to: subnav_destination(subnav_spec.subnav_name))
19
+ end
20
+
21
+ attr_reader :fs, :output_locations, :html_doc_manipulator
22
+
23
+ private
24
+
25
+ def subnavs_path
26
+ output_locations.subnavs_for_layout_dir
27
+ end
28
+
29
+ def filename(name)
30
+ "#{name}.erb"
31
+ end
32
+
33
+ def template_path
34
+ subnavs_path.join('_subnav_template.erb')
35
+ end
36
+
37
+ def subnav_destination(name)
38
+ subnavs_path.join(filename(name))
39
+ end
40
+
41
+ class LinkHolder
42
+ def initialize(links, template)
43
+ @links = links
44
+ @template = template
45
+ end
46
+
47
+ attr_reader :links
48
+
49
+ def get_binding
50
+ binding
51
+ end
52
+
53
+ def render_links(some_links)
54
+ @template.result(LinkHolder.new(some_links, @template).get_binding)
55
+ end
56
+
57
+ def submenu_class(link)
58
+ 'has_submenu' unless links?(link[:nested_links])
59
+ end
60
+
61
+ def links?(links)
62
+ (links || []).empty?
63
+ end
64
+
65
+ def escape_html(str)
66
+ Rack::Utils.escape_html(str)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,19 @@
1
+ require_relative 'colorizer'
2
+
3
+ module Bookwatch
4
+ class Terminal
5
+ def initialize(colorizer)
6
+ @colorizer = colorizer
7
+ end
8
+
9
+ def update(user_message)
10
+ if user_message.error?
11
+ error_message = @colorizer.colorize(user_message.message, Colorizer::Colors.red)
12
+ $stderr.puts error_message
13
+ elsif user_message.warn?
14
+ warning_message = @colorizer.colorize(user_message.message, Colorizer::Colors.yellow)
15
+ puts warning_message
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,91 @@
1
+ require_relative '../directory_helpers'
2
+ require_relative '../errors/programmer_mistake'
3
+
4
+ module Bookwatch
5
+ class OutputLocations
6
+ include DirectoryHelperMethods
7
+
8
+ def initialize(final_app_dir: nil, context_dir: nil)
9
+ @final_app_dir = final_app_dir
10
+ @context_dir = context_dir
11
+ end
12
+
13
+ def final_app_dir
14
+ Pathname(@final_app_dir)
15
+ end
16
+
17
+ def public_dir
18
+ final_app_dir.join('public')
19
+ end
20
+
21
+ def build_dir
22
+ master_dir.join('build/.')
23
+ end
24
+
25
+ def workspace_dir
26
+ master_dir.join('source')
27
+ end
28
+
29
+ def master_dir
30
+ output_dir.join('master_middleman')
31
+ end
32
+
33
+ def output_dir
34
+ context_dir.join(output_dir_name)
35
+ end
36
+
37
+ def preprocessing_home_dir
38
+ output_dir.join('preprocessing')
39
+ end
40
+
41
+ def cloned_preprocessing_dir
42
+ preprocessing_home_dir.join('sections')
43
+ end
44
+
45
+ def html_from_preprocessing_dir
46
+ preprocessing_home_dir.join('html_from_preprocessing')
47
+ end
48
+
49
+ def pdf_from_preprocessing_dir
50
+ preprocessing_home_dir.join('pdf_from_preprocessing')
51
+ end
52
+
53
+ def formatted_dir
54
+ preprocessing_home_dir.join('site_generator_ready')
55
+ end
56
+
57
+ def site_generator_home
58
+ output_dir.join('master_middleman')
59
+ end
60
+
61
+ def source_for_site_generator
62
+ site_generator_home.join('source')
63
+ end
64
+
65
+ def subnavs_for_layout_dir
66
+ source_for_site_generator.join('subnavs')
67
+ end
68
+
69
+ def pdf_config_dir
70
+ context_dir
71
+ end
72
+
73
+ def artifact_dir
74
+ context_dir.join('artifacts')
75
+ end
76
+
77
+ def pdf_artifact_dir
78
+ artifact_dir.join('pdfs')
79
+ end
80
+
81
+ private
82
+
83
+ def context_dir
84
+ if @context_dir.nil?
85
+ raise Errors::ProgrammerMistake.new("You must provide a context_dir to OutputLocations")
86
+ else
87
+ Pathname(@context_dir)
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,11 @@
1
+ module Bookwatch
2
+ ProductInfo = Struct.new(
3
+ :use_local_header,
4
+ :latest_stable_version,
5
+ :changelog_href,
6
+ :local_header_img,
7
+ :local_header_title
8
+ ) do
9
+
10
+ end
11
+ end
@@ -0,0 +1,58 @@
1
+ require_relative '../errors/programmer_mistake'
2
+ require_relative '../ingest/destination_directory'
3
+
4
+ module Bookwatch
5
+ Section = Struct.new(
6
+ :path_to_repository,
7
+ :full_name,
8
+ :desired_directory_name,
9
+ :subnav_templ,
10
+ :desired_subnav_name,
11
+ :preprocessor_config,
12
+ :at_repo_path,
13
+ :repo_name,
14
+ :source_ref,
15
+ :pdf_output_filename,
16
+ :product_info) do
17
+
18
+ def path_to_repo_dir
19
+ at_repo_path.nil? ? path_to_repository : path_to_repository.join(at_repo_path)
20
+ end
21
+
22
+ def subnav_template
23
+ subnav_templ.sub(/^_/, '').sub(/\.erb$/, '') if subnav_templ
24
+ end
25
+
26
+ def destination_directory
27
+ Ingest::DestinationDirectory.new(full_name, desired_directory_name)
28
+ end
29
+
30
+ def subnav
31
+ {namespace => subnav_name}
32
+ end
33
+
34
+ def namespace
35
+ destination_directory.to_s.gsub(%r{[./]}, '_')
36
+ end
37
+
38
+ def subnav_name
39
+ subnav_template || desired_subnav_name || 'default'
40
+ end
41
+
42
+ def path_to_preprocessor_attribute(attr)
43
+ path_to_repo_dir.join(preprocessor_config[attr]) if preprocessor_config[attr]
44
+ rescue NoMethodError => e
45
+ raise Errors::ProgrammerMistake.new(
46
+ "path_to_preprocessor_attribute assumes preprocessor_config is available, got nil.\n" +
47
+ "Original exception:\n\n#{e.inspect}\n\n#{e.backtrace.join("\n")}"
48
+ )
49
+ end
50
+
51
+ private
52
+
53
+ def path_to_repository
54
+ Pathname(self[:path_to_repository].to_s)
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,4 @@
1
+ module Bookwatch
2
+ SubnavTemplate = Struct.new(:json_links,
3
+ :text)
4
+ end
@@ -0,0 +1,15 @@
1
+ require 'ostruct'
2
+
3
+ module Bookwatch
4
+ UserMessage = Struct.new(:message, :escalation_type) do
5
+ def error?
6
+ escalation_type == EscalationType.error
7
+ end
8
+
9
+ def warn?
10
+ escalation_type == EscalationType.warn
11
+ end
12
+ end
13
+
14
+ EscalationType = OpenStruct.new(success: 0, error: 1, warn: 2)
15
+ end
@@ -0,0 +1,50 @@
1
+ module Bookwatch
2
+ class ArchiveDropDownMenu
3
+ def initialize(config, current_path: nil)
4
+ @config = config_with_default(config)
5
+ @current_path = current_path
6
+ end
7
+
8
+ def title
9
+ directory_config.first
10
+ end
11
+
12
+ def dropdown_links
13
+ versions_to_paths.map { |version_path|
14
+ {version_path.keys.first => "/#{version_path.values.first}"}
15
+ }
16
+ end
17
+
18
+ def empty?
19
+ title.nil? && dropdown_links.empty?
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :config, :current_path
25
+
26
+ def versions_to_paths
27
+ directory_config[1..-1]
28
+ end
29
+
30
+ def directory_config
31
+ config.fetch(directory, config.fetch(root_menu_reference)) || empty_menu
32
+ end
33
+
34
+ def directory
35
+ File.dirname(current_path)
36
+ end
37
+
38
+ def config_with_default(config)
39
+ { root_menu_reference => empty_menu }.merge(config || {})
40
+ end
41
+
42
+ def empty_menu
43
+ [nil]
44
+ end
45
+
46
+ def root_menu_reference
47
+ '.'
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,259 @@
1
+ require 'bookwatch/code_example_reader'
2
+ require 'bookwatch/ingest/cloner_factory'
3
+ require 'bookwatch/ingest/git_accessor'
4
+ require 'bookwatch/local_filesystem_accessor'
5
+ require 'date'
6
+ require_relative 'archive_drop_down_menu'
7
+ require_relative 'quicklinks_renderer'
8
+
9
+ I18n.enforce_available_locales = false
10
+
11
+ module Bookwatch
12
+ module Navigation
13
+ class << self
14
+ def registered(app)
15
+ app.helpers HelperMethods
16
+ end
17
+
18
+ alias :included :registered
19
+ end
20
+
21
+ module HelperMethods
22
+
23
+ def yield_for_code_snippet(from: nil, at: nil)
24
+ cloner_factory = Ingest::ClonerFactory.new({out: $stdout},
25
+ LocalFilesystemAccessor.new,
26
+ Ingest::GitAccessor.new)
27
+
28
+ cloner = cloner_factory.produce(config[:local_repo_dir])
29
+ code_example_reader = CodeExampleReader.new({out: $stdout},
30
+ LocalFilesystemAccessor.new)
31
+ working_copy = cloner.call(source_repo_name: from,
32
+ source_ref: 'master',
33
+ destination_parent_dir: config[:workspace])
34
+
35
+ snippet, language = code_example_reader.get_snippet_and_language_at(at, working_copy)
36
+
37
+ delimiter = '```'
38
+
39
+ snippet.prepend("#{delimiter}#{language}\n").concat("\n#{delimiter}")
40
+ end
41
+
42
+ def elastic_search?
43
+ !!config[:elastic_search]
44
+ end
45
+
46
+ def yield_for_subnav
47
+ partial "subnavs/#{subnav_template_name}"
48
+ end
49
+
50
+ def yield_for_archive_drop_down_menu
51
+ menu = ArchiveDropDownMenu.new(
52
+ config[:archive_menu],
53
+ current_path: current_page.path
54
+ )
55
+ unless menu.empty?
56
+ partial 'archive_menus/default', locals: { menu_title: menu.title,
57
+ dropdown_links: menu.dropdown_links }
58
+ end
59
+ end
60
+
61
+ def exclude_feedback
62
+ current_page.add_metadata({page: {feedback_disabled: true}})
63
+ end
64
+
65
+ def yield_for_feedback
66
+ partial 'layouts/feedback' if config[:feedback_enabled] && !current_page.metadata[:page][:feedback_disabled]
67
+ end
68
+
69
+ def exclude_repo_link
70
+ current_page.add_metadata({page: {repo_link_disabled: true}})
71
+ end
72
+
73
+ def render_repo_link
74
+ if config[:repo_link_enabled] && repo_url && !current_page.metadata[:page][:repo_link_disabled]
75
+ "<a id='repo-link' href='#{repo_url}'>View the source for this page in GitHub</a>"
76
+ end
77
+ end
78
+
79
+ def mermaid_diagram(&blk)
80
+ escaped_text = capture(&blk).gsub('-','\-')
81
+
82
+ @_out_buf.concat "<div class='mermaid'>#{escaped_text}</div>"
83
+ end
84
+
85
+ def modified_date(default_date: nil)
86
+ parsed_default_date = Time.parse(default_date).utc if default_date
87
+
88
+ date = page_last_modified_date || parsed_default_date
89
+
90
+ "Page last updated: <span data-behavior=\"DisplayModifiedDate\" data-modified-date=\"#{date.to_i}000\"></span>" if date
91
+ end
92
+
93
+ def breadcrumbs
94
+ page_chain = add_ancestors_of(current_page, [])
95
+ breadcrumbs = page_chain.map do |page|
96
+ make_breadcrumb(page, page == current_page)
97
+ end.compact
98
+ return if breadcrumbs.size < 2
99
+ return content_tag :ul, breadcrumbs.reverse.join(' '), class: 'breadcrumbs'
100
+ end
101
+
102
+ def vars
103
+ OpenStruct.new config[:template_variables]
104
+ end
105
+
106
+ def product_info
107
+ config[:product_info].fetch(template_key, {})
108
+ end
109
+
110
+ def quick_links
111
+ page_src = File.read(current_page.source_file)
112
+ quicklinks_renderer = QuicklinksRenderer.new(vars)
113
+ Redcarpet::Markdown.new(quicklinks_renderer).render(page_src)
114
+ end
115
+
116
+ def owners
117
+ html_resources = sitemap.resources.select { |r| r.path.end_with?('.html') }
118
+ html_resources.each.with_object({}) { |resource, owners|
119
+ owners[resource.path] = Array(resource.data['owner'])
120
+ }
121
+ end
122
+
123
+ def template_key
124
+ decreasingly_specific_namespaces.detect { |ns|
125
+ config[:subnav_templates].has_key?(ns)
126
+ }
127
+ end
128
+
129
+ def body_classes(path=current_path.dup, options={})
130
+ if path.is_a? Hash
131
+ options = path
132
+ path = current_path.dup
133
+ end
134
+ basename = File.basename(path)
135
+ dirname = File.dirname(path).gsub('.', '_')
136
+ page_classes(File.join(dirname, basename), options)
137
+ end
138
+
139
+ private
140
+
141
+ def subnav_template_name
142
+ if current_path == 'search.html'
143
+ config[:subnav_templates].values.first
144
+ else
145
+ config[:subnav_templates][template_key] || 'default'
146
+ end
147
+ end
148
+
149
+ def decreasingly_specific_namespaces
150
+ body_classes(numeric_prefix: numeric_class_prefix).
151
+ split(' ').reverse.drop(1).
152
+ map {|ns| ns.sub(/^#{numeric_class_prefix}/, '')}
153
+ end
154
+
155
+ def numeric_class_prefix
156
+ 'NUMERIC_CLASS_PREFIX'
157
+ end
158
+
159
+ def page_last_modified_date
160
+ git_accessor = Ingest::GitAccessor.new
161
+
162
+ current_date = if current_page.data.dita
163
+ git_accessor.author_date(preprocessing_path(current_page.source_file), dita: true)
164
+ else
165
+ git_accessor.author_date(current_page.source_file)
166
+ end
167
+
168
+ current_date.utc if current_date
169
+ end
170
+
171
+ def repo_url
172
+ nested_dir, filename = parse_out_nested_dir_and_filename
173
+
174
+ repo_dir = match_repo_dir(nested_dir)
175
+ page_repo_config = config[:repo_links][repo_dir]
176
+
177
+ if page_repo_config && page_repo_config['ref']
178
+ org_repo = Pathname(page_repo_config['repo'])
179
+ ref = Pathname(page_repo_config['ref'])
180
+ at_path = at_path(page_repo_config)
181
+ nested_dir = extract_nested_directory(nested_dir, repo_dir)
182
+
183
+ "http://github.com/#{org_repo.join(Pathname('tree'), ref, Pathname(nested_dir), at_path, source_file(filename))}"
184
+ end
185
+ end
186
+
187
+ def match_repo_dir(nested_dir)
188
+ config[:repo_links].keys
189
+ .select{ |key| nested_dir.match(/^#{key}/) }
190
+ .sort_by{ |key| key.length }
191
+ .last
192
+ end
193
+
194
+ def source_file(filename)
195
+ fs = LocalFilesystemAccessor.new
196
+
197
+ if current_page.data.dita
198
+ source_filename = "#{filename}.xml"
199
+
200
+ if fs.source_file_exists?(Pathname(preprocessing_path(current_page.source_file)).dirname,
201
+ source_filename)
202
+ source_filename
203
+ else
204
+ ''
205
+ end
206
+ else
207
+ "#{filename}.html.md.erb"
208
+ end
209
+ end
210
+
211
+ def preprocessing_path(current_source_path)
212
+ root_path, nested_repo_path = current_source_path.split('source')
213
+
214
+ root_path.gsub!('/output/master_middleman', '')
215
+
216
+ "#{root_path}output/preprocessing/sections#{nested_repo_path}"
217
+ end
218
+
219
+ def parse_out_nested_dir_and_filename
220
+ current_page.path
221
+ .match(/\/?(.*?)\/?([^\/]*)\.html$?/)
222
+ .captures
223
+ end
224
+
225
+ def extract_nested_directory(nested_dir, repo_dir)
226
+ nested_dir = nested_dir.gsub("#{repo_dir}", '')
227
+ nested_dir = nested_dir.sub('/', '') if nested_dir[0] == '/'
228
+
229
+ nested_dir
230
+ end
231
+
232
+ def at_path(page_repo_config)
233
+ path = page_repo_config['at_path'] || ""
234
+
235
+ Pathname(path)
236
+ end
237
+
238
+ def add_ancestors_of(page, ancestors)
239
+ if page
240
+ add_ancestors_of(page.parent, ancestors + [page])
241
+ else
242
+ ancestors
243
+ end
244
+ end
245
+
246
+ def make_breadcrumb(page, is_current_page)
247
+ return nil unless (text = page.data.breadcrumb || page.data.title)
248
+ if is_current_page
249
+ css_class = 'active'
250
+ link = content_tag :span, text
251
+ else
252
+ link = link_to(text, '/' + page.path)
253
+ end
254
+ content_tag :li, link, :class => css_class
255
+ end
256
+ end
257
+ end
258
+ end
259
+ ::Middleman::Extensions.register(:navigation, Bookwatch::Navigation)