gitlab-gollum-lib 1.0.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.
@@ -0,0 +1,101 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+ module Gollum
3
+ class File
4
+ Wiki.file_class = self
5
+
6
+ # Public: Initialize a file.
7
+ #
8
+ # wiki - The Gollum::Wiki in question.
9
+ #
10
+ # Returns a newly initialized Gollum::File.
11
+ def initialize(wiki)
12
+ @wiki = wiki
13
+ @blob = nil
14
+ @path = nil
15
+ end
16
+
17
+ # Public: The url path required to reach this page within the repo.
18
+ #
19
+ # Returns the String url_path
20
+ def url_path
21
+ path = self.path
22
+ path = path.sub(/\/[^\/]+$/, '/') if path.include?('/')
23
+ path
24
+ end
25
+
26
+ # Public: The url_path, but CGI escaped.
27
+ #
28
+ # Returns the String url_path
29
+ def escaped_url_path
30
+ CGI.escape(self.url_path).gsub('%2F','/')
31
+ end
32
+
33
+ # Public: The on-disk filename of the file.
34
+ #
35
+ # Returns the String name.
36
+ def name
37
+ @blob && @blob.name
38
+ end
39
+ alias filename name
40
+
41
+ # Public: The raw contents of the page.
42
+ #
43
+ # Returns the String data.
44
+ def raw_data
45
+ return nil unless @blob
46
+
47
+ if !@wiki.repo.bare && @blob.is_symlink
48
+ new_path = @blob.symlink_target(::File.join(@wiki.repo.path, '..', self.path))
49
+ return IO.read(new_path) if new_path
50
+ end
51
+
52
+ @blob.data
53
+ end
54
+
55
+ # Public: The Grit::Commit version of the file.
56
+ attr_accessor :version
57
+
58
+ # Public: The String path of the file.
59
+ attr_reader :path
60
+
61
+ # Public: The String mime type of the file.
62
+ def mime_type
63
+ @blob.mime_type
64
+ end
65
+
66
+ # Populate the File with information from the Blob.
67
+ #
68
+ # blob - The Grit::Blob that contains the info.
69
+ # path - The String directory path of the file.
70
+ #
71
+ # Returns the populated Gollum::File.
72
+ def populate(blob, path=nil)
73
+ @blob = blob
74
+ @path = "#{path}/#{blob.name}"[1..-1]
75
+ self
76
+ end
77
+
78
+ #########################################################################
79
+ #
80
+ # Internal Methods
81
+ #
82
+ #########################################################################
83
+
84
+ # Find a file in the given Gollum repo.
85
+ #
86
+ # name - The full String path.
87
+ # version - The String version ID to find.
88
+ #
89
+ # Returns a Gollum::File or nil if the file could not be found.
90
+ def find(name, version)
91
+ checked = name.downcase
92
+ map = @wiki.tree_map_for(version)
93
+ if entry = map.detect { |entry| entry.path.downcase == checked }
94
+ @path = name
95
+ @blob = entry.blob(@wiki.repo)
96
+ @version = version.is_a?(Grit::Commit) ? version : @wiki.commit_for(version)
97
+ self
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,155 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+ module Gollum
3
+ =begin
4
+ FileView requires that:
5
+ - All files in root dir are processed first
6
+ - Then all the folders are sorted and processed
7
+ =end
8
+ class FileView
9
+ # common use cases:
10
+ # set pages to wiki.pages and show_all to false
11
+ # set pages to wiki.pages + wiki.files and show_all to true
12
+ def initialize pages, options = {}
13
+ @pages = pages
14
+ @show_all = options[:show_all] || false
15
+ @checked = options[:collapse_tree] ? '' : "checked"
16
+ end
17
+
18
+ def enclose_tree string
19
+ %Q(<ol class="tree">\n) + string + %Q(</ol>)
20
+ end
21
+
22
+ def new_page page
23
+ name = page.name
24
+ url = url_for_page page
25
+ %Q( <li class="file"><a href="#{url}"><span class="icon"></span>#{name}</a></li>)
26
+ end
27
+
28
+ def new_folder folder_path
29
+ new_sub_folder folder_path
30
+ end
31
+
32
+ def new_sub_folder path
33
+ <<-HTML
34
+ <li>
35
+ <label>#{path}</label> <input type="checkbox" #{@checked} />
36
+ <ol>
37
+ HTML
38
+ end
39
+
40
+ def end_folder
41
+ "</ol></li>\n"
42
+ end
43
+
44
+ def url_for_page page
45
+ url = ''
46
+ if @show_all
47
+ # Remove ext for valid pages.
48
+ filename = page.filename
49
+ filename = Page::valid_page_name?(filename) ? filename.chomp(::File.extname(filename)) : filename
50
+
51
+ url = ::File.join(::File.dirname(page.path), filename)
52
+ else
53
+ url = ::File.join(::File.dirname(page.path), page.filename_stripped)
54
+ end
55
+ url = url[2..-1] if url[0,2] == './'
56
+ url
57
+ end
58
+
59
+ def render_files
60
+ html = ''
61
+ count = @pages.size
62
+ folder_start = -1
63
+
64
+ # Process all pages until folders start
65
+ count.times do | index |
66
+ page = @pages[ index ]
67
+ path = page.path
68
+
69
+ unless path.include? '/'
70
+ # Page processed (not contained in a folder)
71
+ html += new_page page
72
+ else
73
+ # Folders start at the next index
74
+ folder_start = index
75
+ break # Pages finished, move on to folders
76
+ end
77
+ end
78
+
79
+ # If there are no folders, then we're done.
80
+ return enclose_tree(html) if folder_start <= -1
81
+
82
+ # Handle special case of only one folder.
83
+ if (count - folder_start == 1)
84
+ page = @pages[ folder_start ]
85
+ html += <<-HTML
86
+ <li>
87
+ <label>#{::File.dirname(page.path)}</label> <input type="checkbox" #{@checked} />
88
+ <ol>
89
+ #{new_page page}
90
+ </ol>
91
+ </li>
92
+ HTML
93
+
94
+ return enclose_tree html
95
+ end
96
+
97
+ sorted_folders = []
98
+ (folder_start).upto count - 1 do | index |
99
+ sorted_folders += [[ @pages[ index ].path, index ]]
100
+ end
101
+
102
+ # http://stackoverflow.com/questions/3482814/sorting-list-of-string-paths-in-vb-net
103
+ sorted_folders.sort! do |first,second|
104
+ a = first[0]
105
+ b = second[0]
106
+
107
+ # use :: operator because gollum defines its own conflicting File class
108
+ dir_compare = ::File.dirname(a) <=> ::File.dirname(b)
109
+
110
+ # Sort based on directory name unless they're equal (0) in
111
+ # which case sort based on file name.
112
+ if dir_compare == 0
113
+ ::File.basename(a) <=> ::File.basename(b)
114
+ else
115
+ dir_compare
116
+ end
117
+ end
118
+
119
+ # keep track of folder depth, 0 = at root.
120
+ cwd_array = []
121
+ changed = false
122
+
123
+ # process rest of folders
124
+ (0...sorted_folders.size).each do | index |
125
+ page = @pages[ sorted_folders[ index ][ 1 ] ]
126
+ path = page.path
127
+ folder = ::File.dirname path
128
+
129
+ tmp_array = folder.split '/'
130
+
131
+ (0...tmp_array.size).each do | index |
132
+ if cwd_array[ index ].nil? || changed
133
+ html += new_sub_folder tmp_array[ index ]
134
+ next
135
+ end
136
+
137
+ if cwd_array[ index ] != tmp_array[ index ]
138
+ changed = true
139
+ (cwd_array.size - index).times do
140
+ html += end_folder
141
+ end
142
+ html += new_sub_folder tmp_array[ index ]
143
+ end
144
+ end
145
+
146
+ html += new_page page
147
+ cwd_array = tmp_array
148
+ changed = false
149
+ end
150
+
151
+ # return the completed html
152
+ enclose_tree html
153
+ end # end render_files
154
+ end # end FileView class
155
+ end # end Gollum module
@@ -0,0 +1,249 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+ module Gollum
3
+ # Controls all access to the Git objects from Gollum. Extend this class to
4
+ # add custom caching for special cases.
5
+ class GitAccess
6
+ # Initializes the GitAccess instance.
7
+ #
8
+ # path - The String path to the Git repository that holds the
9
+ # Gollum site.
10
+ # page_file_dir - String the directory in which all page files reside
11
+ #
12
+ # Returns this instance.
13
+ def initialize(path, page_file_dir = nil, bare = false)
14
+ @page_file_dir = page_file_dir
15
+ @path = path
16
+ @repo = Grit::Repo.new(path, { :is_bare => bare })
17
+ clear
18
+ end
19
+
20
+ # Public: Determines whether the Git repository exists on disk.
21
+ #
22
+ # Returns true if it exists, or false.
23
+ def exist?
24
+ @repo.git.exist?
25
+ end
26
+
27
+ # Public: Converts a given Git reference to a SHA, using the cache if
28
+ # available.
29
+ #
30
+ # ref - a String Git reference (ex: "master")
31
+ #
32
+ # Returns a String, or nil if the ref isn't found.
33
+ def ref_to_sha(ref)
34
+ ref = ref.to_s
35
+ return if ref.empty?
36
+ sha =
37
+ if sha?(ref)
38
+ ref
39
+ else
40
+ get_cache(:ref, ref) { ref_to_sha!(ref) }
41
+ end.to_s
42
+ sha.empty? ? nil : sha
43
+ end
44
+
45
+ # Public: Gets a recursive list of Git blobs for the whole tree at the
46
+ # given commit.
47
+ #
48
+ # ref - A String Git reference or Git SHA to a commit.
49
+ #
50
+ # Returns an Array of BlobEntry instances.
51
+ def tree(ref)
52
+ if sha = ref_to_sha(ref)
53
+ get_cache(:tree, sha) { tree!(sha) }
54
+ else
55
+ []
56
+ end
57
+ end
58
+
59
+ # Public: Fetches the contents of the Git blob at the given SHA.
60
+ #
61
+ # sha - A String Git SHA.
62
+ #
63
+ # Returns the String content of the blob.
64
+ def blob(sha)
65
+ cat_file!(sha)
66
+ end
67
+
68
+ # Public: Looks up the Git commit using the given Git SHA or ref.
69
+ #
70
+ # ref - A String Git SHA or ref.
71
+ #
72
+ # Returns a Grit::Commit.
73
+ def commit(ref)
74
+ if sha?(ref)
75
+ get_cache(:commit, ref) { commit!(ref) }
76
+ else
77
+ if sha = get_cache(:ref, ref)
78
+ commit(sha)
79
+ else
80
+ if cm = commit!(ref)
81
+ set_cache(:ref, ref, cm.id)
82
+ set_cache(:commit, cm.id, cm)
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ # Public: Clears all of the cached data that this GitAccess is tracking.
89
+ #
90
+ # Returns nothing.
91
+ def clear
92
+ @ref_map = {}
93
+ @tree_map = {}
94
+ @commit_map = {}
95
+ end
96
+
97
+ # Public: Refreshes just the cached Git reference data. This should
98
+ # be called after every Gollum update.
99
+ #
100
+ # Returns nothing.
101
+ def refresh
102
+ @ref_map.clear
103
+ end
104
+
105
+ #########################################################################
106
+ #
107
+ # Internal Methods
108
+ #
109
+ #########################################################################
110
+
111
+ # Gets the String path to the Git repository.
112
+ attr_reader :path
113
+
114
+ # Gets the Grit::Repo instance for the Git repository.
115
+ attr_reader :repo
116
+
117
+ # Gets a Hash cache of refs to commit SHAs.
118
+ #
119
+ # {"master" => "abc123", ...}
120
+ #
121
+ attr_reader :ref_map
122
+
123
+ # Gets a Hash cache of commit SHAs to a recursive tree of blobs.
124
+ #
125
+ # {"abc123" => [<BlobEntry>, <BlobEntry>]}
126
+ #
127
+ attr_reader :tree_map
128
+
129
+ # Gets a Hash cache of commit SHAs to the Grit::Commit instance.
130
+ #
131
+ # {"abcd123" => <Grit::Commit>}
132
+ #
133
+ attr_reader :commit_map
134
+
135
+ # Checks to see if the given String is a 40 character hex SHA.
136
+ #
137
+ # str - Possible String SHA.
138
+ #
139
+ # Returns true if the String is a SHA, or false.
140
+ def sha?(str)
141
+ !!(str =~ /^[0-9a-f]{40}$/)
142
+ end
143
+
144
+ # Looks up the Git SHA for the given Git ref.
145
+ #
146
+ # ref - String Git ref.
147
+ #
148
+ # Returns a String SHA.
149
+ def ref_to_sha!(ref)
150
+ @repo.git.rev_list({:max_count=>1}, ref)
151
+ rescue Grit::GitRuby::Repository::NoSuchShaFound
152
+ end
153
+
154
+ # Looks up the Git blobs for a given commit.
155
+ #
156
+ # sha - String commit SHA.
157
+ #
158
+ # Returns an Array of BlobEntry instances.
159
+ def tree!(sha)
160
+ tree = @repo.git.native(:ls_tree,
161
+ {:r => true, :l => true, :z => true}, sha)
162
+ if tree.respond_to?(:force_encoding)
163
+ tree.force_encoding("UTF-8")
164
+ end
165
+ items = tree.split("\0").inject([]) do |memo, line|
166
+ memo << parse_tree_line(line)
167
+ end
168
+
169
+ if dir = @page_file_dir
170
+ regex = /^#{dir}\//
171
+ items.select { |i| i.path =~ regex }
172
+ else
173
+ items
174
+ end
175
+ end
176
+
177
+ # Reads the content from the Git db at the given SHA.
178
+ #
179
+ # sha - The String SHA.
180
+ #
181
+ # Returns the String content of the Git object.
182
+ def cat_file!(sha)
183
+ @repo.git.cat_file({:p => true}, sha)
184
+ end
185
+
186
+ # Reads a Git commit.
187
+ #
188
+ # sha - The string SHA of the Git commit.
189
+ #
190
+ # Returns a Grit::Commit.
191
+ def commit!(sha)
192
+ @repo.commit(sha)
193
+ end
194
+
195
+ # Attempts to get the given data from a cache. If it doesn't exist, it'll
196
+ # pass the results of the yielded block to the cache for future accesses.
197
+ #
198
+ # name - The cache prefix used in building the full cache key.
199
+ # key - The unique cache key suffix, usually a String Git SHA.
200
+ #
201
+ # Yields a block to pass to the cache.
202
+ # Returns the cached result.
203
+ def get_cache(name, key)
204
+ cache = instance_variable_get("@#{name}_map")
205
+ value = cache[key]
206
+ if value.nil? && block_given?
207
+ set_cache(name, key, value = yield)
208
+ end
209
+ value == :_nil ? nil : value
210
+ end
211
+
212
+ # Writes some data to the internal cache.
213
+ #
214
+ # name - The cache prefix used in building the full cache key.
215
+ # key - The unique cache key suffix, usually a String Git SHA.
216
+ # value - The value to write to the cache.
217
+ #
218
+ # Returns nothing.
219
+ def set_cache(name, key, value)
220
+ cache = instance_variable_get("@#{name}_map")
221
+ cache[key] = value || :_nil
222
+ end
223
+
224
+ # Parses a line of output from the `ls-tree` command.
225
+ #
226
+ # line - A String line of output:
227
+ # "100644 blob 839c2291b30495b9a882c17d08254d3c90d8fb53 Home.md"
228
+ #
229
+ # Returns an Array of BlobEntry instances.
230
+ def parse_tree_line(line)
231
+ mode, type, sha, size, *name = line.split(/\s+/)
232
+ BlobEntry.new(sha, name.join(' '), size.to_i, mode.to_i(8))
233
+ end
234
+
235
+ # Decode octal sequences (\NNN) in tree path names.
236
+ #
237
+ # path - String path name.
238
+ #
239
+ # Returns a decoded String.
240
+ def decode_git_path(path)
241
+ if path[0] == ?" && path[-1] == ?"
242
+ path = path[1...-1]
243
+ path.gsub!(/\\\d{3}/) { |m| m[1..-1].to_i(8).chr }
244
+ end
245
+ path.gsub!(/\\[rn"\\]/) { |m| eval(%("#{m.to_s}")) }
246
+ path
247
+ end
248
+ end
249
+ end