olelo 0.9.0

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.
Files changed (282) hide show
  1. data/.gitignore +10 -0
  2. data/.gitmodules +3 -0
  3. data/Gemfile +3 -0
  4. data/README.markdown +104 -0
  5. data/Rakefile +92 -0
  6. data/bin/olelo +5 -0
  7. data/config.ru +86 -0
  8. data/config/aspects.rb +256 -0
  9. data/config/config.yml.default +154 -0
  10. data/config/initializers/00-mime_types.rb +29 -0
  11. data/config/initializers/01-slim.rb +2 -0
  12. data/config/interwiki.yml +11 -0
  13. data/doc/AUTHORS +7 -0
  14. data/doc/LICENSE +22 -0
  15. data/lib/olelo.rb +39 -0
  16. data/lib/olelo/application.rb +294 -0
  17. data/lib/olelo/attributes.rb +285 -0
  18. data/lib/olelo/config.rb +88 -0
  19. data/lib/olelo/extensions.rb +252 -0
  20. data/lib/olelo/helper.rb +290 -0
  21. data/lib/olelo/hooks.rb +142 -0
  22. data/lib/olelo/html_safe.rb +29 -0
  23. data/lib/olelo/initializer.rb +76 -0
  24. data/lib/olelo/locale.rb +63 -0
  25. data/lib/olelo/locale.yml +284 -0
  26. data/lib/olelo/menu.rb +101 -0
  27. data/lib/olelo/middleware/blacklist.rb +25 -0
  28. data/lib/olelo/middleware/degrade_mime_type.rb +19 -0
  29. data/lib/olelo/middleware/flash.rb +97 -0
  30. data/lib/olelo/middleware/force_encoding.rb +41 -0
  31. data/lib/olelo/page.rb +266 -0
  32. data/lib/olelo/patch.rb +311 -0
  33. data/lib/olelo/plugin.rb +188 -0
  34. data/lib/olelo/repository.rb +225 -0
  35. data/lib/olelo/routing.rb +223 -0
  36. data/lib/olelo/templates.rb +30 -0
  37. data/lib/olelo/user.rb +132 -0
  38. data/lib/olelo/util.rb +233 -0
  39. data/lib/olelo/version.rb +3 -0
  40. data/lib/olelo/virtualfs.rb +161 -0
  41. data/lib/rack/olelo_patches.rb +33 -0
  42. data/lib/rack/relative_redirect.rb +44 -0
  43. data/lib/rack/static_cache.rb +93 -0
  44. data/lib/yard/addons.rb +1 -0
  45. data/lib/yard/addons/hook_handler.rb +25 -0
  46. data/lib/yard/addons/override_tag.rb +14 -0
  47. data/lib/yard/addons/route_handler.rb +33 -0
  48. data/lib/yard/addons/sanitize_anchor.rb +16 -0
  49. data/olelo.gemspec +31 -0
  50. data/plugins/aspects/changelog.rb +45 -0
  51. data/plugins/aspects/documentbrowser.rb +57 -0
  52. data/plugins/aspects/download.rb +11 -0
  53. data/plugins/aspects/highlight.rb +8 -0
  54. data/plugins/aspects/image.rb +41 -0
  55. data/plugins/aspects/imageinfo.rb +64 -0
  56. data/plugins/aspects/locale.yml +60 -0
  57. data/plugins/aspects/main.rb +199 -0
  58. data/plugins/aspects/pageinfo.rb +37 -0
  59. data/plugins/aspects/source.rb +6 -0
  60. data/plugins/aspects/subpages.rb +44 -0
  61. data/plugins/aspects/text.rb +9 -0
  62. data/plugins/blog/blog.css +1 -0
  63. data/plugins/blog/blog.scss +37 -0
  64. data/plugins/blog/locale.yml +12 -0
  65. data/plugins/blog/main.rb +85 -0
  66. data/plugins/editor/locale.yml +18 -0
  67. data/plugins/editor/markup/main.rb +3 -0
  68. data/plugins/editor/markup/script.js +10 -0
  69. data/plugins/editor/markup/script/00-jquery.textselection.js +267 -0
  70. data/plugins/editor/markup/script/01-olelo.markupeditor.js +116 -0
  71. data/plugins/editor/markup/script/init.js +10 -0
  72. data/plugins/editor/preview.rb +52 -0
  73. data/plugins/editor/recaptcha.rb +56 -0
  74. data/plugins/filters/creole.rb +37 -0
  75. data/plugins/filters/disposition.rb +9 -0
  76. data/plugins/filters/editsection.rb +67 -0
  77. data/plugins/filters/fix_img_tag.rb +16 -0
  78. data/plugins/filters/html_wrapper.rb +12 -0
  79. data/plugins/filters/interwiki.rb +19 -0
  80. data/plugins/filters/link_classifier.rb +26 -0
  81. data/plugins/filters/locale.yml +15 -0
  82. data/plugins/filters/main.rb +202 -0
  83. data/plugins/filters/markdown_nowiki.rb +15 -0
  84. data/plugins/filters/numbering.xsl +93 -0
  85. data/plugins/filters/orgmode.rb +6 -0
  86. data/plugins/filters/rubypants.rb +6 -0
  87. data/plugins/filters/s5/main.rb +32 -0
  88. data/plugins/filters/s5/s5.xsl +118 -0
  89. data/plugins/filters/tilt.rb +17 -0
  90. data/plugins/filters/toc.rb +50 -0
  91. data/plugins/filters/xhtml2latex.xsl +232 -0
  92. data/plugins/filters/xslt.rb +22 -0
  93. data/plugins/gallery/gallery.css +1 -0
  94. data/plugins/gallery/gallery.scss +28 -0
  95. data/plugins/gallery/main.rb +34 -0
  96. data/plugins/misc/fancybox/images/blank.gif +0 -0
  97. data/plugins/misc/fancybox/images/fancy_close.png +0 -0
  98. data/plugins/misc/fancybox/images/fancy_loading.png +0 -0
  99. data/plugins/misc/fancybox/images/fancy_nav_left.png +0 -0
  100. data/plugins/misc/fancybox/images/fancy_nav_right.png +0 -0
  101. data/plugins/misc/fancybox/images/fancy_shadow_e.png +0 -0
  102. data/plugins/misc/fancybox/images/fancy_shadow_n.png +0 -0
  103. data/plugins/misc/fancybox/images/fancy_shadow_ne.png +0 -0
  104. data/plugins/misc/fancybox/images/fancy_shadow_nw.png +0 -0
  105. data/plugins/misc/fancybox/images/fancy_shadow_s.png +0 -0
  106. data/plugins/misc/fancybox/images/fancy_shadow_se.png +0 -0
  107. data/plugins/misc/fancybox/images/fancy_shadow_sw.png +0 -0
  108. data/plugins/misc/fancybox/images/fancy_shadow_w.png +0 -0
  109. data/plugins/misc/fancybox/images/fancy_title_left.png +0 -0
  110. data/plugins/misc/fancybox/images/fancy_title_main.png +0 -0
  111. data/plugins/misc/fancybox/images/fancy_title_over.png +0 -0
  112. data/plugins/misc/fancybox/images/fancy_title_right.png +0 -0
  113. data/plugins/misc/fancybox/images/fancybox-x.png +0 -0
  114. data/plugins/misc/fancybox/images/fancybox-y.png +0 -0
  115. data/plugins/misc/fancybox/images/fancybox.png +0 -0
  116. data/plugins/misc/fancybox/jquery.fancybox.css +1 -0
  117. data/plugins/misc/fancybox/jquery.fancybox.scss +323 -0
  118. data/plugins/misc/fancybox/main.rb +4 -0
  119. data/plugins/misc/fancybox/script.js +37 -0
  120. data/plugins/misc/fancybox/script/00-jquery.mousewheel.js +84 -0
  121. data/plugins/misc/fancybox/script/01-jquery.easing.js +205 -0
  122. data/plugins/misc/fancybox/script/02-jquery.fancybox.js +1156 -0
  123. data/plugins/misc/fancybox/script/init.js +18 -0
  124. data/plugins/misc/system.rb +192 -0
  125. data/plugins/misc/variables.rb +29 -0
  126. data/plugins/misc/webdav.rb +45 -0
  127. data/plugins/repositories/git_grep.rb +69 -0
  128. data/plugins/repositories/gitrb_repository.rb +204 -0
  129. data/plugins/repositories/locale.yml +12 -0
  130. data/plugins/repositories/rugged_repository.rb +454 -0
  131. data/plugins/security/acl.rb +57 -0
  132. data/plugins/security/basic_auth.rb +21 -0
  133. data/plugins/security/locale.yml +21 -0
  134. data/plugins/security/persistent_login.rb +32 -0
  135. data/plugins/security/portal.rb +28 -0
  136. data/plugins/security/private_wiki.rb +24 -0
  137. data/plugins/security/readonly_wiki.rb +25 -0
  138. data/plugins/security/stack.rb +20 -0
  139. data/plugins/security/yamlfile.rb +66 -0
  140. data/plugins/tags/code.rb +6 -0
  141. data/plugins/tags/footnotes.rb +35 -0
  142. data/plugins/tags/gist-embed.css +123 -0
  143. data/plugins/tags/gist.rb +13 -0
  144. data/plugins/tags/html.rb +57 -0
  145. data/plugins/tags/include.rb +20 -0
  146. data/plugins/tags/main.rb +353 -0
  147. data/plugins/tags/math.rb +117 -0
  148. data/plugins/tags/scripting.rb +64 -0
  149. data/plugins/tags/sort.rb +7 -0
  150. data/plugins/tags/tabs.rb +20 -0
  151. data/plugins/treeview/images/collapsed.png +0 -0
  152. data/plugins/treeview/images/expanded.png +0 -0
  153. data/plugins/treeview/images/menu.png +0 -0
  154. data/plugins/treeview/images/tree.png +0 -0
  155. data/plugins/treeview/images/wait.gif +0 -0
  156. data/plugins/treeview/main.rb +16 -0
  157. data/plugins/treeview/script.js +5 -0
  158. data/plugins/treeview/script/00-jquery.treeview.js +164 -0
  159. data/plugins/treeview/script/init.js +25 -0
  160. data/plugins/treeview/treeview.css +1 -0
  161. data/plugins/treeview/treeview.scss +113 -0
  162. data/plugins/utils/assets.rb +74 -0
  163. data/plugins/utils/cache.rb +53 -0
  164. data/plugins/utils/image_magick.rb +34 -0
  165. data/plugins/utils/pygments.css +1 -0
  166. data/plugins/utils/pygments.rb +50 -0
  167. data/plugins/utils/pygments.scss +83 -0
  168. data/plugins/utils/semaphore.rb +50 -0
  169. data/plugins/utils/shell.rb +45 -0
  170. data/plugins/utils/store.rb +315 -0
  171. data/plugins/utils/worker.rb +36 -0
  172. data/plugins/utils/xml.rb +29 -0
  173. data/static/images/favicon.png +0 -0
  174. data/static/script.js +267 -0
  175. data/static/script/00-json2.js +486 -0
  176. data/static/script/01-jstorage.js +217 -0
  177. data/static/script/02-jquery.js +9440 -0
  178. data/static/script/03-jquery.ui.core.js +337 -0
  179. data/static/script/04-jquery.ui.widget.js +502 -0
  180. data/static/script/05-jquery.ui.position.js +517 -0
  181. data/static/script/06-jquery.ui.menu.js +609 -0
  182. data/static/script/07-jquery.ui.autocomplete.js +601 -0
  183. data/static/script/08-olelo.i18n.js +37 -0
  184. data/static/script/09-olelo.unsaved.js +68 -0
  185. data/static/script/10-olelo.historytable.js +40 -0
  186. data/static/script/11-olelo.pagination.js +18 -0
  187. data/static/script/13-olelo.tabwidget.js +57 -0
  188. data/static/script/14-olelo.timeago.js +70 -0
  189. data/static/script/15-olelo.underliner.js +31 -0
  190. data/static/script/16-olelo.ui.combobox.js +32 -0
  191. data/static/script/init.js +48 -0
  192. data/static/themes/atlantis/constants.scss +15 -0
  193. data/static/themes/atlantis/iehacks.scss +38 -0
  194. data/static/themes/atlantis/images/actions/delete.png +0 -0
  195. data/static/themes/atlantis/images/actions/edit.png +0 -0
  196. data/static/themes/atlantis/images/actions/history.png +0 -0
  197. data/static/themes/atlantis/images/actions/home.png +0 -0
  198. data/static/themes/atlantis/images/actions/move.png +0 -0
  199. data/static/themes/atlantis/images/actions/new.png +0 -0
  200. data/static/themes/atlantis/images/actions/page.png +0 -0
  201. data/static/themes/atlantis/images/bg/button.png +0 -0
  202. data/static/themes/atlantis/images/bg/container.png +0 -0
  203. data/static/themes/atlantis/images/bg/content.png +0 -0
  204. data/static/themes/atlantis/images/bg/footer.png +0 -0
  205. data/static/themes/atlantis/images/bg/header.jpg +0 -0
  206. data/static/themes/atlantis/images/bg/header.orig.jpg +0 -0
  207. data/static/themes/atlantis/images/bg/header_gray.jpg +0 -0
  208. data/static/themes/atlantis/images/bug.png +0 -0
  209. data/static/themes/atlantis/images/filetypes/7z.png +0 -0
  210. data/static/themes/atlantis/images/filetypes/_archive.png +0 -0
  211. data/static/themes/atlantis/images/filetypes/_audio.png +0 -0
  212. data/static/themes/atlantis/images/filetypes/_code.png +0 -0
  213. data/static/themes/atlantis/images/filetypes/_linux.png +0 -0
  214. data/static/themes/atlantis/images/filetypes/_picture.png +0 -0
  215. data/static/themes/atlantis/images/filetypes/_video.png +0 -0
  216. data/static/themes/atlantis/images/filetypes/bz2.png +0 -0
  217. data/static/themes/atlantis/images/filetypes/doc.png +0 -0
  218. data/static/themes/atlantis/images/filetypes/flac.png +0 -0
  219. data/static/themes/atlantis/images/filetypes/gz.png +0 -0
  220. data/static/themes/atlantis/images/filetypes/html.png +0 -0
  221. data/static/themes/atlantis/images/filetypes/java.png +0 -0
  222. data/static/themes/atlantis/images/filetypes/jpg.png +0 -0
  223. data/static/themes/atlantis/images/filetypes/midi.png +0 -0
  224. data/static/themes/atlantis/images/filetypes/mp3.png +0 -0
  225. data/static/themes/atlantis/images/filetypes/ogg.png +0 -0
  226. data/static/themes/atlantis/images/filetypes/pdf.png +0 -0
  227. data/static/themes/atlantis/images/filetypes/php.png +0 -0
  228. data/static/themes/atlantis/images/filetypes/png.png +0 -0
  229. data/static/themes/atlantis/images/filetypes/ppt.png +0 -0
  230. data/static/themes/atlantis/images/filetypes/psd.png +0 -0
  231. data/static/themes/atlantis/images/filetypes/rar.png +0 -0
  232. data/static/themes/atlantis/images/filetypes/rb.png +0 -0
  233. data/static/themes/atlantis/images/filetypes/sh.png +0 -0
  234. data/static/themes/atlantis/images/filetypes/tar.png +0 -0
  235. data/static/themes/atlantis/images/filetypes/txt.png +0 -0
  236. data/static/themes/atlantis/images/filetypes/wma.png +0 -0
  237. data/static/themes/atlantis/images/filetypes/xls.png +0 -0
  238. data/static/themes/atlantis/images/filetypes/zip.png +0 -0
  239. data/static/themes/atlantis/images/folder.png +0 -0
  240. data/static/themes/atlantis/images/folder_open.png +0 -0
  241. data/static/themes/atlantis/images/loading.gif +0 -0
  242. data/static/themes/atlantis/images/loading.xcf +0 -0
  243. data/static/themes/atlantis/images/not_found.png +0 -0
  244. data/static/themes/atlantis/images/page.png +0 -0
  245. data/static/themes/atlantis/images/search.png +0 -0
  246. data/static/themes/atlantis/layout.scss +115 -0
  247. data/static/themes/atlantis/menu.scss +99 -0
  248. data/static/themes/atlantis/print.scss +129 -0
  249. data/static/themes/atlantis/screen.scss +495 -0
  250. data/static/themes/atlantis/style.css +3 -0
  251. data/static/themes/lib/autocomplete.scss +39 -0
  252. data/static/themes/lib/headlines.scss +10 -0
  253. data/static/themes/lib/horizontal-list.scss +31 -0
  254. data/static/themes/lib/patch.scss +88 -0
  255. data/static/themes/lib/reset.scss +114 -0
  256. data/static/themes/lib/rounded.scss +46 -0
  257. data/static/themes/lib/shadow.scss +14 -0
  258. data/test/config_test.rb +28 -0
  259. data/test/factory_test.rb +29 -0
  260. data/test/hash_extensions_test.rb +16 -0
  261. data/test/helper.rb +38 -0
  262. data/test/hooks_test.rb +85 -0
  263. data/test/object_extensions_test.rb +20 -0
  264. data/test/page_test.rb +168 -0
  265. data/test/request_test.rb +166 -0
  266. data/test/string_extensions_test.rb +32 -0
  267. data/test/templates_test.rb +39 -0
  268. data/test/util_test.rb +71 -0
  269. data/views/changes.slim +22 -0
  270. data/views/compare.slim +8 -0
  271. data/views/delete.slim +9 -0
  272. data/views/deleted.slim +2 -0
  273. data/views/edit.slim +65 -0
  274. data/views/error.slim +6 -0
  275. data/views/history.slim +20 -0
  276. data/views/layout.slim +38 -0
  277. data/views/login.slim +37 -0
  278. data/views/move.slim +10 -0
  279. data/views/not_found.slim +6 -0
  280. data/views/profile.slim +26 -0
  281. data/views/show.slim +9 -0
  282. metadata +488 -0
@@ -0,0 +1,12 @@
1
+ en:
2
+ search_results: 'Search results for #{pattern}'
3
+ match: 'One match'
4
+ match_plural: '#{count} matches'
5
+ de:
6
+ search_results: 'Suchergebnisse für #{pattern}'
7
+ match: 'Ein Treffer'
8
+ match_plural: '#{count} Treffer'
9
+ cs_CZ:
10
+ search_results: 'Výsledky hledání pro #{pattern}'
11
+ match: 'Jeden výsledek'
12
+ match_plural: '#{count} výsledků'
@@ -0,0 +1,454 @@
1
+ description 'Git repository backend (Using rugged library)'
2
+ require 'rugged'
3
+ require 'fileutils'
4
+
5
+ class RuggedRepository < Repository
6
+ CONTENT_EXT = '.content'
7
+ ATTRIBUTE_EXT = '.attributes'
8
+
9
+ class Blob
10
+ def initialize(git, content)
11
+ @git = git
12
+ @content = content
13
+ end
14
+
15
+ def type
16
+ :blob
17
+ end
18
+
19
+ def filemode
20
+ 0100644
21
+ end
22
+
23
+ def save
24
+ Rugged::Blob.create(@git, @content)
25
+ end
26
+ end
27
+
28
+ class Reference
29
+ attr_reader :filemode, :type
30
+
31
+ def initialize(git, entry)
32
+ @git = git
33
+ @oid = entry[:oid]
34
+ @filemode = entry[:filemode]
35
+ @type = entry[:type]
36
+ end
37
+
38
+ def save
39
+ @oid
40
+ end
41
+
42
+ def lookup
43
+ if type == :tree
44
+ Tree.new(@git, @oid)
45
+ else
46
+ self
47
+ end
48
+ end
49
+ end
50
+
51
+ class Tree
52
+ def initialize(git, oid = nil)
53
+ @git = git
54
+ @entries = {}
55
+ @oid = oid
56
+ if oid
57
+ tree = @git.lookup(oid)
58
+ raise 'Not a tree' unless Rugged::Tree === tree
59
+ tree.each {|entry| @entries[entry[:name].force_encoding(Encoding.default_external)] = Reference.new(@git, entry) }
60
+ end
61
+ end
62
+
63
+ def empty?
64
+ @entries.empty?
65
+ end
66
+
67
+ def type
68
+ :tree
69
+ end
70
+
71
+ def filemode
72
+ 0040000
73
+ end
74
+
75
+ def get(name)
76
+ child = @entries[name]
77
+ Reference === child ? @entries[name] = child.lookup : child
78
+ end
79
+
80
+ def [](path)
81
+ return self if path.blank?
82
+ name, path = path.split('/', 2)
83
+ child = get(name)
84
+ if path && child
85
+ raise 'Find child in blob' unless child.type == :tree
86
+ child[path]
87
+ else
88
+ child
89
+ end
90
+ end
91
+
92
+ def []=(path, object)
93
+ raise 'Blank path' if path.blank?
94
+ @oid = nil
95
+ name, path = path.split('/', 2)
96
+ child = get(name)
97
+ if path
98
+ child = @entries[name] = Tree.new(@git) unless child
99
+ if child.type == :tree
100
+ child[path] = object
101
+ else
102
+ raise 'Parent not found'
103
+ end
104
+ else
105
+ @entries[name] = object
106
+ end
107
+ end
108
+
109
+ def move(path, destination)
110
+ self[destination] = delete(path)
111
+ end
112
+
113
+ def delete(path)
114
+ raise 'Blank path' if path.blank?
115
+ @oid = nil
116
+ name, path = path.split('/', 2)
117
+ child = get(name)
118
+ if path
119
+ if child.type == :tree
120
+ child.delete(path)
121
+ else
122
+ raise 'Object not found'
123
+ end
124
+ else
125
+ entry = @entries.delete(name)
126
+ raise 'Object not found' unless entry
127
+ entry
128
+ end
129
+ end
130
+
131
+ def save
132
+ return @oid if @oid
133
+ builder = Rugged::Tree::Builder.new
134
+ @entries.each do |name, entry|
135
+ builder << { :type => entry.type, :filemode => entry.filemode, :oid => entry.save, :name => name }
136
+ end
137
+ builder.write(@git)
138
+ end
139
+ end
140
+
141
+ class Transaction
142
+ attr_reader :tree
143
+
144
+ def initialize(git)
145
+ @git = git
146
+ @head = current_head
147
+ @tree = Tree.new(@git, @head && @git.lookup(@head).tree_oid)
148
+ end
149
+
150
+ def commit(comment)
151
+ raise 'Concurrent transactions' if @head != current_head
152
+
153
+ user = User.current
154
+ author = {:email => user.email, :name => user.name, :time => Time.now }
155
+ commit = Rugged::Commit.create(@git,
156
+ :author => author,
157
+ :message => comment,
158
+ :committer => author,
159
+ :parents => [@head],
160
+ :tree => @tree.save)
161
+
162
+ raise 'Concurrent transactions' if @head != current_head
163
+ if current_head
164
+ @git.head.target = commit
165
+ else
166
+ Rugged::Reference.create(@git, "refs/heads/master", commit)
167
+ end
168
+ end
169
+
170
+ private
171
+
172
+ def current_head
173
+ @git.head.target rescue nil
174
+ end
175
+ end
176
+
177
+ def initialize(config)
178
+ @git = Rugged::Repository.new(config[:path])
179
+ Olelo.logger.info "Opening git repository: #{config[:path]}"
180
+ rescue Rugged::OSError
181
+ Olelo.logger.info "Creating git repository: #{config[:path]}"
182
+ FileUtils.mkpath(config[:path])
183
+ @git = Rugged::Repository.init_at(config[:path], config[:bare])
184
+ end
185
+
186
+ def transaction
187
+ raise 'Transaction already running' if Thread.current[:olelo_rugged_tx]
188
+ Thread.current[:olelo_rugged_tx] = Transaction.new(@git)
189
+ yield
190
+ ensure
191
+ Thread.current[:olelo_rugged_tx] = nil
192
+ end
193
+
194
+ def set_content(path, content)
195
+ check_path(path)
196
+ content = content.read if content.respond_to? :read
197
+ expand_tree(path)
198
+ object = work_tree[path]
199
+ if object && object.type == :tree
200
+ if content.blank?
201
+ work_tree.delete(path + CONTENT_EXT)
202
+ else
203
+ work_tree[path + CONTENT_EXT] = Blob.new(@git, content)
204
+ end
205
+ collapse_empty_tree(path)
206
+ else
207
+ work_tree[path] = Blob.new(@git, content)
208
+ end
209
+ end
210
+
211
+ def set_attributes(path, attributes)
212
+ check_path(path)
213
+ attributes = attributes.blank? ? nil : YAML.dump(attributes).sub(/\A\-\-\-\s*\n/s, '')
214
+ expand_tree(path)
215
+ if attributes
216
+ work_tree[path + ATTRIBUTE_EXT] = Blob.new(@git, attributes)
217
+ else
218
+ work_tree.delete(path + ATTRIBUTE_EXT) if work_tree[path + ATTRIBUTE_EXT]
219
+ end
220
+ end
221
+
222
+ def move(path, destination)
223
+ check_path(destination)
224
+ work_tree.move(path, destination)
225
+ work_tree.move(path + CONTENT_EXT, destination + CONTENT_EXT) if work_tree[path + CONTENT_EXT]
226
+ work_tree.move(path + ATTRIBUTE_EXT, destination + ATTRIBUTE_EXT) if work_tree[path + ATTRIBUTE_EXT]
227
+ collapse_empty_tree(path/'..')
228
+ end
229
+
230
+ def delete(path)
231
+ work_tree.delete(path)
232
+ work_tree.delete(path + CONTENT_EXT) if work_tree[path + CONTENT_EXT]
233
+ work_tree.delete(path + ATTRIBUTE_EXT) if work_tree[path + ATTRIBUTE_EXT]
234
+ collapse_empty_tree(path/'..')
235
+ end
236
+
237
+ def commit(comment)
238
+ current_transaction.commit(comment)
239
+ commit_to_version(@git.last_commit)
240
+ end
241
+
242
+ def path_exists?(path, version)
243
+ commit = @git.lookup(version.to_s)
244
+ raise 'Not a commit' unless Rugged::Commit === commit
245
+ path.blank? || commit.tree.path(path) != nil rescue nil
246
+ end
247
+
248
+ def get_version(version = nil)
249
+ if version
250
+ commit = @git.rev_parse(version.to_s) rescue nil
251
+ commit_to_version(commit)
252
+ else
253
+ commit_to_version(@git.last_commit) rescue nil
254
+ end
255
+ end
256
+
257
+ def get_history(path, skip = nil, limit = nil)
258
+ commits = []
259
+ walker = Rugged::Walker.new(@git)
260
+ walker.sorting(Rugged::SORT_TOPO)
261
+ walker.push(@git.head.target)
262
+ walker.each do |c|
263
+ if path_changed?(c, path)
264
+ if skip > 0
265
+ skip -= 1
266
+ else
267
+ commits << c
268
+ break if limit && commits.size >= limit
269
+ end
270
+ end
271
+ end
272
+ commits.map {|c| commit_to_version(c) }
273
+ end
274
+
275
+ def get_path_version(path, version)
276
+ version ||= @git.head.target
277
+ version = version.to_s
278
+
279
+ commits = []
280
+ walker = Rugged::Walker.new(@git)
281
+ walker.sorting(Rugged::SORT_TOPO)
282
+ walker.push(version)
283
+ walker.each do |c|
284
+ if path_changed?(c, path)
285
+ commits << c
286
+ break if commits.size == 2
287
+ end
288
+ end
289
+
290
+ succ = nil
291
+ if version != @git.head.target
292
+ newer = nil
293
+ walker.reset
294
+ walker.sorting(Rugged::SORT_TOPO)
295
+ walker.push(@git.head.target)
296
+ walker.each do |c|
297
+ if path_changed?(c, path)
298
+ if c == commits[0]
299
+ succ = newer
300
+ break
301
+ end
302
+ newer = c
303
+ end
304
+ end
305
+ end
306
+
307
+ [commit_to_version(commits[1]), # previous version
308
+ commit_to_version(commits[0]), # current version
309
+ commit_to_version(succ)] # next version
310
+ end
311
+
312
+ def get_children(path, version)
313
+ commit = @git.lookup(version.to_s)
314
+ raise 'Not a commit' unless Rugged::Commit === commit
315
+ object = object_by_path(commit.tree, path)
316
+ Rugged::Tree === object ? object.map {|e| e[:name].force_encoding(Encoding.default_external) }.reject {|name| reserved_name?(name) } : []
317
+ end
318
+
319
+ def get_content(path, version)
320
+ commit = @git.lookup(version.to_s)
321
+ raise 'Not a commit' unless Rugged::Commit === commit
322
+ object = object_by_path(commit.tree, path)
323
+ object = object_by_path(commit.tree, path + CONTENT_EXT) if Rugged::Tree === object
324
+ Rugged::Blob === object ? object.content.try_encoding(Encoding.default_external) : ''
325
+ end
326
+
327
+ def get_attributes(path, version)
328
+ commit = @git.lookup(version.to_s)
329
+ raise 'Not a commit' unless Rugged::Commit === commit
330
+ path += ATTRIBUTE_EXT
331
+ object = object_by_path(commit.tree, path)
332
+ object ? YAML.load(object.content) : {}
333
+ end
334
+
335
+ def diff(path, from, to)
336
+ commit_from = from && @git.rev_parse(from.to_s)
337
+ commit_to = @git.rev_parse(to.to_s)
338
+ raise 'Not a commit' unless (!commit_from || Rugged::Commit === commit_from) && Rugged::Commit === commit_to
339
+ diff = git_diff_tree('--root', '--full-index', '-u', '-M', commit_from ? commit_from.oid : nil, commit_to.oid, '--', path, path + CONTENT_EXT, path + ATTRIBUTE_EXT)
340
+ Diff.new(commit_to_version(commit_from), commit_to_version(commit_to), diff)
341
+ end
342
+
343
+ def short_version(version)
344
+ version[0..4]
345
+ end
346
+
347
+ def method_missing(name, *args)
348
+ cmd = name.to_s
349
+ if cmd =~ /\Agit_/
350
+ cmd = $'.tr('_', '-')
351
+ args = args.flatten.compact.map(&:to_s)
352
+
353
+ out = IO.popen('-', 'rb') do |io|
354
+ if io
355
+ # Read in binary mode (ascii-8bit) and convert afterwards
356
+ block_given? ? yield(io) : io.read.try_encoding(Encoding.default_external)
357
+ else
358
+ # child's stderr goes to stdout
359
+ STDERR.reopen(STDOUT)
360
+ ENV['GIT_DIR'] = @git.path
361
+ exec(self.class.git_path, cmd, *args)
362
+ end
363
+ end
364
+
365
+ if $?.exitstatus > 0
366
+ return '' if $?.exitstatus == 1 && out == ''
367
+ raise "git #{cmd} #{args.inspect} #{out}"
368
+ end
369
+
370
+ out
371
+ else
372
+ super
373
+ end
374
+ end
375
+
376
+ def reserved_name?(name)
377
+ name.ends_with?(ATTRIBUTE_EXT) || name.ends_with?(CONTENT_EXT)
378
+ end
379
+
380
+ private
381
+
382
+ def self.git_path
383
+ @git_path ||= begin
384
+ path = `which git`.chomp
385
+ raise 'git not found' if $?.exitstatus != 0
386
+ path
387
+ end
388
+ end
389
+
390
+ def check_path(path)
391
+ raise :reserved_path.t if path.split('/').any? {|name| reserved_name?(name) }
392
+ end
393
+
394
+ def path_changed?(c, path)
395
+ return true if path.blank?
396
+ new = [(c.tree.path(path) rescue nil),
397
+ (c.tree.path(path + ATTRIBUTE_EXT) rescue nil),
398
+ (c.tree.path(path + CONTENT_EXT) rescue nil)]
399
+ (new.first && c.parents.empty?) || c.parents.any? do |parent|
400
+ new != [(parent.tree.path(path) rescue nil),
401
+ (parent.tree.path(path + ATTRIBUTE_EXT) rescue nil),
402
+ (parent.tree.path(path + CONTENT_EXT) rescue nil)]
403
+ end
404
+ end
405
+
406
+ def object_by_path(tree, path)
407
+ return tree if path.blank?
408
+ ref = tree.path(path)
409
+ @git.lookup(ref[:oid])
410
+ rescue Rugged::IndexerError
411
+ nil
412
+ end
413
+
414
+ def commit_to_version(commit)
415
+ commit && Version.new(commit.oid, User.new(commit.author[:name], commit.author[:email]),
416
+ Time.at(commit.time), commit.message, commit.parents.map(&:oid), commit.oid == @git.head.target)
417
+ end
418
+
419
+ def current_transaction
420
+ Thread.current[:olelo_rugged_tx] || raise('No transaction running')
421
+ end
422
+
423
+ def work_tree
424
+ current_transaction.tree
425
+ end
426
+
427
+ # Convert blob parents to trees
428
+ # to allow children
429
+ def expand_tree(path)
430
+ names = path.split('/')
431
+ names.pop
432
+ parent = work_tree
433
+ names.each do |name|
434
+ object = parent[name]
435
+ break if !object
436
+ if object.type == :blob
437
+ parent.move(name, name + CONTENT_EXT)
438
+ break
439
+ end
440
+ parent = object
441
+ end
442
+ end
443
+
444
+ # If a tree consists only of tree/, tree.content and tree.attributes without
445
+ # children, tree.content can be moved to tree ("collapsing").
446
+ def collapse_empty_tree(path)
447
+ if !path.blank? && work_tree[path].empty? && work_tree[path + CONTENT_EXT]
448
+ work_tree.move(path + CONTENT_EXT, path)
449
+ end
450
+ end
451
+ end
452
+
453
+ Repository.register :git, RuggedRepository
454
+ Repository.register :rugged, RuggedRepository