gitlab-gollum-lib 1.0.0

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