Compare commits

...

90 Commits

Author SHA1 Message Date
rick 86ff1766b4 1.1.1 bump 2011-01-11 00:56:19 -08:00
rick 26624b70bd tweak code markup parser so that blocks without a language are just output in pre tags 2011-01-11 00:47:46 -08:00
rick fc84a4e989 add proper shell escaping for Albino 2011-01-11 00:34:31 -08:00
Tom Preston-Werner 996f81d63d Release 1.1.0 2010-10-28 12:18:27 -07:00
Marko Anastasov 16ef859073 Ignore case when matching image paths.
Otherwise images with file extensions in caps don't get rendered.
2010-10-12 12:07:07 +02:00
rick e7b1265873 Gollum::Wiki#size returns 0 if the repo is invalid or empty 2010-10-11 17:40:09 -07:00
rick c0f8c897e7 update gemspec file listing 2010-10-11 16:58:22 -07:00
rick 33aad801bd update history 2010-10-06 13:20:29 -07:00
rick dd7dddddcf Revert "Gollum::Wiki#pages come back sorted by title, not permalink or the default ordering from git ls-tree."
This reverts commit e37a8b9da4.
2010-10-06 13:17:47 -07:00
rick 251d5c8201 add to history 2010-09-30 17:20:31 -07:00
rick 4dbe3ea844 ensures rel="nofollow" is added to all anchors if a page is retrieved with a SHA 2010-09-30 17:19:37 -07:00
rick cc929bbc56 tomdoc'd 2010-09-30 15:36:14 -07:00
rick 5e59e39d67 add to history 2010-09-30 15:32:38 -07:00
rick 05a7b4c06c Allow users to pass anchors to page links.
http://github.com/github/gollum/issues/\#issue/21
2010-09-30 15:32:02 -07:00
rick 7924db8e01 mention cgi escapage and git grep in the history 2010-09-30 14:26:27 -07:00
rick 4b313944fe merge 2010-09-30 14:22:18 -07:00
rick 9786f5f347 Merge branch 'master' into special-chars-in-page-names 2010-09-30 14:02:39 -07:00
Tom Preston-Werner 42985dc5c1 Explicitly set default port to 4567. 2010-09-29 14:42:24 -07:00
Tom Preston-Werner ee6b71ff80 Change 'bind' CLI option to 'host'. Set default to 127.0.0.1. Closes #47. 2010-09-29 14:40:36 -07:00
Tom Preston-Werner cdf75d20ef Merge remote branch 'etaque/sinatra-options' 2010-09-29 13:56:03 -07:00
Tom Preston-Werner 1e2932fd21 Merge remote branch 'henrikh/test-sections' 2010-09-29 13:09:08 -07:00
rick 4bb7d53854 history update 2010-09-16 09:23:02 -07:00
rick e37a8b9da4 Gollum::Wiki#pages come back sorted by title, not permalink or the default ordering from git ls-tree. 2010-09-16 09:16:42 -07:00
Emilien Taque 48bfe954d1 add Sinatra --bind option through gollum binary 2010-09-15 22:53:15 +02:00
rick 9cc8790d4f history lesson 2010-09-13 16:09:05 -07:00
rick d51f42e647 theres no blank? in ruby 2010-09-13 15:49:10 -07:00
rick 430436ce8d handle non strings passed to Page.cname 2010-09-13 15:48:54 -07:00
rick e774ceaf1f dont bomb when trying to find a page with a blank name 2010-09-12 20:04:21 -07:00
rick 42bd6c221a invalid shas return empty treemaps 2010-09-03 10:59:30 -07:00
Paul Baumgart 50e9a42503 fix documentation for Wiki.size 2010-09-02 20:30:13 -07:00
Paul Baumgart 5fcb5d971a Add support for RFC 1738 special characters in page names.
Escape all links and redirects to support this. Some changes to app.rb
necessary for correct routing by Sinatra.
2010-09-01 22:02:19 -07:00
Jon Frisby e9239050e0 Properly handle pages that exist in sub-dirs in search results 2010-08-31 10:48:10 +02:00
Jon Frisby 50c207ad91 Update gemspec with search files 2010-08-31 10:47:51 +02:00
rick 7c70174725 handle invalid options 2010-08-30 18:50:02 -07:00
rick e0d234c255 use grit to get the file's mime type, as suggested.
http://github.com/github/gollum/commit/6de9ddfd3f79a9cb320aab3c43b8e0a912107b2e#commitcomment-137656
2010-08-30 18:25:48 -07:00
rick bf37f44970 pull default name/email for commits from wiki repo's git config 2010-08-30 18:11:03 -07:00
rick 847e6c00a4 Merge branch 'master' of github.com:github/gollum 2010-08-30 18:04:36 -07:00
Jon Frisby 6de9ddfd3f Make an attempt at setting a proper mime-type when showing a file.
Prevents things like having the browser show raw binary data when you have a PDF in your repo.
2010-08-31 09:04:27 +08:00
Jon Frisby 1f8a67598a Use Grit to determine committer instead of executing a sub-shell and calling the git CLI. 2010-08-31 09:04:27 +08:00
Jon Frisby 515f8610c4 Make --version not cough up a furball.
Commit 8c625419b8 moved the require down,
which broke --version.
2010-08-31 08:59:56 +08:00
rick ba18cbebe8 more tree_map tests 2010-08-30 17:56:03 -07:00
rick 646c8e21b2 Add Wiki#size to efficiently count pages without loading them. 2010-08-30 17:52:20 -07:00
rick 6dab4f7fe6 Wiki#pages uses the treemap 2010-08-30 17:45:32 -07:00
rick 7e2b1fdbc6 Wiki#tree_map_for returns an array of BlobEntry instances 2010-08-30 17:31:19 -07:00
rick 6f077702e1 Page#find_page_in_this_tree is redundant 2010-08-30 17:04:30 -07:00
Dean Strelau f4c4b2e49c Update development dependencies. Closes github#18
Tests require a few markdown libraries to pass.
2010-08-30 16:38:01 -07:00
Tom Preston-Werner db1ccc4525 DRY up test commit details. 2010-08-30 16:38:01 -07:00
Tom Preston-Werner dddb73672a Minor readme typo and various whitespace fixups. 2010-08-30 16:38:01 -07:00
Tom Preston-Werner 94f05b0796 Update working dir (if present) when edited via the API. Closes #6. 2010-08-30 16:37:59 -07:00
Sirupsen 6d3b40c1d4 Using Grit instead of backticked bash.
A bit unclear what's going on though, would be more explicit to say:
{:count => query}, however Grit::Git#transform_options converts to this
syntax:
`--count='query'` and `git grep` can't take this syntax, it has to be:
`git grep --count query`, and this is why we're using :c, and not
:count. As {:c => query} converts to: `git grep -c 'query'` which works
as expected.

Linked to this commit, for above information in comment.

Updated comment in Wiki.search to link describing commit.
2010-08-29 15:25:04 +02:00
Sirupsen 45eb364a6d Refactoring of Wiki.search. 2010-08-29 14:30:30 +02:00
Henrik Hansen 959f02b50e Use correct capitalization of TeX 2010-08-29 14:26:14 +02:00
Henrik Hansen 49d88a0370 Add search API and update frontend
The frontend has been updated to use the new search API.
2010-08-29 13:58:06 +02:00
Henrik Hansen ed41152228 Refactor of search view 2010-08-29 13:54:38 +02:00
Henrik Hansen 294847ed99 Remove display of search code 2010-08-29 13:54:21 +02:00
Henrik Hansen 8301428974 Add sections to clean up markup test code 2010-08-29 10:08:08 +02:00
rick 0500c7e10c cache the ref => sha lookup 2010-08-24 12:39:26 -07:00
rick 3eeed9ba1e update Wiki#find_page_in_tree to use cached tree map 2010-08-24 12:15:11 -07:00
rick 292f6ec8dc update Wiki#find_page_in_this_tree to use the new cached tree map. 2010-08-24 11:47:20 -07:00
rick 068a902a00 don't modify path names in cached wiki treemaps 2010-08-24 11:47:02 -07:00
rick d606511fdf use cached tree map when finding files 2010-08-24 11:29:39 -07:00
rick cc3d9529cc add a cached Wiki#tree_map_for method 2010-08-24 11:24:49 -07:00
rick 74ca65b045 history updates 2010-08-23 16:39:10 -07:00
rick 05283347b0 more descriptive irb output
also, code poetry arguments
2010-08-23 16:35:17 -07:00
rick 99995f7364 convert $path global to a gollum_path sinatra setting 2010-08-23 16:16:39 -07:00
rick 8c625419b8 add irb option for the cli script 2010-08-23 16:13:38 -07:00
Henrik Hansen d1003f5331 Change search-field to use type="search"
The search-field will now show with a little magnifying-glass on Webkit
based browsers.
type="search" is part of HTML5.
2010-08-23 16:59:47 +02:00
Tobias Adam 4905ce99da Add a very rough and dirty search mechanism that uses Git’s grep. 2010-08-22 23:17:54 +02:00
Tom Preston-Werner 48b143c6ed Update history for massive performance optimization. 2010-08-19 18:21:26 -07:00
Tom Preston-Werner dae2e0c79c Remove obsolete tree map methods. 2010-08-19 17:40:05 -07:00
Tom Preston-Werner 9b32d3bfb1 Update Wiki#delete_page to use Index#read_tree awesomes. 2010-08-19 17:37:49 -07:00
Tom Preston-Werner b1fc173a3f Update Wiki#update_page to use Index#read_tree goodness. 2010-08-19 17:35:18 -07:00
Tom Preston-Werner 6a53e3d037 Clean up some whitespace. 2010-08-19 15:43:26 -07:00
Tom Preston-Werner 15879a5a88 Use Index#read_tree for Wiki#write_page instead of rebuilding the entire index. 2010-08-19 15:42:36 -07:00
rick c6e2acbf62 history is written by the victors 2010-08-16 17:39:17 -07:00
rick 28a29f49f2 Gollum::Wiki#add_to_tree_map updates pages if a file with the matching name + extension exists 2010-08-16 17:37:53 -07:00
Tom Preston-Werner acdb04ba4e Handle duplicate page error in frontend. Closes #10. 2010-08-16 16:54:01 -07:00
rick dd30a7bdc2 fix markup on framed absolute image urls 2010-08-16 15:49:47 -07:00
rick c7e172f5ca can't access trees as files 2010-08-16 14:57:39 -07:00
rick c69a5f80dd prevent Gollum::Wiki instances from creating new pages that overwrite pages with the same name. 2010-08-16 07:56:46 -07:00
rick ee04dd84aa normalize commit hashes so they at least show 'anonymous' if no git user is set. 2010-08-16 07:36:32 -07:00
Tom Preston-Werner 763387979a Update history for op4g/1.9_compat. 2010-08-15 11:54:02 -07:00
Chris Hoffman 4e3b4e23da Ruby 1.9 compatibility
* Adds current directory to load path for tests because 1.9 does not
* Aliases String#lines to String#to_a under 1.8 to match 1.9 behavior
* Sets encoding magic comment in test file with characters outside ascii
* Require 'ostruct' explicitly

This depends on a patch to Grit to have full functionality through the
whole stack.
2010-08-14 21:37:00 +00:00
Tom Preston-Werner 8b0c971552 Merge remote branch 'dblugeon/correct_url_markup' 2010-08-14 11:34:18 -07:00
Tom Preston-Werner f4069f1dfb Update history for rgrove/newer-sanitize. 2010-08-14 11:17:41 -07:00
Tom Preston-Werner 3d730295b3 Merge remote branch 'rgrove/newer-sanitize' 2010-08-14 10:57:09 -07:00
rick edcfdfa402 return nil for bad file version 2010-08-13 16:11:37 -07:00
dblugeon 45446f3481 update the source code url of github markup 2010-08-13 18:23:57 +02:00
Tom Preston-Werner 2a905cedc9 Release 1.0.1 2010-08-12 16:04:51 -07:00
Ryan Grove f159c54dc8 Increase minimum Sanitize version requirement to 1.1.0.
1.0.x versions of Sanitize require Hpricot instead of Nokogiri
and have bugs that may allow non-whitelisted HTML to sneak
through.
2010-08-12 14:18:43 -07:00
29 changed files with 1071 additions and 324 deletions
+39
View File
@@ -1,3 +1,42 @@
# 1.1.1 / 2011-1-11
* Bug Fixes
* Critical shell escaping bug with syntax highlighting fixed.
# 1.1.0 / 2010-10-28
* Major Enhancements
* Optimize page write/update/delete to use Grit::Index::read_tree instead
of manually recreating entire index contents.
* Added --irb option for the gollum command.
* Update working dir (if present) when edited via the API (#6)
* Add basic `git grep` based search for repos.
* Minor Enhancements
* Support a `:gollum_path` Sinatra setting for `Precious::App`
* Add Wiki#size to efficiently count pages without loading them.
* Add the correct content type when serving files from the frontend.
* Add --host option and default it to 127.0.0.1.
* Allow anchors in page links, such as `[[Abc#header]]`.
* All pages retrieved with a SHA add `rel="nofollow"` to all
page links.
* Bug Fixes
* Increase minimum Sanitize version requirement to 1.1.0.
1.0.x versions of Sanitize require Hpricot instead of Nokogiri
and have bugs that may allow non-whitelisted HTML to sneak
through.
* Introduce Ruby 1.9 compatibility fixes.
* Commit hashes are normalized so that missing author data is replaced with
anonymous info.
* Prevent `Gollum::Wiki#write_page` from clobbering existing pages.
* Handle duplicate page errors in frontend.
* Fix bugs trying to retrieve pages with invalid names.
* CGI escape page names in links and redirects.
# 1.0.1 / 2010-08-12
* Bug Fixes
* Force Grit dep to 2.1 or higher.
# 1.0.0 / 2010-08-12 # 1.0.0 / 2010-08-12
* Open Source Birthday! * Open Source Birthday!
+2 -2
View File
@@ -63,7 +63,7 @@ choose. Special footers can be created in `footer files`. Other content
## PAGE FILES ## PAGE FILES
Page files may be written in any format supported by Page files may be written in any format supported by
[GitHub-Markup](http://github.com/defunkt/github-markup) (except roff). The [GitHub-Markup](http://github.com/github/markup) (except roff). The
current list of formats and allowed extensions is: current list of formats and allowed extensions is:
* ASCIIDoc: .asciidoc * ASCIIDoc: .asciidoc
@@ -326,7 +326,7 @@ Initialize the Gollum::Repo object:
wiki = Gollum::Wiki.new("my-gollum-repo.git") wiki = Gollum::Wiki.new("my-gollum-repo.git")
# => <Gollum::Wiki> # => <Gollum::Wiki>
By default, internal wiki links are all absolute from the root. To specify a different base path, you can send specify the `:base_path` option: By default, internal wiki links are all absolute from the root. To specify a different base path, you can specify the `:base_path` option:
wiki = Gollum::Wiki.new("my-gollum-repo.git", :base_path => "/wiki") wiki = Gollum::Wiki.new("my-gollum-repo.git", :base_path => "/wiki")
+2 -2
View File
@@ -47,7 +47,7 @@ task :default => :test
require 'rake/testtask' require 'rake/testtask'
Rake::TestTask.new(:test) do |test| Rake::TestTask.new(:test) do |test|
test.libs << 'lib' << 'test' test.libs << 'lib' << 'test' << '.'
test.pattern = 'test/**/test_*.rb' test.pattern = 'test/**/test_*.rb'
test.verbose = true test.verbose = true
end end
@@ -95,7 +95,7 @@ task :release => :build do
sh "git commit --allow-empty -a -m 'Release #{version}'" sh "git commit --allow-empty -a -m 'Release #{version}'"
sh "git tag v#{version}" sh "git tag v#{version}"
sh "git push origin master" sh "git push origin master"
sh "git push v#{version}" sh "git push origin v#{version}"
sh "gem push pkg/#{name}-#{version}.gem" sh "gem push pkg/#{name}-#{version}.gem"
end end
+69 -7
View File
@@ -8,18 +8,17 @@ Gollum is a multi-format Wiki Engine/API/Frontend.
Basic Command Line Usage: Basic Command Line Usage:
gollum [OPTIONS] [PATH] gollum [OPTIONS] [PATH]
PATH The path to the Gollum repository. PATH The path to the Gollum repository (default .).
Options: Options:
HELP HELP
require 'optparse' require 'optparse'
require 'rubygems' require 'rubygems'
require 'gollum/frontend/app' require 'gollum'
exec = {} exec = {}
options = {} options = { 'port' => 4567, 'bind' => '127.0.0.1' }
opts = OptionParser.new do |opts| opts = OptionParser.new do |opts|
opts.banner = help opts.banner = help
@@ -27,15 +26,78 @@ opts = OptionParser.new do |opts|
options['port'] = port.to_i options['port'] = port.to_i
end end
opts.on("--host [HOST]", "Hostname or IP address to listen on (default 0.0.0.0).") do |host|
options['bind'] = host
end
opts.on("--version", "Display current version.") do opts.on("--version", "Display current version.") do
puts "Gollum " + Gollum::VERSION puts "Gollum " + Gollum::VERSION
exit 0 exit 0
end end
opts.on("--irb", "Start an irb process with gollum loaded for the current wiki.") do
options['irb'] = true
end
end end
# Read command line options into `options` hash # Read command line options into `options` hash
opts.parse! begin
opts.parse!
rescue OptionParser::InvalidOption
puts "gollum: #{$!.message}"
puts "gollum: try 'gollum --help' for more information"
exit
end
$path = ARGV[0] || Dir.pwd gollum_path = ARGV[0] || Dir.pwd
Precious::App.run!(options) if options['irb']
require 'irb'
# http://jameskilton.com/2009/04/02/embedding-irb-into-your-ruby-application/
module IRB # :nodoc:
def self.start_session(binding)
unless @__initialized
args = ARGV
ARGV.replace(ARGV.dup)
IRB.setup(nil)
ARGV.replace(args)
@__initialized = true
end
ws = WorkSpace.new(binding)
irb = Irb.new(ws)
@CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
@CONF[:MAIN_CONTEXT] = irb.context
catch(:IRB_EXIT) do
irb.eval_input
end
end
end
begin
wiki = Gollum::Wiki.new(gollum_path)
if !wiki.exist? then raise Grit::InvalidGitRepositoryError end
puts "Loaded Gollum wiki at #{File.expand_path(gollum_path).inspect}."
puts
puts %( page = wiki.page('page-name'))
puts %( # => <Gollum::Page>)
puts
puts %( page.raw_data)
puts %( # => "# My wiki page")
puts
puts %( page.formatted_data)
puts %( # => "<h1>My wiki page</h1>")
puts
puts "Check out the Gollum README for more."
IRB.start_session(binding)
rescue Grit::InvalidGitRepositoryError, Grit::NoSuchPathError
puts "Invalid Gollum wiki at #{File.expand_path(gollum_path).inspect}"
exit 0
end
else
require 'gollum/frontend/app'
Precious::App.set(:gollum_path, gollum_path)
Precious::App.run!(options)
end
+14 -6
View File
@@ -4,8 +4,8 @@ Gem::Specification.new do |s|
s.rubygems_version = '1.3.5' s.rubygems_version = '1.3.5'
s.name = 'gollum' s.name = 'gollum'
s.version = '1.0.0' s.version = '1.1.1'
s.date = '2010-08-12' s.date = '2010-10-28'
s.rubyforge_project = 'gollum' s.rubyforge_project = 'gollum'
s.summary = "A simple, Git-powered wiki." s.summary = "A simple, Git-powered wiki."
@@ -23,17 +23,19 @@ Gem::Specification.new do |s|
s.rdoc_options = ["--charset=UTF-8"] s.rdoc_options = ["--charset=UTF-8"]
s.extra_rdoc_files = %w[README.md LICENSE] s.extra_rdoc_files = %w[README.md LICENSE]
s.add_dependency('grit', "~> 2.0") s.add_dependency('grit', "~> 2.3")
s.add_dependency('github-markup', [">= 0.4.0", "< 1.0.0"]) s.add_dependency('github-markup', [">= 0.4.0", "< 1.0.0"])
s.add_dependency('albino', "~> 1.0") s.add_dependency('albino', "~> 1.1.1")
s.add_dependency('sinatra', "~> 1.0") s.add_dependency('sinatra', "~> 1.0")
s.add_dependency('mustache', [">= 0.11.2", "< 1.0.0"]) s.add_dependency('mustache', [">= 0.11.2", "< 1.0.0"])
s.add_dependency('sanitize', "~> 1.0") s.add_dependency('sanitize', "~> 1.1")
s.add_dependency('nokogiri', "~> 1.4") s.add_dependency('nokogiri', "~> 1.4")
s.add_development_dependency('shoulda') s.add_development_dependency('RedCloth')
s.add_development_dependency('mocha') s.add_development_dependency('mocha')
s.add_development_dependency('org-ruby') s.add_development_dependency('org-ruby')
s.add_development_dependency('rdiscount')
s.add_development_dependency('shoulda')
# = MANIFEST = # = MANIFEST =
s.files = %w[ s.files = %w[
@@ -46,6 +48,7 @@ Gem::Specification.new do |s|
gollum.gemspec gollum.gemspec
lib/gollum.rb lib/gollum.rb
lib/gollum/albino.rb lib/gollum/albino.rb
lib/gollum/blob_entry.rb
lib/gollum/file.rb lib/gollum/file.rb
lib/gollum/frontend/app.rb lib/gollum/frontend/app.rb
lib/gollum/frontend/public/css/editbar.css lib/gollum/frontend/public/css/editbar.css
@@ -443,19 +446,24 @@ Gem::Specification.new do |s|
lib/gollum/frontend/templates/create.mustache lib/gollum/frontend/templates/create.mustache
lib/gollum/frontend/templates/edit.mustache lib/gollum/frontend/templates/edit.mustache
lib/gollum/frontend/templates/editbar.mustache lib/gollum/frontend/templates/editbar.mustache
lib/gollum/frontend/templates/error.mustache
lib/gollum/frontend/templates/history.mustache lib/gollum/frontend/templates/history.mustache
lib/gollum/frontend/templates/layout.mustache lib/gollum/frontend/templates/layout.mustache
lib/gollum/frontend/templates/page.mustache lib/gollum/frontend/templates/page.mustache
lib/gollum/frontend/templates/search.mustache
lib/gollum/frontend/views/compare.rb lib/gollum/frontend/views/compare.rb
lib/gollum/frontend/views/create.rb lib/gollum/frontend/views/create.rb
lib/gollum/frontend/views/edit.rb lib/gollum/frontend/views/edit.rb
lib/gollum/frontend/views/editable.rb lib/gollum/frontend/views/editable.rb
lib/gollum/frontend/views/error.rb
lib/gollum/frontend/views/history.rb lib/gollum/frontend/views/history.rb
lib/gollum/frontend/views/layout.rb lib/gollum/frontend/views/layout.rb
lib/gollum/frontend/views/page.rb lib/gollum/frontend/views/page.rb
lib/gollum/frontend/views/search.rb
lib/gollum/markup.rb lib/gollum/markup.rb
lib/gollum/page.rb lib/gollum/page.rb
lib/gollum/pagination.rb lib/gollum/pagination.rb
lib/gollum/ruby1.8.rb
lib/gollum/wiki.rb lib/gollum/wiki.rb
templates/formatting.html templates/formatting.html
test/examples/lotr.git/HEAD test/examples/lotr.git/HEAD
+25 -1
View File
@@ -1,13 +1,18 @@
# stdlib # stdlib
require 'digest/md5' require 'digest/md5'
require 'ostruct'
# external # external
require 'grit' require 'grit'
require 'github/markup' require 'github/markup'
require 'sanitize' require 'sanitize'
# ruby 1.8 compatibility
require 'gollum/ruby1.8'
# internal # internal
require 'gollum/pagination' require 'gollum/pagination'
require 'gollum/blob_entry'
require 'gollum/wiki' require 'gollum/wiki'
require 'gollum/page' require 'gollum/page'
require 'gollum/file' require 'gollum/file'
@@ -15,7 +20,7 @@ require 'gollum/markup'
require 'gollum/albino' require 'gollum/albino'
module Gollum module Gollum
VERSION = '1.0.0' VERSION = '1.1.1'
SANITIZATION_OPTIONS = { SANITIZATION_OPTIONS = {
:elements => [ :elements => [
@@ -54,5 +59,24 @@ module Gollum
'img' => {'href' => ['http', 'https', :relative]} 'img' => {'href' => ['http', 'https', :relative]}
} }
} }
HISTORY_SANITIZATION_OPTIONS = SANITIZATION_OPTIONS.merge(
:add_attributes => {
'a' => {'rel' => 'nofollow'}
}
)
class Error < StandardError; end
class DuplicatePageError < Error
attr_accessor :dir
attr_accessor :existing_path
attr_accessor :attempted_path
def initialize(dir, existing, attempted, message = nil)
@dir = dir
@existing_path = existing
@attempted_path = attempted
super(message || "Cannot write #{@dir}/#{@attempted_path}, found #{@dir}/#{@existing_path}.")
end
end
end end
+13
View File
@@ -14,4 +14,17 @@ class Gollum::Albino < Albino
html.sub!(%r{</pre></div>\Z}, "</pre>\n</div>") html.sub!(%r{</pre></div>\Z}, "</pre>\n</div>")
html html
end end
# Hotfix for vulnerable versions of Albino
if !instance_methods.include?('shell_escape')
def convert_options(options = {})
@options.merge(options).inject('') do |string, (flag, value)|
string + " -#{flag} #{shell_escape value}"
end
end
def shell_escape(str)
str.to_s.gsub("'", "\\\\'").gsub(";", '\\;')
end
end
end end
+71
View File
@@ -0,0 +1,71 @@
module Gollum
class BlobEntry
# Gets the String SHA for this blob.
attr_reader :sha
# Gets the String full path for this blob.
attr_reader :path
def initialize(sha, path)
@sha = sha
@path = path
@dir = @name = @blob = nil
end
# Gets the normalized directory path for this blob.
def dir
@dir ||= self.class.normalize_dir(::File.dirname(@path))
end
# Gets the String file base name for this blob.
def name
@name ||= ::File.basename(@path)
end
# Gets a Grit::Blob instance for this blob.
#
# repo - Grit::Repo instance for the Grit::Blob.
#
# Returns an unbaked Grit::Blob instance.
def blob(repo)
@blob ||= Grit::Blob.create(repo, :id => @sha, :name => @name)
end
# Gets a Page instance for this blob.
#
# wiki - Gollum::Wiki instance for the Gollum::Page
#
# Returns a Gollum::Page instance.
def page(wiki, commit)
blob = self.blob(wiki.repo)
page = wiki.page_class.new(wiki).populate(blob, self.dir)
page.version = commit
page
end
def inspect
%(#<Gollum::BlobEntry #{@sha} #{@path}>)
end
# Normalizes a given directory name for searching through tree paths.
# Ensures that a directory begins with a slash, or
#
# normalize_dir("") # => ""
# normalize_dir(".") # => ""
# normalize_dir("foo") # => "/foo"
# normalize_dir("/foo/") # => "/foo"
# normalize_dir("/") # => ""
#
# dir - String directory name.
#
# Returns a normalized String directory name, or nil if no directory
# is given.
def self.normalize_dir(dir)
if dir
dir = ::File.expand_path(dir, '/')
dir = '' if dir == '/'
end
dir
end
end
end
+11 -6
View File
@@ -33,6 +33,11 @@ module Gollum
# Public: The String path of the file. # Public: The String path of the file.
attr_reader :path attr_reader :path
# Public: The String mime type of the file.
def mime_type
@blob.mime_type
end
######################################################################### #########################################################################
# #
# Internal Methods # Internal Methods
@@ -46,14 +51,14 @@ module Gollum
# #
# Returns a Gollum::File or nil if the file could not be found. # Returns a Gollum::File or nil if the file could not be found.
def find(name, version) def find(name, version)
commit = @wiki.repo.commit(version) checked = name.downcase
if blob = commit.tree / name map = @wiki.tree_map_for(version)
@blob = blob sha = @wiki.ref_map[version] || version
if entry = map.detect { |entry| entry.path.downcase == checked }
@path = name @path = name
@version = commit @blob = Grit::Blob.create(@wiki.repo, :id => entry.sha, :name => entry.name)
@version = Grit::Commit.create(@wiki.repo, :id => sha)
self self
else
nil
end end
end end
end end
+33 -23
View File
@@ -1,3 +1,4 @@
require 'cgi'
require 'sinatra' require 'sinatra'
require 'gollum' require 'gollum'
require 'mustache/sinatra' require 'mustache/sinatra'
@@ -12,6 +13,7 @@ module Precious
dir = File.dirname(File.expand_path(__FILE__)) dir = File.dirname(File.expand_path(__FILE__))
# We want to serve public assets for now # We want to serve public assets for now
set :public, "#{dir}/public" set :public, "#{dir}/public"
set :static, true set :static, true
@@ -38,9 +40,9 @@ module Precious
show_page_or_file('Home') show_page_or_file('Home')
end end
get '/edit/:name' do get '/edit/*' do
@name = params[:name] @name = params[:splat].first
wiki = Gollum::Wiki.new($path) wiki = Gollum::Wiki.new(settings.gollum_path)
if page = wiki.page(@name) if page = wiki.page(@name)
@page = page @page = page
@content = page.raw_data @content = page.raw_data
@@ -50,38 +52,43 @@ module Precious
end end
end end
post '/edit/:name' do post '/edit/*' do
name = params[:name] name = params[:splat].first
wiki = Gollum::Wiki.new($path) wiki = Gollum::Wiki.new(settings.gollum_path)
page = wiki.page(name) page = wiki.page(name)
format = params[:format].intern format = params[:format].intern
name = params[:rename] if params[:rename] name = params[:rename] if params[:rename]
wiki.update_page(page, name, format, params[:content], commit_message) wiki.update_page(page, name, format, params[:content], commit_message)
redirect "/#{Gollum::Page.cname name}" redirect "/#{CGI.escape(Gollum::Page.cname(name))}"
end end
post '/create/:name' do post '/create/*' do
name = params[:page] name = params[:page]
wiki = Gollum::Wiki.new($path) wiki = Gollum::Wiki.new(settings.gollum_path)
format = params[:format].intern format = params[:format].intern
begin
wiki.write_page(name, format, params[:content], commit_message) wiki.write_page(name, format, params[:content], commit_message)
redirect "/#{name}" redirect "/#{CGI.escape(name)}"
rescue Gollum::DuplicatePageError => e
@message = "Duplicate page: #{e.message}"
mustache :error
end
end end
post '/preview' do post '/preview' do
format = params['wiki_format'] format = params['wiki_format']
data = params['text'] data = params['text']
wiki = Gollum::Wiki.new($path) wiki = Gollum::Wiki.new(settings.gollum_path)
wiki.preview_page("Preview", data, format).formatted_data wiki.preview_page("Preview", data, format).formatted_data
end end
get '/history/:name' do get '/history/:name' do
@name = params[:name] @name = params[:name]
wiki = Gollum::Wiki.new($path) wiki = Gollum::Wiki.new(settings.gollum_path)
@page = wiki.page(@name) @page = wiki.page(@name)
@page_num = [params[:page].to_i, 1].max @page_num = [params[:page].to_i, 1].max
@versions = @page.versions :page => @page_num @versions = @page.versions :page => @page_num
@@ -91,10 +98,10 @@ module Precious
post '/compare/:name' do post '/compare/:name' do
@versions = params[:versions] || [] @versions = params[:versions] || []
if @versions.size < 2 if @versions.size < 2
redirect "/history/#{params[:name]}" redirect "/history/#{CGI.escape(params[:name])}"
else else
redirect "/compare/%s/%s...%s" % [ redirect "/compare/%s/%s...%s" % [
params[:name], CGI.escape(params[:name]),
@versions.last, @versions.last,
@versions.first] @versions.first]
end end
@@ -103,7 +110,7 @@ module Precious
get '/compare/:name/:version_list' do get '/compare/:name/:version_list' do
@name = params[:name] @name = params[:name]
@versions = params[:version_list].split(/\.{2,3}/) @versions = params[:version_list].split(/\.{2,3}/)
wiki = Gollum::Wiki.new($path) wiki = Gollum::Wiki.new(settings.gollum_path)
@page = wiki.page(@name) @page = wiki.page(@name)
diffs = wiki.repo.diff(@versions.first, @versions.last, @page.path) diffs = wiki.repo.diff(@versions.first, @versions.last, @page.path)
@diff = diffs.first @diff = diffs.first
@@ -112,7 +119,7 @@ module Precious
get %r{/(.+?)/([0-9a-f]{40})} do get %r{/(.+?)/([0-9a-f]{40})} do
name = params[:captures][0] name = params[:captures][0]
wiki = Gollum::Wiki.new($path) wiki = Gollum::Wiki.new(settings.gollum_path)
if page = wiki.page(name, params[:captures][1]) if page = wiki.page(name, params[:captures][1])
@page = page @page = page
@name = name @name = name
@@ -123,18 +130,26 @@ module Precious
end end
end end
get '/search' do
@query = params[:q]
wiki = Gollum::Wiki.new(settings.gollum_path)
@results = wiki.search @query
mustache :search
end
get '/*' do get '/*' do
show_page_or_file(params[:splat].first) show_page_or_file(params[:splat].first)
end end
def show_page_or_file(name) def show_page_or_file(name)
wiki = Gollum::Wiki.new($path) wiki = Gollum::Wiki.new(settings.gollum_path)
if page = wiki.page(name) if page = wiki.page(name)
@page = page @page = page
@name = name @name = name
@content = page.formatted_data @content = page.formatted_data
mustache :page mustache :page
elsif file = wiki.file(name) elsif file = wiki.file(name)
content_type file.mime_type
file.raw_data file.raw_data
else else
@name = name @name = name
@@ -143,12 +158,7 @@ module Precious
end end
def commit_message def commit_message
message = params[:message] { :message => params[:message] }
author_name = `git config --get user.name`.strip || 'Anonymous'
author_email = `git config --get user.email`.strip || 'anon@anon.com'
{ :message => message,
:name => author_name,
:email => author_email }
end end
end end
end end
@@ -1,7 +1,7 @@
<div class="guide"> <div class="guide">
<div class="main"> <div class="main">
<div class="actions"> <div class="actions">
<a href="/{{name}}">&laquo; Back</a> <a href="/{{escaped_name}}">&laquo; Back</a>
</div> </div>
<h1>{{title}}: {{before}} &rarr; {{after}}</h1> <h1>{{title}}: {{before}} &rarr; {{after}}</h1>
<div id="files"> <div id="files">
@@ -2,7 +2,7 @@
<a href="/">&laquo; Home</a> <a href="/">&laquo; Home</a>
<h1>{{title}}</h1> <h1>{{title}}</h1>
<form class="new_wiki" method="post" action="/create/{{name}}"> <form class="new_wiki" method="post" action="/create/{{escaped_name}}">
<div> <div>
<label> <label>
Title<br /> Title<br />
+2 -2
View File
@@ -1,8 +1,8 @@
<div class="write"> <div class="write">
<a href="/{{name}}">&laquo; Back</a> <a href="/{{escaped_name}}">&laquo; Back</a>
<h1>{{title}}</h1> <h1>{{title}}</h1>
<form class="edit_wiki" method="post" action="/edit/{{name}}"> <form class="edit_wiki" method="post" action="/edit/{{escaped_name}}">
<div> <div>
<label> <label>
Title<br /> Title<br />
@@ -0,0 +1,11 @@
<div class="guide">
<div class="main">
<div class="actions">
<a href="/">Home</a>
</div>
<h1>Error</h1>
<div class="error">
{{message}}
</div>
</div>
</div>
@@ -1,10 +1,10 @@
<div class="guide"> <div class="guide">
<div class="main"> <div class="main">
<div class="actions"> <div class="actions">
<a href="/{{name}}">&laquo; Back</a> <a href="/{{escaped_name}}">&laquo; Back</a>
</div> </div>
<h1>{{title}}</h1> <h1>{{title}}</h1>
<form id="history" method="post" action="/compare/{{name}}"> <form id="history" method="post" action="/compare/{{escaped_name}}">
<table class="commits" cellpadding="0" cellspacing="0"> <table class="commits" cellpadding="0" cellspacing="0">
<tr> <tr>
<th colspan="5"> <th colspan="5">
@@ -17,7 +17,7 @@
<input name="versions[]" type="checkbox" value="{{id}}" /> <input name="versions[]" type="checkbox" value="{{id}}" />
</td> </td>
<td class="sha"> <td class="sha">
<a href="/{{name}}/{{id}}">{{id7}}</a> <a href="/{{escaped_name}}/{{id}}">{{id7}}</a>
</td> </td>
<td nowrap class="author"> <td nowrap class="author">
<img src="http://www.gravatar.com/avatar/{{gravatar}}?s=16" alt="Gravatar" /> <img src="http://www.gravatar.com/avatar/{{gravatar}}?s=16" alt="Gravatar" />
+9 -3
View File
@@ -1,7 +1,13 @@
<div class="guide"> <div class="guide">
<div class="main"> <div class="main">
<div class="actions"> <div class="actions">
<a href="/">Home</a> | <a href="/edit/{{name}}">Edit</a> <form action="/search" method="get">
<div>
<a href="/">Home</a> |
<a href="/edit/{{escaped_name}}">Edit</a> |
<input type="search" name="q" size="10" /> <input type="submit" value="search" />
</div>
</form>
</div> </div>
<h1>{{title}}</h1> <h1>{{title}}</h1>
<div class="content wikistyle gollum {{format}}"> <div class="content wikistyle gollum {{format}}">
@@ -18,10 +24,10 @@
<div style="float: left;"> <div style="float: left;">
<small>Last edited by <b>{{author}}</b>, {{date}}</small> <small>Last edited by <b>{{author}}</b>, {{date}}</small>
<div class="actions"> <div class="actions">
<a href="/">Home</a> | <a href="/edit/{{name}}">Edit</a> <a href="/">Home</a> | <a href="/edit/{{escaped_name}}">Edit</a>
</div> </div>
</div> </div>
<div style="float: right;"> <div style="float: right;">
<a href="/history/{{name}}">View Revision History</a> <a href="/history/{{escaped_name}}">View Revision History</a>
</div> </div>
</div> </div>
@@ -0,0 +1,19 @@
<div class="guide">
<div class="main">
<div class="actions">
<form action="/search" method="get">
<a href="/">Home</a> | <input type="search" name="q" size="10" /> <input type="submit" value="search" />
</form>
</div>
<h1>Search for “{{query}}”</h1>
<div class="content wikistyle gollum">
{{#has_results}}
<ul>
{{#results}}
<li><a href="/{{name}}">{{name}} ({{count}})</a></li>
{{/results}}
</ul>
{{/has_results}}
</div>
</div>
</div>
+7
View File
@@ -0,0 +1,7 @@
module Precious
module Views
class Error < Layout
attr_reader :message
end
end
end
+6
View File
@@ -1,3 +1,5 @@
require 'cgi'
module Precious module Precious
module Views module Views
class Layout < Mustache class Layout < Mustache
@@ -6,6 +8,10 @@ module Precious
attr_reader :name attr_reader :name
def escaped_name
CGI.escape(@name)
end
def title def title
"Home" "Home"
end end
+12
View File
@@ -0,0 +1,12 @@
module Precious
module Views
class Search < Layout
attr_reader :content, :page, :footer, :results, :query
def has_results
!@results.empty?
end
end
end
end
+75 -28
View File
@@ -1,4 +1,5 @@
require 'digest/sha1' require 'digest/sha1'
require 'cgi'
module Gollum module Gollum
class Markup class Markup
@@ -21,8 +22,14 @@ module Gollum
# Render the content with Gollum wiki syntax on top of the file's own # Render the content with Gollum wiki syntax on top of the file's own
# markup language. # markup language.
# #
# no_follow - Boolean that determines if rel="nofollow" is added to all
# <a> tags.
#
# Returns the formatted String content. # Returns the formatted String content.
def render def render(no_follow = false)
sanitize_options = no_follow ?
HISTORY_SANITIZATION_OPTIONS :
SANITIZATION_OPTIONS
data = extract_tex(@data) data = extract_tex(@data)
data = extract_code(data) data = extract_code(data)
data = extract_tags(data) data = extract_tags(data)
@@ -36,9 +43,9 @@ module Gollum
end end
data = process_tags(data) data = process_tags(data)
data = process_code(data) data = process_code(data)
data = Sanitize.clean(data, SANITIZATION_OPTIONS) data = Sanitize.clean(data, sanitize_options)
data = process_tex(data) data = process_tex(data)
data = data.gsub(/<p><\/p>/, '') data.gsub!(/<p><\/p>/, '')
data data
end end
@@ -115,27 +122,32 @@ module Gollum
# final markup. # final markup.
# #
# data - The String data (with placeholders). # data - The String data (with placeholders).
# no_follow - Boolean that determines if rel="nofollow" is added to all
# <a> tags.
# #
# Returns the marked up String data. # Returns the marked up String data.
def process_tags(data) def process_tags(data, no_follow = false)
@tagmap.each do |id, tag| @tagmap.each do |id, tag|
data.gsub!(id, process_tag(tag)) data.gsub!(id, process_tag(tag, no_follow))
end end
data data
end end
# Process a single tag into its final HTML form. # Process a single tag into its final HTML form.
# #
# tag - The String tag contents (the stuff inside the double brackets). # tag - The String tag contents (the stuff inside the double
# brackets).
# no_follow - Boolean that determines if rel="nofollow" is added to all
# <a> tags.
# #
# Returns the String HTML version of the tag. # Returns the String HTML version of the tag.
def process_tag(tag) def process_tag(tag, no_follow = false)
if html = process_image_tag(tag) if html = process_image_tag(tag)
return html html
elsif html = process_file_link_tag(tag) elsif html = process_file_link_tag(tag, no_follow)
return html html
else else
return process_page_link_tag(tag) process_page_link_tag(tag, no_follow)
end end
end end
@@ -150,7 +162,7 @@ module Gollum
name = parts[0].strip name = parts[0].strip
path = if file = find_file(name) path = if file = find_file(name)
::File.join @wiki.base_path, file.path ::File.join @wiki.base_path, file.path
elsif name =~ /^https?:\/\/.+(jpg|png|gif|svg|bmp)$/ elsif name =~ /^https?:\/\/.+(jpg|png|gif|svg|bmp)$/i
name name
end end
@@ -200,7 +212,7 @@ module Gollum
classes << 'frame' if opts['frame'] classes << 'frame' if opts['frame']
%{<span class="#{classes.join(' ')}">} + %{<span class="#{classes.join(' ')}">} +
%{<span>} + %{<span>} +
%{<img src="/#{file.path}" #{attr_string}/>} + %{<img src="#{path}" #{attr_string}/>} +
(alt ? %{<span>#{alt}</span>} : '') + (alt ? %{<span>#{alt}</span>} : '') +
%{</span>} + %{</span>} +
%{</span>} %{</span>}
@@ -228,11 +240,14 @@ module Gollum
# Attempt to process the tag as a file link tag. # Attempt to process the tag as a file link tag.
# #
# tag - The String tag contents (the stuff inside the double brackets). # tag - The String tag contents (the stuff inside the double
# brackets).
# no_follow - Boolean that determines if rel="nofollow" is added to all
# <a> tags.
# #
# Returns the String HTML if the tag is a valid file link tag or nil # Returns the String HTML if the tag is a valid file link tag or nil
# if it is not. # if it is not.
def process_file_link_tag(tag) def process_file_link_tag(tag, no_follow = false)
parts = tag.split('|') parts = tag.split('|')
name = parts[0].strip name = parts[0].strip
path = parts[1] && parts[1].strip path = parts[1] && parts[1].strip
@@ -244,37 +259,49 @@ module Gollum
nil nil
end end
if name && path && file tag = if name && path && file
%{<a href="#{::File.join @wiki.base_path, file.path}">#{name}</a>} %{<a href="#{::File.join @wiki.base_path, file.path}">#{name}</a>}
elsif name && path elsif name && path
%{<a href="#{path}">#{name}</a>} %{<a href="#{path}">#{name}</a>}
else else
nil nil
end end
if tag && no_follow
tag.sub! /^<a/, '<a ref="nofollow"'
end
tag
end end
# Attempt to process the tag as a page link tag. # Attempt to process the tag as a page link tag.
# #
# tag - The String tag contents (the stuff inside the double brackets). # tag - The String tag contents (the stuff inside the double
# brackets).
# no_follow - Boolean that determines if rel="nofollow" is added to all
# <a> tags.
# #
# Returns the String HTML if the tag is a valid page link tag or nil # Returns the String HTML if the tag is a valid page link tag or nil
# if it is not. # if it is not.
def process_page_link_tag(tag) def process_page_link_tag(tag, no_follow = false)
parts = tag.split('|') parts = tag.split('|')
name = parts[0].strip name = parts[0].strip
cname = Page.cname((parts[1] || parts[0]).strip) cname = Page.cname((parts[1] || parts[0]).strip)
if name =~ %r{^https?://} && parts[1].nil? tag = if name =~ %r{^https?://} && parts[1].nil?
%{<a href="#{name}">#{name}</a>} %{<a href="#{name}">#{name}</a>}
else else
if page = @wiki.page(cname)
link = ::File.join(@wiki.base_path, Page.cname(page.name))
presence = "present"
else
link = ::File.join(@wiki.base_path, cname)
presence = "absent" presence = "absent"
link_name = cname
page, extra = find_page_from_name(cname)
if page
link_name = Page.cname(page.name)
presence = "present"
end end
%{<a class="internal #{presence}" href="#{link}">#{name}</a>} link = ::File.join(@wiki.base_path, CGI.escape(link_name))
%{<a class="internal #{presence}" href="#{link}#{extra}">#{name}</a>}
end end
if tag && no_follow
tag.sub! /^<a/, '<a ref="nofollow"'
end
tag
end end
# Find the given file in the repo. # Find the given file in the repo.
@@ -291,6 +318,23 @@ module Gollum
end end
end end
# Find a page from a given cname. If the page has an anchor (#) and has
# no match, strip the anchor and try again.
#
# cname - The String canonical page name.
#
# Returns a Gollum::Page instance if a page is found, or an Array of
# [Gollum::Page, String extra] if a page without the extra anchor data
# is found.
def find_page_from_name(cname)
if page = @wiki.page(cname)
return page
end
if pos = cname.index('#')
[@wiki.page(cname[0...pos]), cname[pos..-1]]
end
end
######################################################################### #########################################################################
# #
# Code # Code
@@ -303,7 +347,7 @@ module Gollum
# #
# Returns the placeholder'd String data. # Returns the placeholder'd String data.
def extract_code(data) def extract_code(data)
data.gsub(/^``` ?(.+?)\r?\n(.+?)\r?\n```\r?$/m) do data.gsub(/^``` ?([^\r\n]+)?\r?\n(.+?)\r?\n```\r?$/m) do
id = Digest::SHA1.hexdigest($2) id = Digest::SHA1.hexdigest($2)
@codemap[id] = { :lang => $1, :code => $2 } @codemap[id] = { :lang => $1, :code => $2 }
id id
@@ -318,12 +362,15 @@ module Gollum
# Returns the marked up String data. # Returns the marked up String data.
def process_code(data) def process_code(data)
@codemap.each do |id, spec| @codemap.each do |id, spec|
lang = spec[:lang]
code = spec[:code] code = spec[:code]
if code.all? { |line| line =~ /\A\r?\n\Z/ || line =~ /^( |\t)/ } if code.lines.all? { |line| line =~ /\A\r?\n\Z/ || line =~ /^( |\t)/ }
code.gsub!(/^( |\t)/m, '') code.gsub!(/^( |\t)/m, '')
end end
if lang = spec[:lang]
data.gsub!(id, Gollum::Albino.new(code, lang).colorize) data.gsub!(id, Gollum::Albino.new(code, lang).colorize)
else
data.gsub!(id, "<pre><code>#{CGI.escapeHTML(code)}</code></pre>")
end
end end
data data
end end
+46 -53
View File
@@ -14,6 +14,11 @@ module Gollum
:asciidoc => "AsciiDoc", :asciidoc => "AsciiDoc",
:pod => "Pod" } :pod => "Pod" }
# Sets a Boolean determing whether this page is a historical version.
#
# Returns nothing.
attr_writer :historical
# Checks if a filename has a valid extension understood by GitHub::Markup. # Checks if a filename has a valid extension understood by GitHub::Markup.
# #
# filename - String filename, like "Home.md". # filename - String filename, like "Home.md".
@@ -35,6 +40,14 @@ module Gollum
filename =~ /^_/ ? false : match filename =~ /^_/ ? false : match
end end
# Reusable filter to turn a filename (without path) into a canonical name.
# Strips extension, converts spaces to dashes.
#
# Returns the filtered String.
def self.canonicalize_filename(filename)
filename.split('.')[0..-2].join('.').gsub('-', ' ')
end
# Public: Initialize a page. # Public: Initialize a page.
# #
# wiki - The Gollum::Wiki in question. # wiki - The Gollum::Wiki in question.
@@ -57,7 +70,7 @@ module Gollum
# #
# Returns the String name. # Returns the String name.
def name def name
filename.split('.')[0..-2].join('.').gsub('-', ' ') self.class.canonicalize_filename(filename)
end end
# Public: If the first element of a formatted page is an <h1> tag it can # Public: If the first element of a formatted page is an <h1> tag it can
@@ -107,7 +120,7 @@ module Gollum
# #
# Returns the String data. # Returns the String data.
def formatted_data def formatted_data
@blob && Gollum::Markup.new(self).render @blob && Gollum::Markup.new(self).render(historical?)
end end
# Public: The format of the page. # Public: The format of the page.
@@ -174,20 +187,24 @@ module Gollum
dirs = self.path.split('/') dirs = self.path.split('/')
dirs.pop dirs.pop
map = @wiki.tree_map_for(self.version.id)
while !dirs.empty? while !dirs.empty?
tree = self.version.tree / dirs.join('/') if page = find_page_in_tree(map, '_Footer', dirs.join('/'))
if page = find_page_in_this_tree(tree, dirs.join('/'), '_Footer')
return page return page
end end
dirs.pop dirs.pop
end end
tree = self.version.tree find_page_in_tree(map, '_Footer', '')
if page = find_page_in_this_tree(tree, '', '_Footer')
return page
end end
return nil # Gets a Boolean determining whether this page is a historical version.
# Historical pages are pulled using exact SHA hashes and format all links
# with rel="nofollow"
#
# Returns true if the page is pulled from a named branch or tag, or false.
def historical?
!!@historical
end end
######################################################################### #########################################################################
@@ -207,7 +224,9 @@ module Gollum
# #
# Returns the String canonical name. # Returns the String canonical name.
def self.cname(name) def self.cname(name)
name.gsub(%r{[ /<>]}, '-') name.respond_to?(:gsub) ?
name.gsub(%r{[ /<>]}, '-') :
''
end end
# Convert a format Symbol into an extension String. # Convert a format Symbol into an extension String.
@@ -251,61 +270,35 @@ module Gollum
# #
# Returns a Gollum::Page or nil if the page could not be found. # Returns a Gollum::Page or nil if the page could not be found.
def find(name, version) def find(name, version)
if commit = @wiki.repo.commit(version) map = @wiki.tree_map_for(version)
if page = find_page_in_tree(commit.tree, name) if page = find_page_in_tree(map, name)
page.version = commit sha = @wiki.ref_map[version] || version
page.version = Grit::Commit.create(@wiki.repo, :id => sha)
page.historical = sha == version
page page
end end
end rescue Grit::GitRuby::Repository::NoSuchShaFound
end end
# Find a page in a given tree. # Find a page in a given tree.
# #
# tree - The Grit::Tree in which to look. # map - The Array tree map from Wiki#tree_map.
# name - The canonical String page name. # name - The canonical String page name.
# checked_dir - Optional String of the directory a matching page needs
# to be in. The string should
# #
# Returns a Gollum::Page or nil if the page could not be found. # Returns a Gollum::Page or nil if the page could not be found.
def find_page_in_tree(tree, name) def find_page_in_tree(map, name, checked_dir = nil)
treemap = {} return nil if name.to_s.empty?
trees = [tree] if checked_dir = BlobEntry.normalize_dir(checked_dir)
checked_dir.downcase!
while !trees.empty?
ptree = trees.shift
ptree.contents.each do |item|
case item
when Grit::Blob
if page_match(name, item.name)
return self.class.new(@wiki).populate(item, tree_path(treemap, ptree))
end
when Grit::Tree
treemap[item] = ptree
trees << item
end
end
end end
return nil # nothing was found map.each do |entry|
end next if entry.name.to_s.empty?
next unless checked_dir.nil? || entry.dir.downcase == checked_dir
# Find a page in a given tree without recursing into subtrees. next unless page_match(name, entry.name)
# return entry.page(@wiki, @version)
# tree - The Grit::Tree in which to look.
# dir - The String path of the given Grit::Tree.
# name - The canonical String page name.
#
# Returns a Gollum::Page or nil if the page could not be found.
def find_page_in_this_tree(tree, dir, name)
treemap = {}
tree.contents.each do |item|
case item
when Grit::Blob
if page_match(name, item.name)
path = dir == '' ? '' : ::File.join('/', dir)
page = self.class.new(@wiki).populate(item, path)
page.version = self.version
return page
end
end
end end
return nil # nothing was found return nil # nothing was found
+3
View File
@@ -0,0 +1,3 @@
class String
alias :lines :to_a if defined?(RUBY_VERSION) && RUBY_VERSION < '1.9'
end
+318 -105
View File
@@ -9,6 +9,12 @@ module Gollum
# Sets the file class used by all instances of this Wiki. # Sets the file class used by all instances of this Wiki.
attr_writer :file_class attr_writer :file_class
# Sets the default name for commits.
attr_accessor :default_committer_name
# Sets the default email for commits.
attr_accessor :default_committer_email
# Gets the page class used by all instances of this Wiki. # Gets the page class used by all instances of this Wiki.
# Default: Gollum::Page. # Default: Gollum::Page.
def page_class def page_class
@@ -32,6 +38,8 @@ module Gollum
end end
end end
self.default_committer_name = 'Anonymous'
self.default_committer_email = 'anon@anon.com'
# The String base path to prefix to internal links. For example, when set # The String base path to prefix to internal links. For example, when set
# to "/wiki", the page "Hobbit" will be linked as "/wiki/Hobbit". Defaults # to "/wiki", the page "Hobbit" will be linked as "/wiki/Hobbit". Defaults
@@ -55,6 +63,7 @@ module Gollum
@base_path = options[:base_path] || "/" @base_path = options[:base_path] || "/"
@page_class = options[:page_class] || self.class.page_class @page_class = options[:page_class] || self.class.page_class
@file_class = options[:file_class] || self.class.file_class @file_class = options[:file_class] || self.class.file_class
clear_cache
end end
# Public: check whether the wiki's git repo exists on the filesystem. # Public: check whether the wiki's git repo exists on the filesystem.
@@ -115,17 +124,23 @@ module Gollum
# #
# Returns the String SHA1 of the newly written version. # Returns the String SHA1 of the newly written version.
def write_page(name, format, data, commit = {}) def write_page(name, format, data, commit = {})
map = {} commit = normalize_commit(commit)
index = self.repo.index
if pcommit = @repo.commit('master') if pcommit = @repo.commit('master')
map = tree_map(pcommit.tree) index.read_tree(pcommit.tree.id)
end end
map = add_to_tree_map(map, '', name, format, data) add_to_index(index, '', name, format, data)
index = tree_map_to_index(map)
parents = pcommit ? [pcommit] : [] parents = pcommit ? [pcommit] : []
actor = Grit::Actor.new(commit[:name], commit[:email]) actor = Grit::Actor.new(commit[:name], commit[:email])
index.commit(commit[:message], parents, actor) sha1 = index.commit(commit[:message], parents, actor)
@ref_map.clear
update_working_dir(index, '', name, format)
sha1
end end
# Public: Update an existing page with new content. The location of the # Public: Update an existing page with new content. The location of the
@@ -144,24 +159,32 @@ module Gollum
# #
# Returns the String SHA1 of the newly written version. # Returns the String SHA1 of the newly written version.
def update_page(page, name, format, data, commit = {}) def update_page(page, name, format, data, commit = {})
commit = normalize_commit(commit)
pcommit = @repo.commit('master') pcommit = @repo.commit('master')
map = tree_map(pcommit.tree)
name ||= page.name name ||= page.name
format ||= page.format format ||= page.format
index = nil index = self.repo.index
dir = ::File.dirname(page.path)
dir = '' if dir == '.'
index.read_tree(pcommit.tree.id)
if page.name == name && page.format == format if page.name == name && page.format == format
index = tree_map_to_index(map)
index.add(page.path, normalize(data)) index.add(page.path, normalize(data))
else else
map = delete_from_tree_map(map, page.path) index.delete(page.path)
dir = ::File.dirname(page.path) add_to_index(index, dir, name, format, data, :allow_same_ext)
map = add_to_tree_map(map, dir, name, format, data)
index = tree_map_to_index(map)
end end
actor = Grit::Actor.new(commit[:name], commit[:email]) actor = Grit::Actor.new(commit[:name], commit[:email])
index.commit(commit[:message], [pcommit], actor) sha1 = index.commit(commit[:message], [pcommit], actor)
@ref_map.clear
update_working_dir(index, dir, page.name, page.format)
update_working_dir(index, dir, name, format)
sha1
end end
# Public: Delete a page. # Public: Delete a page.
@@ -169,19 +192,27 @@ module Gollum
# page - The Gollum::Page to delete. # page - The Gollum::Page to delete.
# commit - The commit Hash details: # commit - The commit Hash details:
# :message - The String commit message. # :message - The String commit message.
# :author - The String author full name. # :name - The String author full name.
# :email - The String email address. # :email - The String email address.
# #
# Returns the String SHA1 of the newly written version. # Returns the String SHA1 of the newly written version.
def delete_page(page, commit) def delete_page(page, commit)
pcommit = @repo.commit('master') pcommit = @repo.commit('master')
map = tree_map(pcommit.tree)
map = delete_from_tree_map(map, page.path) index = self.repo.index
index = tree_map_to_index(map) index.read_tree(pcommit.tree.id)
index.delete(page.path)
dir = ::File.dirname(page.path)
dir = '' if dir == '.'
actor = Grit::Actor.new(commit[:name], commit[:email]) actor = Grit::Actor.new(commit[:name], commit[:email])
index.commit(commit[:message], [pcommit], actor) sha1 = index.commit(commit[:message], [pcommit], actor)
@ref_map.clear
update_working_dir(index, dir, page.name, page.format)
sha1
end end
# Public: Lists all pages for this wiki. # Public: Lists all pages for this wiki.
@@ -190,11 +221,39 @@ module Gollum
# #
# Returns an Array of Gollum::Page instances. # Returns an Array of Gollum::Page instances.
def pages(treeish = nil) def pages(treeish = nil)
treeish ||= 'master' tree_list(treeish || 'master')
if commit = @repo.commit(treeish) end
tree_list(commit)
else # Public: Returns the number of pages accessible from a commit
[] #
# ref - A String ref that is either a commit SHA or references one.
#
# Returns a Fixnum
def size(ref = nil)
tree_map_for(ref || 'master').inject(0) do |num, entry|
num + (@page_class.valid_page_name?(entry.name) ? 1 : 0)
end
rescue Grit::GitRuby::Repository::NoSuchShaFound
0
end
# Public: Search all pages for this wiki.
#
# query - The string to search for
#
# Returns an Array with Objects of page name and count of matches
def search(query)
# See: http://github.com/Sirupsen/gollum/commit/f0a6f52bdaf6bee8253ca33bb3fceaeb27bfb87e
search_output = @repo.git.grep({:c => query}, 'master')
search_output.split("\n").collect do |line|
result = line.split(':')
file_name = Gollum::Page.canonicalize_filename(::File.basename(result[1]))
{
:count => result[2].to_i,
:name => file_name
}
end end
end end
@@ -225,6 +284,26 @@ module Gollum
# Returns the String path. # Returns the String path.
attr_reader :path attr_reader :path
# Gets a Hash cache of refs to commit SHAs.
#
# {"master" => "abc123", ...}
#
# Returns the Hash cache.
attr_reader :ref_map
# Gets a Hash cache of commit SHAs to a recursive tree of blobs.
#
# {"abc123" => [["lib/foo.rb", "blob-sha"], [file, sha], ...], ...}
#
# Returns the Hash cache.
attr_reader :tree_map
# Gets the page class used by all instances of this Wiki.
attr_reader :page_class
# Gets the file class used by all instances of this Wiki.
attr_reader :file_class
# Normalize the data. # Normalize the data.
# #
# data - The String data to be normalized. # data - The String data to be normalized.
@@ -234,102 +313,236 @@ module Gollum
data.gsub(/\r/, '') data.gsub(/\r/, '')
end end
# Assemble a Page's filename from its name and format.
#
# name - The String name of the page (may be in human format).
# format - The Symbol format of the page.
#
# Returns the String filename.
def page_file_name(name, format)
ext = @page_class.format_to_ext(format)
@page_class.cname(name) + '.' + ext
end
# Update the given file in the repository's working directory if there
# is a working directory present.
#
# index - The Grit::Index with which to sync.
# dir - The String directory in which the file lives.
# name - The String name of the page (may be in human format).
# format - The Symbol format of the page.
#
# Returns nothing.
def update_working_dir(index, dir, name, format)
unless @repo.bare
path =
if dir == ''
page_file_name(name, format)
else
::File.join(dir, page_file_name(name, format))
end
Dir.chdir(::File.join(@repo.path, '..')) do
if file_path_scheduled_for_deletion?(index.tree, path)
@repo.git.rm({'f' => true}, '--', path)
else
@repo.git.checkout({}, 'HEAD', '--', path)
end
end
end
end
# Fill an array with a list of pages. # Fill an array with a list of pages.
# #
# commit - The Grit::Commit # ref - A String ref that is either a commit SHA or references one.
# tree - The Grit::Tree to start with.
# sub_tree - Optional String specifying the parent path of the Page.
# #
# Returns a flat Array of Gollum::Page instances. # Returns a flat Array of Gollum::Page instances.
def tree_list(commit, tree = commit.tree, sub_tree = nil) def tree_list(ref)
list = [] tree_map_for(ref).inject([]) do |list, entry|
path = tree.name ? "#{sub_tree}/#{tree.name}" : '' next list unless @page_class.valid_page_name?(entry.name)
tree.contents.each do |item| sha = ref_map[ref]
case item list << entry.page(self, @repo.commit(sha))
when Grit::Blob
if @page_class.valid_page_name?(item.name)
page = @page_class.new(self).populate(item, path)
page.version = commit
list << page
end end
when Grit::Tree
list.push *tree_list(commit, item, path)
end
end
list
end end
# Fill an index with the existing state of the repository. # Determine if a given file is scheduled to be deleted in the next commit
# # for the given Index.
# tree - The Grit::Tree to start with.
#
# Returns a nested Hash of filename to content mappings.
def tree_map(tree)
map = {}
tree.contents.each do |item|
case item
when Grit::Blob
map[item.name] = item.data
when Grit::Tree
map[item.name] = tree_map(item)
end
end
map
end
# Use a treemap to fill in the index.
# #
# map - The Hash map: # map - The Hash map:
# key - The String directory or filename. # key - The String directory or filename.
# val - The Hash submap or the String contents of the file. # val - The Hash submap or the String contents of the file.
# index - The Grit::Index to use. Leave blank when calling from outside # path - The String path of the file including extension.
# this method (default: nil).
# #
# Returns the Grit::Index. # Returns the Boolean response.
def tree_map_to_index(map, prefix = '', index = nil) def file_path_scheduled_for_deletion?(map, path)
index ||= @repo.index
map.each do |k, v|
case k
when String
name = [prefix, k].join('/')[1..-1]
index.add(k, v)
when Hash
new_prefix = [prefix, k].join('/')[1..-1]
tree_map_to_index(v, new_prefix, index)
end
end
index
end
def add_to_tree_map(map, dir, name, format, data)
ext = @page_class.format_to_ext(format)
path = @page_class.cname(name) + '.' + ext
parts = dir.split('/')
container = nil
parts.each do |part|
container = map[part]
end
(container || map)[path] = normalize(data)
map
end
# Delete an entry from a tree map.
#
# map - The Hash tree map of the repository.
# path - The String path of the file to delete.
#
# Returns the modified Hash tree map.
def delete_from_tree_map(map, path)
parts = path.split('/') parts = path.split('/')
name = parts.pop if parts.size == 1
container = nil deletions = map.keys.select { |k| !map[k] }
parts.each do |part| deletions.any? { |d| d == parts.first }
container = map[part] else
part = parts.shift
if rest = map[part]
file_path_scheduled_for_deletion?(rest, parts.join('/'))
else
false
end end
(container || map).delete(name) end
map end
# Determine if a given page (regardless of format) is scheduled to be
# deleted in the next commit for the given Index.
#
# map - The Hash map:
# key - The String directory or filename.
# val - The Hash submap or the String contents of the file.
# path - The String path of the page file. This may include the format
# extension in which case it will be ignored.
#
# Returns the Boolean response.
def page_path_scheduled_for_deletion?(map, path)
parts = path.split('/')
if parts.size == 1
deletions = map.keys.select { |k| !map[k] }
downfile = parts.first.downcase.sub(/\.\w+$/, '')
deletions.any? { |d| d.downcase.sub(/\.\w+$/, '') == downfile }
else
part = parts.shift
if rest = map[part]
page_path_scheduled_for_deletion?(rest, parts.join('/'))
else
false
end
end
end
# Adds a page to the given Index.
#
# index - The Grit::Index to which the page will be added.
# dir - The String subdirectory of the Gollum::Page without any
# prefix or suffix slashes (e.g. "foo/bar").
# name - The String Gollum::Page name.
# format - The Symbol Gollum::Page format.
# data - The String wiki data to store in the tree map.
# allow_same_ext - A Boolean determining if the tree map allows the same
# filename with the same extension.
#
# Raises Gollum::DuplicatePageError if a matching filename already exists.
# This way, pages are not inadvertently overwritten.
#
# Returns nothing (modifies the Index in place).
def add_to_index(index, dir, name, format, data, allow_same_ext = false)
path = page_file_name(name, format)
dir = '/' if dir.strip.empty?
fullpath = ::File.join(dir, path)
fullpath = fullpath[1..-1] if fullpath =~ /^\//
if index.current_tree && tree = index.current_tree / dir
downpath = path.downcase.sub(/\.\w+$/, '')
tree.blobs.each do |blob|
next if page_path_scheduled_for_deletion?(index.tree, fullpath)
file = blob.name.downcase.sub(/\.\w+$/, '')
file_ext = ::File.extname(blob.name).sub(/^\./, '')
if downpath == file && !(allow_same_ext && file_ext == ext)
raise DuplicatePageError.new(dir, blob.name, path)
end
end
end
index.add(fullpath, normalize(data))
end
# Ensures a commit hash has all the required fields for a commit.
#
# commit - The commit Hash details:
# :message - The String commit message.
# :name - The String author full name.
# :email - The String email address.
#
# Returns the commit Hash
def normalize_commit(commit = {})
commit[:name] = default_committer_name if commit[:name].to_s.empty?
commit[:email] = default_committer_email if commit[:email].to_s.empty?
commit
end
# Gets the default name for commits.
def default_committer_name
@default_committer_name ||= \
@repo.config['user.name'] || self.class.default_committer_name
end
# Gets the default email for commits.
def default_committer_email
@default_committer_email ||= \
@repo.config['user.email'] || self.class.default_committer_email
end
# Finds a full listing of files and their blob SHA for a given ref. Each
# listing is cached based on its actual commit SHA.
#
# ref - A String ref that is either a commit SHA or references one.
#
# Returns an Array of BlobEntry instances.
def tree_map_for(ref)
sha = @ref_map[ref] || ref
@tree_map[sha] || begin
real_sha = @repo.git.rev_list({:max_count=>1}, ref)
@ref_map[ref] = real_sha if real_sha != ref
@tree_map[real_sha] ||= parse_tree_for(real_sha)
end
rescue Grit::GitRuby::Repository::NoSuchShaFound
[]
end
# Finds the full listing of files and their blob SHA for a given commit
# SHA. No caching or ref lookups are performed.
#
# sha - String commit SHA.
#
# Returns an Array of BlobEntry instances.
def parse_tree_for(sha)
tree = @repo.git.native(:ls_tree, {:r => true, :z => true}, sha)
items = []
tree.split("\0").each do |line|
items << parse_tree_line(line)
end
items
end
# Parses a line of output from the `ls-tree` command.
#
# line - A String line of output:
# "100644 blob 839c2291b30495b9a882c17d08254d3c90d8fb53 Home.md"
#
# Returns an Array of BlobEntry instances.
def parse_tree_line(line)
data, name = line.split("\t")
mode, type, sha = data.split(' ')
name = decode_git_path(name)
BlobEntry.new sha, name
end
# Decode octal sequences (\NNN) in tree path names.
#
# path - String path name.
#
# Returns a decoded String.
def decode_git_path(path)
if path[0] == ?" && path[-1] == ?"
path = path[1...-1]
path.gsub!(/\\\d{3}/) { |m| m[1..-1].to_i(8).chr }
end
path.gsub!(/\\[rn"\\]/) { |m| eval(%("#{m.to_s}")) }
path
end
# Resets the ref and tree caches for this wiki.
def clear_cache
@ref_map = {}
@tree_map = {}
end end
end end
end end
+6
View File
@@ -18,6 +18,12 @@ def testpath(path)
File.join(TEST_DIR, path) File.join(TEST_DIR, path)
end end
def commit_details
{ :message => "Did something at #{Time.now}",
:name => "Tom Preston-Werner",
:email => "tom@github.com" }
end
# test/spec/mini 3 # test/spec/mini 3
# http://gist.github.com/25455 # http://gist.github.com/25455
# chris@ozmm.org # chris@ozmm.org
+8 -1
View File
@@ -11,8 +11,15 @@ context "File" do
end end
test "existing file" do test "existing file" do
commit = @wiki.repo.commits.first
file = @wiki.file("Mordor/todo.txt") file = @wiki.file("Mordor/todo.txt")
assert_equal "[ ] Write section on Ents\n", file.raw_data assert_equal "[ ] Write section on Ents\n", file.raw_data
assert_equal @wiki.repo.commits.first.id, file.version.id assert_equal 'todo.txt', file.name
assert_equal commit.id, file.version.id
assert_equal commit.author.name, file.version.author.name
end
test "accessing tree" do
assert_nil @wiki.file("Mordor")
end end
end end
+117 -21
View File
@@ -6,10 +6,6 @@ context "Markup" do
FileUtils.rm_rf(@path) FileUtils.rm_rf(@path)
Grit::Repo.init_bare(@path) Grit::Repo.init_bare(@path)
@wiki = Gollum::Wiki.new(@path) @wiki = Gollum::Wiki.new(@path)
@commit = { :message => "Add stuff",
:name => "Tom Preston-Werner",
:email => "tom@github.com" }
end end
teardown do teardown do
@@ -17,12 +13,18 @@ context "Markup" do
end end
test "formats page from Wiki#pages" do test "formats page from Wiki#pages" do
@wiki.write_page("Bilbo Baggins", :markdown, "a [[Foo]][[Bar]] b", @commit) @wiki.write_page("Bilbo Baggins", :markdown, "a [[Foo]][[Bar]] b", commit_details)
assert @wiki.pages[0].formatted_data assert @wiki.pages[0].formatted_data
end end
#########################################################################
#
# Links
#
#########################################################################
test "double page links no space" do test "double page links no space" do
@wiki.write_page("Bilbo Baggins", :markdown, "a [[Foo]][[Bar]] b", @commit) @wiki.write_page("Bilbo Baggins", :markdown, "a [[Foo]][[Bar]] b", commit_details)
# "<p>a <a class=\"internal absent\" href=\"/Foo\">Foo</a><a class=\"internal absent\" href=\"/Bar\">Bar</a> b</p>" # "<p>a <a class=\"internal absent\" href=\"/Foo\">Foo</a><a class=\"internal absent\" href=\"/Bar\">Bar</a> b</p>"
page = @wiki.page("Bilbo Baggins") page = @wiki.page("Bilbo Baggins")
@@ -41,7 +43,7 @@ context "Markup" do
end end
test "double page links with space" do test "double page links with space" do
@wiki.write_page("Bilbo Baggins", :markdown, "a [[Foo]] [[Bar]] b", @commit) @wiki.write_page("Bilbo Baggins", :markdown, "a [[Foo]] [[Bar]] b", commit_details)
# "<p>a <a class=\"internal absent\" href=\"/Foo\">Foo</a> <a class=\"internal absent\" href=\"/Bar\">Bar</a> b</p>" # "<p>a <a class=\"internal absent\" href=\"/Foo\">Foo</a> <a class=\"internal absent\" href=\"/Bar\">Bar</a> b</p>"
page = @wiki.page("Bilbo Baggins") page = @wiki.page("Bilbo Baggins")
@@ -60,7 +62,7 @@ context "Markup" do
end end
test "page link" do test "page link" do
@wiki.write_page("Bilbo Baggins", :markdown, "a [[Bilbo Baggins]] b", @commit) @wiki.write_page("Bilbo Baggins", :markdown, "a [[Bilbo Baggins]] b", commit_details)
page = @wiki.page("Bilbo Baggins") page = @wiki.page("Bilbo Baggins")
output = page.formatted_data output = page.formatted_data
@@ -69,8 +71,18 @@ context "Markup" do
assert_match /\>Bilbo Baggins\</, output assert_match /\>Bilbo Baggins\</, output
end end
test "adds nofollow to links on historical pages" do
sha1 = @wiki.write_page("Sauron", :markdown, "a [[b]] c", commit_details)
page = @wiki.page("Sauron")
sha2 = @wiki.update_page(page, page.name, :markdown, "c [[b]] a", commit_details)
regx = /rel="nofollow"/
assert_no_match regx, page.formatted_data
assert_match regx, @wiki.page(page.name, sha1).formatted_data
assert_match regx, @wiki.page(page.name, sha2).formatted_data
end
test "absent page link" do test "absent page link" do
@wiki.write_page("Tolkien", :markdown, "a [[J. R. R. Tolkien]]'s b", @commit) @wiki.write_page("Tolkien", :markdown, "a [[J. R. R. Tolkien]]'s b", commit_details)
page = @wiki.page("Tolkien") page = @wiki.page("Tolkien")
output = page.formatted_data output = page.formatted_data
@@ -80,41 +92,76 @@ context "Markup" do
end end
test "page link with custom base path" do test "page link with custom base path" do
["/wiki", "/wiki/"].each do |path| ["/wiki", "/wiki/"].each_with_index do |path, i|
name = "Bilbo Baggins #{i}"
@wiki = Gollum::Wiki.new(@path, :base_path => path) @wiki = Gollum::Wiki.new(@path, :base_path => path)
@wiki.write_page("Bilbo Baggins", :markdown, "a [[Bilbo Baggins]] b", @commit) @wiki.write_page(name, :markdown, "a [[#{name}]] b", commit_details)
page = @wiki.page("Bilbo Baggins") page = @wiki.page(name)
output = page.formatted_data output = page.formatted_data
assert_match /class="internal present"/, output assert_match /class="internal present"/, output
assert_match /href="\/wiki\/Bilbo-Baggins"/, output assert_match /href="\/wiki\/Bilbo-Baggins-\d"/, output
assert_match /\>Bilbo Baggins\</, output assert_match /\>Bilbo Baggins \d\</, output
end end
end end
test "page link with included #" do
@wiki.write_page("Precious #1", :markdown, "a [[Precious #1]] b", commit_details)
page = @wiki.page('Precious #1')
output = page.formatted_data
assert_match /class="internal present"/, output
assert_match /href="\/Precious-%231"/, output
end
test "page link with extra #" do
@wiki.write_page("Potato", :markdown, "a [[Potato#1]] b", commit_details)
page = @wiki.page('Potato')
output = page.formatted_data
assert_match /class="internal present"/, output
assert_match /href="\/Potato#1"/, output
end
test "external page link" do test "external page link" do
@wiki.write_page("Bilbo Baggins", :markdown, "a [[http://example.com]] b", @commit) @wiki.write_page("Bilbo Baggins", :markdown, "a [[http://example.com]] b", commit_details)
page = @wiki.page("Bilbo Baggins") page = @wiki.page("Bilbo Baggins")
assert_equal "<p>a <a href=\"http://example.com\">http://example.com</a> b</p>", page.formatted_data assert_equal "<p>a <a href=\"http://example.com\">http://example.com</a> b</p>", page.formatted_data
end end
#########################################################################
#
# Images
#
#########################################################################
test "image with http url" do test "image with http url" do
['http', 'https'].each do |scheme| ['http', 'https'].each do |scheme|
@wiki.write_page("Bilbo Baggins", :markdown, "a [[#{scheme}://example.com/bilbo.jpg]] b", @commit) name = "Bilbo Baggins #{scheme}"
@wiki.write_page(name, :markdown, "a [[#{scheme}://example.com/bilbo.jpg]] b", commit_details)
page = @wiki.page("Bilbo Baggins") page = @wiki.page(name)
output = page.formatted_data output = page.formatted_data
assert_equal %{<p>a <img src="#{scheme}://example.com/bilbo.jpg" /> b</p>}, output assert_equal %{<p>a <img src="#{scheme}://example.com/bilbo.jpg" /> b</p>}, output
end end
end end
test "image with extension in caps with http url" do
['http', 'https'].each do |scheme|
name = "Bilbo Baggins #{scheme}"
@wiki.write_page(name, :markdown, "a [[#{scheme}://example.com/bilbo.JPG]] b", commit_details)
page = @wiki.page(name)
output = page.formatted_data
assert_equal %{<p>a <img src="#{scheme}://example.com/bilbo.JPG" /> b</p>}, output
end
end
test "image with absolute path" do test "image with absolute path" do
@wiki = Gollum::Wiki.new(@path, :base_path => '/wiki') @wiki = Gollum::Wiki.new(@path, :base_path => '/wiki')
index = @wiki.repo.index index = @wiki.repo.index
index.add("alpha.jpg", "hi") index.add("alpha.jpg", "hi")
index.commit("Add alpha.jpg") index.commit("Add alpha.jpg")
@wiki.write_page("Bilbo Baggins", :markdown, "a [[/alpha.jpg]] [[a | /alpha.jpg]] b", @commit) @wiki.write_page("Bilbo Baggins", :markdown, "a [[/alpha.jpg]] [[a | /alpha.jpg]] b", commit_details)
page = @wiki.page("Bilbo Baggins") page = @wiki.page("Bilbo Baggins")
assert_equal %{<p>a <img src="/wiki/alpha.jpg" /><a href="/wiki/alpha.jpg">a</a> b</p>}, page.formatted_data assert_equal %{<p>a <img src="/wiki/alpha.jpg" /><a href="/wiki/alpha.jpg">a</a> b</p>}, page.formatted_data
@@ -203,17 +250,29 @@ context "Markup" do
relative_image(content, output) relative_image(content, output)
end end
test "absolute image with frame" do
content = "a\n\n[[http://example.com/bilbo.jpg|frame]]\n\nb"
output = "<p>a</p>\n\n<p><span class=\"frame\"><span><img src=\"http://example.com/bilbo.jpg\" /></span></span></p>\n\n<p>b</p>"
relative_image(content, output)
end
test "image with frame and alt" do test "image with frame and alt" do
content = "a\n\n[[alpha.jpg|frame|alt=Alpha]]\n\nb" content = "a\n\n[[alpha.jpg|frame|alt=Alpha]]\n\nb"
output = "<p>a</p>\n\n<p><span class=\"frame\"><span><img src=\"/greek/alpha.jpg\" alt=\"Alpha\" /><span>Alpha</span></span></span></p>\n\n<p>b</p>" output = "<p>a</p>\n\n<p><span class=\"frame\"><span><img src=\"/greek/alpha.jpg\" alt=\"Alpha\" /><span>Alpha</span></span></span></p>\n\n<p>b</p>"
relative_image(content, output) relative_image(content, output)
end end
#########################################################################
#
# File links
#
#########################################################################
test "file link with absolute path" do test "file link with absolute path" do
index = @wiki.repo.index index = @wiki.repo.index
index.add("alpha.jpg", "hi") index.add("alpha.jpg", "hi")
index.commit("Add alpha.jpg") index.commit("Add alpha.jpg")
@wiki.write_page("Bilbo Baggins", :markdown, "a [[Alpha|/alpha.jpg]] b", @commit) @wiki.write_page("Bilbo Baggins", :markdown, "a [[Alpha|/alpha.jpg]] b", commit_details)
page = @wiki.page("Bilbo Baggins") page = @wiki.page("Bilbo Baggins")
output = Gollum::Markup.new(page).render output = Gollum::Markup.new(page).render
@@ -240,6 +299,12 @@ context "Markup" do
assert_equal %{<p>a <a href="http://example.com/alpha.jpg">Alpha</a> b</p>}, page.formatted_data assert_equal %{<p>a <a href="http://example.com/alpha.jpg">Alpha</a> b</p>}, page.formatted_data
end end
#########################################################################
#
# Code
#
#########################################################################
test "code blocks" do test "code blocks" do
content = "a\n\n```ruby\nx = 1\n```\n\nb" content = "a\n\n```ruby\nx = 1\n```\n\nb"
output = "<p>a</p>\n\n<div class=\"highlight\"><pre>" + output = "<p>a</p>\n\n<div class=\"highlight\"><pre>" +
@@ -288,6 +353,24 @@ context "Markup" do
compare(content, output) compare(content, output)
end end
test "code block with invalid lang" do
content = "a\n\n``` ls -al;\n\tbooya\n\tboom\n```\n\nb"
output = "<p>a</p>\n\n\n\n<p>b</p>"
compare(content, output)
end
test "code block with no lang" do
content = "a\n\n```\n\tls -al;\n\t<booya>\n```\n\nb"
output = "<p>a</p>\n\n<pre><code>ls -al;\n&lt;booya&gt;</code></pre>\n\n<p>b</p>"
compare(content, output)
end
#########################################################################
#
# Various
#
#########################################################################
test "escaped wiki link" do test "escaped wiki link" do
content = "a '[[Foo]], b" content = "a '[[Foo]], b"
output = "<p>a [[Foo]], b</p>" output = "<p>a [[Foo]], b</p>"
@@ -309,18 +392,30 @@ context "Markup" do
compare(content, output, 'org') compare(content, output, 'org')
end end
test "tex block syntax" do #########################################################################
#
# TeX
#
#########################################################################
test "TeX block syntax" do
content = 'a \[ a^2 \] b' content = 'a \[ a^2 \] b'
output = "<p>a <script type=\"math/tex; mode=display\">a^2</script> b</p>" output = "<p>a <script type=\"math/tex; mode=display\">a^2</script> b</p>"
compare(content, output, 'md') compare(content, output, 'md')
end end
test "tex inline syntax" do test "TeX inline syntax" do
content = 'a \( a^2 \) b' content = 'a \( a^2 \) b'
output = "<p>a <script type=\"math/tex\">a^2</script> b</p>" output = "<p>a <script type=\"math/tex\">a^2</script> b</p>"
compare(content, output, 'md') compare(content, output, 'md')
end end
#########################################################################
#
# Helpers
#
#########################################################################
def compare(content, output, ext = "md", regexes = []) def compare(content, output, ext = "md", regexes = [])
index = @wiki.repo.index index = @wiki.repo.index
index.add("Bilbo-Baggins.#{ext}", content) index.add("Bilbo-Baggins.#{ext}", content)
@@ -341,6 +436,7 @@ context "Markup" do
index.add("greek/Bilbo-Baggins.md", content) index.add("greek/Bilbo-Baggins.md", content)
index.commit("Add alpha.jpg") index.commit("Add alpha.jpg")
@wiki.clear_cache
page = @wiki.page("Bilbo Baggins") page = @wiki.page("Bilbo Baggins")
rendered = Gollum::Markup.new(page).render rendered = Gollum::Markup.new(page).render
assert_equal output, rendered assert_equal output, rendered
+6
View File
@@ -1,3 +1,4 @@
# ~*~ encoding: utf-8 ~*~
require File.join(File.dirname(__FILE__), *%w[helper]) require File.join(File.dirname(__FILE__), *%w[helper])
context "Page" do context "Page" do
@@ -109,4 +110,9 @@ context "Page" do
footer = @wiki.page("_Footer") footer = @wiki.page("_Footer")
assert_nil footer.footer assert_nil footer.footer
end end
test "cannot convert non string to human readable page title" do
assert_equal '', Gollum::Page.cname(nil)
assert_equal '', Gollum::Page.cname(3)
end
end end
+129 -46
View File
@@ -38,6 +38,51 @@ context "Wiki" do
%w(Bilbo-Baggins.md Eye-Of-Sauron.md Home.textile My-Precious.md), %w(Bilbo-Baggins.md Eye-Of-Sauron.md Home.textile My-Precious.md),
pages.map { |p| p.filename }.sort pages.map { |p| p.filename }.sort
end end
test "counts pages" do
assert_equal 4, @wiki.size
end
test "normalizes commit hash" do
commit = {:message => 'abc'}
name = @wiki.repo.config['user.name']
email = @wiki.repo.config['user.email']
assert_equal({:message => 'abc', :name => name, :email => email},
@wiki.normalize_commit(commit.dup))
commit[:name] = 'bob'
commit[:email] = ''
assert_equal({:message => 'abc', :name => 'bob', :email => email},
@wiki.normalize_commit(commit.dup))
commit[:email] = 'foo@bar.com'
assert_equal({:message => 'abc', :name => 'bob', :email => 'foo@bar.com'},
@wiki.normalize_commit(commit.dup))
end
test "#tree_map_for caches ref and tree" do
assert @wiki.ref_map.empty?
assert @wiki.tree_map.empty?
@wiki.tree_map_for 'master'
assert_equal({"master"=>"60f12f4254f58801b9ee7db7bca5fa8aeefaa56b"}, @wiki.ref_map)
map = @wiki.tree_map['60f12f4254f58801b9ee7db7bca5fa8aeefaa56b']
assert_equal 'Bilbo-Baggins.md', map[0].path
assert_equal '', map[0].dir
assert_equal map[0].path, map[0].name
assert_equal 'Mordor/Eye-Of-Sauron.md', map[3].path
assert_equal '/Mordor', map[3].dir
assert_equal 'Eye-Of-Sauron.md', map[3].name
end
test "#tree_map_for only caches tree for commit" do
assert @wiki.tree_map.empty?
@wiki.tree_map_for '60f12f4254f58801b9ee7db7bca5fa8aeefaa56b'
assert @wiki.ref_map.empty?
entry = @wiki.tree_map['60f12f4254f58801b9ee7db7bca5fa8aeefaa56b'][0]
assert_equal 'Bilbo-Baggins.md', entry.path
end
end end
context "Wiki page previewing" do context "Wiki page previewing" do
@@ -64,48 +109,48 @@ context "Wiki page writing" do
end end
test "write_page" do test "write_page" do
commit = { :message => "Gollum page", cd = commit_details
:name => "Tom Preston-Werner", @wiki.write_page("Gollum", :markdown, "# Gollum", cd)
:email => "tom@github.com" }
@wiki.write_page("Gollum", :markdown, "# Gollum", commit)
assert_equal 1, @wiki.repo.commits.size assert_equal 1, @wiki.repo.commits.size
assert_equal "Gollum page", @wiki.repo.commits.first.message assert_equal cd[:message], @wiki.repo.commits.first.message
assert_equal "Tom Preston-Werner", @wiki.repo.commits.first.author.name assert_equal cd[:name], @wiki.repo.commits.first.author.name
assert_equal "tom@github.com", @wiki.repo.commits.first.author.email assert_equal cd[:email], @wiki.repo.commits.first.author.email
assert @wiki.page("Gollum") assert @wiki.page("Gollum")
@wiki.write_page("Bilbo", :markdown, "# Bilbo", commit) @wiki.write_page("Bilbo", :markdown, "# Bilbo", commit_details)
assert_equal 2, @wiki.repo.commits.size assert_equal 2, @wiki.repo.commits.size
assert @wiki.page("Bilbo") assert @wiki.page("Bilbo")
assert @wiki.page("Gollum") assert @wiki.page("Gollum")
end end
test "is not allowed to overwrite file" do
@wiki.write_page("Abc-Def", :markdown, "# Gollum", commit_details)
assert_raises Gollum::DuplicatePageError do
@wiki.write_page("ABC DEF", :textile, "# Gollum", commit_details)
end
end
test "update_page" do test "update_page" do
commit = { :message => "Gollum page", @wiki.write_page("Gollum", :markdown, "# Gollum", commit_details)
:name => "Tom Preston-Werner",
:email => "tom@github.com" }
@wiki.write_page("Gollum", :markdown, "# Gollum", commit)
page = @wiki.page("Gollum") page = @wiki.page("Gollum")
@wiki.update_page(page, page.name, :markdown, "# Gollum2", commit) cd = commit_details
@wiki.update_page(page, page.name, :markdown, "# Gollum2", cd)
assert_equal 2, @wiki.repo.commits.size assert_equal 2, @wiki.repo.commits.size
assert_equal "# Gollum2", @wiki.page("Gollum").raw_data assert_equal "# Gollum2", @wiki.page("Gollum").raw_data
assert_equal "Gollum page", @wiki.repo.commits.first.message assert_equal cd[:message], @wiki.repo.commits.first.message
assert_equal "Tom Preston-Werner", @wiki.repo.commits.first.author.name assert_equal cd[:name], @wiki.repo.commits.first.author.name
assert_equal "tom@github.com", @wiki.repo.commits.first.author.email assert_equal cd[:email], @wiki.repo.commits.first.author.email
end end
test "update page with format change" do test "update page with format change" do
commit = { :message => "Gollum page", @wiki.write_page("Gollum", :markdown, "# Gollum", commit_details)
:name => "Tom Preston-Werner",
:email => "tom@github.com" }
@wiki.write_page("Gollum", :markdown, "# Gollum", commit)
assert_equal :markdown, @wiki.page("Gollum").format assert_equal :markdown, @wiki.page("Gollum").format
page = @wiki.page("Gollum") page = @wiki.page("Gollum")
@wiki.update_page(page, page.name, :textile, "h1. Gollum", commit) @wiki.update_page(page, page.name, :textile, "h1. Gollum", commit_details)
assert_equal 2, @wiki.repo.commits.size assert_equal 2, @wiki.repo.commits.size
assert_equal :textile, @wiki.page("Gollum").format assert_equal :textile, @wiki.page("Gollum").format
@@ -113,30 +158,24 @@ context "Wiki page writing" do
end end
test "update page with name change" do test "update page with name change" do
commit = { :message => "Gollum page", @wiki.write_page("Gollum", :markdown, "# Gollum", commit_details)
:name => "Tom Preston-Werner",
:email => "tom@github.com" }
@wiki.write_page("Gollum", :markdown, "# Gollum", commit)
assert_equal :markdown, @wiki.page("Gollum").format assert_equal :markdown, @wiki.page("Gollum").format
page = @wiki.page("Gollum") page = @wiki.page("Gollum")
@wiki.update_page(page, 'Smeagol', :markdown, "h1. Gollum", commit) @wiki.update_page(page, 'Smeagol', :markdown, "h1. Gollum", commit_details)
assert_equal 2, @wiki.repo.commits.size assert_equal 2, @wiki.repo.commits.size
assert_equal "h1. Gollum", @wiki.page("Smeagol").raw_data assert_equal "h1. Gollum", @wiki.page("Smeagol").raw_data
end end
test "update page with name and format change" do test "update page with name and format change" do
commit = { :message => "Gollum page", @wiki.write_page("Gollum", :markdown, "# Gollum", commit_details)
:name => "Tom Preston-Werner",
:email => "tom@github.com" }
@wiki.write_page("Gollum", :markdown, "# Gollum", commit)
assert_equal :markdown, @wiki.page("Gollum").format assert_equal :markdown, @wiki.page("Gollum").format
page = @wiki.page("Gollum") page = @wiki.page("Gollum")
@wiki.update_page(page, 'Smeagol', :textile, "h1. Gollum", commit) @wiki.update_page(page, 'Smeagol', :textile, "h1. Gollum", commit_details)
assert_equal 2, @wiki.repo.commits.size assert_equal 2, @wiki.repo.commits.size
assert_equal :textile, @wiki.page("Smeagol").format assert_equal :textile, @wiki.page("Smeagol").format
@@ -144,17 +183,13 @@ context "Wiki page writing" do
end end
test "update nested page with format change" do test "update nested page with format change" do
commit = { :message => "Gollum page",
:name => "Tom Preston-Werner",
:email => "tom@github.com" }
index = @wiki.repo.index index = @wiki.repo.index
index.add("lotr/Gollum.md", "# Gollum") index.add("lotr/Gollum.md", "# Gollum")
index.commit("Add nested page") index.commit("Add nested page")
page = @wiki.page("Gollum") page = @wiki.page("Gollum")
assert_equal :markdown, @wiki.page("Gollum").format assert_equal :markdown, @wiki.page("Gollum").format
@wiki.update_page(page, page.name, :textile, "h1. Gollum", commit) @wiki.update_page(page, page.name, :textile, "h1. Gollum", commit_details)
page = @wiki.page("Gollum") page = @wiki.page("Gollum")
assert_equal "lotr/Gollum.textile", page.path assert_equal "lotr/Gollum.textile", page.path
@@ -163,23 +198,16 @@ context "Wiki page writing" do
end end
test "delete root page" do test "delete root page" do
commit = { :message => "Gollum page", @wiki.write_page("Gollum", :markdown, "# Gollum", commit_details)
:name => "Tom Preston-Werner",
:email => "tom@github.com" }
@wiki.write_page("Gollum", :markdown, "# Gollum", commit)
page = @wiki.page("Gollum") page = @wiki.page("Gollum")
@wiki.delete_page(page, commit) @wiki.delete_page(page, commit_details)
assert_equal 2, @wiki.repo.commits.size assert_equal 2, @wiki.repo.commits.size
assert_nil @wiki.page("Gollum") assert_nil @wiki.page("Gollum")
end end
test "delete nested page" do test "delete nested page" do
commit = { :message => "Gollum page",
:name => "Tom Preston-Werner",
:email => "tom@github.com" }
index = @wiki.repo.index index = @wiki.repo.index
index.add("greek/Bilbo-Baggins.md", "hi") index.add("greek/Bilbo-Baggins.md", "hi")
index.add("Gollum.md", "hi") index.add("Gollum.md", "hi")
@@ -187,7 +215,7 @@ context "Wiki page writing" do
page = @wiki.page("Bilbo-Baggins") page = @wiki.page("Bilbo-Baggins")
assert page assert page
@wiki.delete_page(page, commit) @wiki.delete_page(page, commit_details)
assert_equal 2, @wiki.repo.commits.size assert_equal 2, @wiki.repo.commits.size
assert_nil @wiki.page("Bilbo-Baggins") assert_nil @wiki.page("Bilbo-Baggins")
@@ -199,3 +227,58 @@ context "Wiki page writing" do
FileUtils.rm_r(File.join(File.dirname(__FILE__), *%w[examples test.git])) FileUtils.rm_r(File.join(File.dirname(__FILE__), *%w[examples test.git]))
end end
end end
context "Wiki sync with working directory" do
setup do
@path = testpath('examples/wdtest')
Grit::Repo.init(@path)
@wiki = Gollum::Wiki.new(@path)
end
test "write a page" do
@wiki.write_page("New Page", :markdown, "Hi", commit_details)
assert_equal "Hi", File.read(File.join(@path, "New-Page.md"))
end
test "update a page with same name and format" do
@wiki.write_page("New Page", :markdown, "Hi", commit_details)
page = @wiki.page("New Page")
@wiki.update_page(page, page.name, page.format, "Bye", commit_details)
assert_equal "Bye", File.read(File.join(@path, "New-Page.md"))
end
test "update a page with different name and same format" do
@wiki.write_page("New Page", :markdown, "Hi", commit_details)
page = @wiki.page("New Page")
@wiki.update_page(page, "New Page 2", page.format, "Bye", commit_details)
assert_equal "Bye", File.read(File.join(@path, "New-Page-2.md"))
assert !File.exist?(File.join(@path, "New-Page.md"))
end
test "update a page with same name and different format" do
@wiki.write_page("New Page", :markdown, "Hi", commit_details)
page = @wiki.page("New Page")
@wiki.update_page(page, page.name, :textile, "Bye", commit_details)
assert_equal "Bye", File.read(File.join(@path, "New-Page.textile"))
assert !File.exist?(File.join(@path, "New-Page.md"))
end
test "update a page with different name and different format" do
@wiki.write_page("New Page", :markdown, "Hi", commit_details)
page = @wiki.page("New Page")
@wiki.update_page(page, "New Page 2", :textile, "Bye", commit_details)
assert_equal "Bye", File.read(File.join(@path, "New-Page-2.textile"))
assert !File.exist?(File.join(@path, "New-Page.md"))
end
test "delete a page" do
@wiki.write_page("New Page", :markdown, "Hi", commit_details)
page = @wiki.page("New Page")
@wiki.delete_page(page, commit_details)
assert !File.exist?(File.join(@path, "New-Page.md"))
end
teardown do
FileUtils.rm_r(@path)
end
end