Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 714985e377 | |||
| 76c37dce96 | |||
| 5a9af40058 | |||
| e871ff35b7 | |||
| ecc6c32933 | |||
| 831cf61a08 | |||
| cc1231dece | |||
| bb3d1a165b | |||
| 3da0426c54 | |||
| e01aa25be3 | |||
| ee2f9d8dcb | |||
| 4d8677965c | |||
| 72729d5510 | |||
| 0b57e70c87 | |||
| 033d6489f8 | |||
| 8608007337 | |||
| 665e493570 | |||
| eb1e2f60f3 | |||
| dc637f0a9b | |||
| f81634728d | |||
| 440cd5ebc0 | |||
| 1cd7d0f205 | |||
| a69d62911c | |||
| 395e9bd006 | |||
| 90e20810d5 | |||
| 9c714e7687 | |||
| 6c3523d61c | |||
| 183840b793 | |||
| 4e2856aa64 | |||
| 4627a39165 | |||
| c87cbe83d2 | |||
| f05282badf | |||
| 957879346e | |||
| 87e64f67f3 | |||
| 43840d246d | |||
| 4aeb9af8a7 | |||
| b37acb8bc6 | |||
| 433865e927 | |||
| 5428161e0f | |||
| db0b536b5b | |||
| a4e50908fb | |||
| 96b89fe83d | |||
| adb131f131 | |||
| 757ab87e8a | |||
| f1d1db1159 | |||
| 3942bf60a6 | |||
| e2c0dcc0da | |||
| f63180d8d8 | |||
| 999bbf3d50 | |||
| eab612bdd0 | |||
| 1147186b4c | |||
| a88314e061 | |||
| 9221f5528d | |||
| 520f60cd65 | |||
| 84c85774e8 | |||
| 1f118deed9 | |||
| 55ce07ae73 | |||
| 85677d5e91 | |||
| 04f9be15b8 | |||
| 09cd72a829 | |||
| f5f5ad70e3 | |||
| a0774b320a |
+2
-4
@@ -1,8 +1,6 @@
|
|||||||
rvm:
|
rvm:
|
||||||
- 1.8.7
|
|
||||||
- 1.9.3
|
- 1.9.3
|
||||||
notifications:
|
- 2.0.0
|
||||||
email:
|
|
||||||
- code@bootstraponline.com
|
|
||||||
before_install:
|
before_install:
|
||||||
- sudo apt-get update
|
- sudo apt-get update
|
||||||
|
- sudo apt-get install libicu-dev
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
gollum -- A wiki built on top of Git
|
gollum -- A wiki built on top of Git
|
||||||
====================================
|
====================================
|
||||||
|
|
||||||
|
[](http://rubygems.org/gems/gollum)
|
||||||
[](http://travis-ci.org/gollum/gollum)
|
[](http://travis-ci.org/gollum/gollum)
|
||||||
[](https://gemnasium.com/gollum/gollum)
|
[](https://gemnasium.com/gollum/gollum)
|
||||||
|
|
||||||
@@ -63,6 +64,15 @@ to install the dependencies for the formats that you plan to use.
|
|||||||
|
|
||||||
[bundler]: http://gembundler.com/
|
[bundler]: http://gembundler.com/
|
||||||
|
|
||||||
|
|
||||||
|
## SYNTAX
|
||||||
|
|
||||||
|
Gollum supports a variety of formats and extensions (Markdown, MediaWiki, Textile, …).
|
||||||
|
On top of these formats Gollum lets you insert headers, footers, links, image, math and more.
|
||||||
|
|
||||||
|
Check out the [Gollum Wiki](https://github.com/gollum/gollum/wiki) for all of Gollum's formats and syntactic options.
|
||||||
|
|
||||||
|
|
||||||
## RUNNING
|
## RUNNING
|
||||||
|
|
||||||
To view and edit your Gollum repository locally via the built in web
|
To view and edit your Gollum repository locally via the built in web
|
||||||
@@ -83,500 +93,6 @@ $ gollum --help
|
|||||||
|
|
||||||
Note that the gollum server will not run on Windows because of [an issue](https://github.com/rtomayko/posix-spawn/issues/9) with posix-spawn (which is used by Grit).
|
Note that the gollum server will not run on Windows because of [an issue](https://github.com/rtomayko/posix-spawn/issues/9) with posix-spawn (which is used by Grit).
|
||||||
|
|
||||||
## REPO STRUCTURE
|
|
||||||
|
|
||||||
A Gollum repository's contents are designed to be human editable. Page content
|
|
||||||
is written in `page files` and may be organized into directories any way you
|
|
||||||
choose. Special footers can be created in `footer files`. Other content
|
|
||||||
(images, PDFs, etc) may also be present and organized in the same way.
|
|
||||||
|
|
||||||
## PAGE FILES
|
|
||||||
|
|
||||||
Page files may be written in any format supported by
|
|
||||||
[GitHub-Markup](http://github.com/github/markup) (except roff). By default,
|
|
||||||
Gollum recognizes the following extensions:
|
|
||||||
|
|
||||||
* ASCIIDoc: .asciidoc
|
|
||||||
* Creole: .creole
|
|
||||||
* Markdown: .markdown, .mdown, .mkdn, .mkd, .md
|
|
||||||
* Org Mode: .org
|
|
||||||
* Pod: .pod
|
|
||||||
* RDoc: .rdoc
|
|
||||||
* ReStructuredText: .rest.txt, .rst.txt, .rest, .rst
|
|
||||||
* Textile: .textile
|
|
||||||
* MediaWiki: .mediawiki, .wiki
|
|
||||||
|
|
||||||
You may also register your own extensions and parsers:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
Gollum::Markup.register(:angry, "Angry") do |content|
|
|
||||||
content.upcase
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Gollum detects the page file format via the extension, so files must have one
|
|
||||||
of the default or registered extensions in order to be converted.
|
|
||||||
|
|
||||||
Page file names may contain any printable UTF-8 character except space
|
|
||||||
(U+0020) and forward slash (U+002F). If you commit a page file with any of
|
|
||||||
these characters in the name it will not be accessible via the web interface.
|
|
||||||
|
|
||||||
Even though page files may be placed in any directory, there is still only a
|
|
||||||
single namespace for page names, so all page files should have globally unique
|
|
||||||
names regardless of where they are located in the repository.
|
|
||||||
|
|
||||||
The special page file `Home.ext` (where the extension is one of the supported
|
|
||||||
formats) will be used as the entrance page to your wiki. If it is missing, an
|
|
||||||
automatically generated table of contents will be shown instead.
|
|
||||||
|
|
||||||
## SIDEBAR FILES
|
|
||||||
|
|
||||||
Sidebar files allow you to add a simple sidebar to your wiki. Sidebar files
|
|
||||||
are named `_Sidebar.ext` where the extension is one of the supported formats.
|
|
||||||
Sidebars affect all pages in their directory and any subdirectories that do not
|
|
||||||
have a sidebar file of their own.
|
|
||||||
|
|
||||||
## HEADER FILES
|
|
||||||
|
|
||||||
Header files allow you to add a simple header to your wiki. Header files must
|
|
||||||
be named `_Header.ext` where the extension is one of the supported formats.
|
|
||||||
Like sidebars, headers affect all pages in their directory and any
|
|
||||||
subdirectories that do not have a header file of their own.
|
|
||||||
|
|
||||||
## FOOTER FILES
|
|
||||||
|
|
||||||
Footer files allow you to add a simple footer to your wiki. Footer files must
|
|
||||||
be named `_Footer.ext` where the extension is one of the supported formats.
|
|
||||||
Like sidebars, footers affect all pages in their directory and any
|
|
||||||
subdirectories that do not have a footer file of their own.
|
|
||||||
|
|
||||||
## HTML SANITIZATION
|
|
||||||
|
|
||||||
For security and compatibility reasons Gollum wikis may not contain custom CSS
|
|
||||||
or JavaScript. These tags will be stripped from the converted HTML. See
|
|
||||||
`docs/sanitization.md` for more details on what tags and attributes are
|
|
||||||
allowed.
|
|
||||||
|
|
||||||
## TITLES
|
|
||||||
|
|
||||||
The first defined `h1` will override the default header on a page. There are
|
|
||||||
two ways to set a page title. The metadata syntax:
|
|
||||||
|
|
||||||
<!-- --- title: New Title -->
|
|
||||||
|
|
||||||
The first `h1` tag can be set to always override the page title, without
|
|
||||||
needing to use the metadata syntax. Start gollum with the `--h1-title` flag.
|
|
||||||
|
|
||||||
## BRACKET TAGS
|
|
||||||
|
|
||||||
A variety of Gollum tags use a double bracket syntax. For example:
|
|
||||||
|
|
||||||
[[Link]]
|
|
||||||
|
|
||||||
Some tags will accept attributes which are separated by pipe symbols. For
|
|
||||||
example:
|
|
||||||
|
|
||||||
[[Link|Page Title]]
|
|
||||||
|
|
||||||
In all cases, the first thing in the link is what is displayed on the page.
|
|
||||||
So, if the tag is an internal wiki link, the first thing in the tag will be
|
|
||||||
the link text displayed on the page. If the tag is an embedded image, the
|
|
||||||
first thing in the tag will be a path to an image file. Use this trick to
|
|
||||||
easily remember which order things should appear in tags.
|
|
||||||
|
|
||||||
Some formats, such as MediaWiki, support the opposite syntax:
|
|
||||||
|
|
||||||
[[Page Title|Link]]
|
|
||||||
|
|
||||||
## PAGE LINKS
|
|
||||||
|
|
||||||
To link to another Gollum wiki page, use the Gollum Page Link Tag.
|
|
||||||
|
|
||||||
[[Frodo Baggins]]
|
|
||||||
|
|
||||||
The above tag will create a link to the corresponding page file named
|
|
||||||
`Frodo-Baggins.ext` where `ext` may be any of the allowed extension types. The
|
|
||||||
conversion is as follows:
|
|
||||||
|
|
||||||
1. Replace any spaces (U+0020) with dashes (U+002D)
|
|
||||||
2. Replace any slashes (U+002F) with dashes (U+002D)
|
|
||||||
|
|
||||||
If you'd like the link text to be something that doesn't map directly to the
|
|
||||||
page name, you can specify the actual page name after a pipe:
|
|
||||||
|
|
||||||
[[Frodo|Frodo Baggins]]
|
|
||||||
|
|
||||||
The above tag will link to `Frodo-Baggins.ext` using "Frodo" as the link text.
|
|
||||||
|
|
||||||
The page file may exist anywhere in the directory structure of the repository.
|
|
||||||
Gollum does a breadth first search and uses the first match that it finds.
|
|
||||||
|
|
||||||
Here are a few more examples:
|
|
||||||
|
|
||||||
[[J. R. R. Tolkien]] -> J.-R.-R.-Tolkien.ext
|
|
||||||
[[Movies / The Hobbit]] -> Movies---The-Hobbit.ext
|
|
||||||
[[モルドール]] -> モルドール.ext
|
|
||||||
|
|
||||||
|
|
||||||
## EXTERNAL LINKS
|
|
||||||
|
|
||||||
As a convenience, simple external links can be placed within brackets and they
|
|
||||||
will be linked to the given URL with the URL as the link text. For example:
|
|
||||||
|
|
||||||
[[http://example.com]]
|
|
||||||
|
|
||||||
External links must begin with either "http://" or "https://". If you need
|
|
||||||
something more flexible, you can resort to the link syntax in the page's
|
|
||||||
underlying markup format.
|
|
||||||
|
|
||||||
|
|
||||||
## ABSOLUTE VS. RELATIVE VS. EXTERNAL PATH
|
|
||||||
|
|
||||||
For Gollum tags that operate on static files (images, PDFs, etc), the paths
|
|
||||||
may be referenced as either relative, absolute, or external. Relative paths
|
|
||||||
point to a static file relative to the page file within the directory
|
|
||||||
structure of the Gollum repo (even though after conversion, all page files
|
|
||||||
appear to be top level). These paths are NOT prefixed with a slash. For
|
|
||||||
example:
|
|
||||||
|
|
||||||
gollum.pdf
|
|
||||||
docs/diagram.png
|
|
||||||
|
|
||||||
Absolute paths point to a static file relative to the Gollum repo's
|
|
||||||
root, regardless of where the page file is stored within the directory
|
|
||||||
structure. These paths ARE prefixed with a slash. For example:
|
|
||||||
|
|
||||||
/pdfs/gollum.pdf
|
|
||||||
/docs/diagram.png
|
|
||||||
|
|
||||||
External paths are full URLs. An external path must begin with either
|
|
||||||
"http://" or "https://". For example:
|
|
||||||
|
|
||||||
http://example.com/pdfs/gollum.pdf
|
|
||||||
http://example.com/images/diagram.png
|
|
||||||
|
|
||||||
All of the examples in this README use relative paths, but you may use
|
|
||||||
whatever works best in your situation.
|
|
||||||
|
|
||||||
|
|
||||||
## FILE LINKS
|
|
||||||
|
|
||||||
To link to static files that are contained in the Gollum repository you should
|
|
||||||
use the Gollum File Link Tag.
|
|
||||||
|
|
||||||
[[Gollum|gollum.pdf]]
|
|
||||||
|
|
||||||
The first part of the tag is the link text. The path to the file appears after
|
|
||||||
the pipe.
|
|
||||||
|
|
||||||
|
|
||||||
## IMAGES
|
|
||||||
|
|
||||||
To display images that are contained in the Gollum repository you should use
|
|
||||||
the Gollum Image Tag. This will display the actual image on the page.
|
|
||||||
|
|
||||||
[[gollum.png]]
|
|
||||||
|
|
||||||
In addition to the simple format, there are a variety of options that you
|
|
||||||
can specify between pipe delimiters.
|
|
||||||
|
|
||||||
To specify alt text, use the `alt=` option. Default is no alt text.
|
|
||||||
|
|
||||||
[[gollum.png|alt=Gollum and his precious wiki]]
|
|
||||||
|
|
||||||
To place the image in a frame, use the `frame` option. When combined with the
|
|
||||||
`alt=` option, the alt text will be used as a caption as well. Default is no
|
|
||||||
frame.
|
|
||||||
|
|
||||||
[[gollum.png|frame|alt=Gollum and his precious wiki]]
|
|
||||||
|
|
||||||
To specify the alignment of the image on the page, use the `align=` option.
|
|
||||||
Possible values are `left`, `center`, and `right`. Default is `left`.
|
|
||||||
|
|
||||||
[[gollum.png|align=center]]
|
|
||||||
|
|
||||||
To float an image so that text flows around it, use the `float` option. When
|
|
||||||
`float` is specified, only `left` and `right` are valid `align` options.
|
|
||||||
Default is not floating. When floating is activated but no alignment is
|
|
||||||
specified, default alignment is `left`.
|
|
||||||
|
|
||||||
[[gollum.png|float]]
|
|
||||||
|
|
||||||
By default text will fill up all the space around the image. To control how
|
|
||||||
much should show up use this tag to stop and start a new block so that
|
|
||||||
additional content doesn't fill in.
|
|
||||||
|
|
||||||
[[_]]
|
|
||||||
|
|
||||||
To specify a max-width, use the `width=` option. Units must be specified in
|
|
||||||
either `px` or `em`.
|
|
||||||
|
|
||||||
[[gollum.png|width=400px]]
|
|
||||||
|
|
||||||
To specify a max-height, use the `height=` option. Units must be specified in
|
|
||||||
either `px` or `em`.
|
|
||||||
|
|
||||||
[[gollum.png|height=300px]]
|
|
||||||
|
|
||||||
Any of these options may be composed together by simply separating them with
|
|
||||||
pipes.
|
|
||||||
|
|
||||||
|
|
||||||
## ESCAPING GOLLUM TAGS
|
|
||||||
|
|
||||||
If you need the literal text of a wiki or static link to show up in your final
|
|
||||||
wiki page, simply preface the link with a single quote (like in LISP):
|
|
||||||
|
|
||||||
'[[Page Link]]
|
|
||||||
'[[File Link|file.pdf]]
|
|
||||||
'[[image.jpg]]
|
|
||||||
|
|
||||||
This is useful for writing about the link syntax in your wiki pages.
|
|
||||||
|
|
||||||
## TABLE OF CONTENTS
|
|
||||||
|
|
||||||
Gollum has a special tag to insert a table of contents (new in v2.1)
|
|
||||||
|
|
||||||
[[_TOC_]]
|
|
||||||
|
|
||||||
This tag is case sensitive, use all upper case. The TOC tag can be inserted
|
|
||||||
into the `_Header`, `_Footer` or `_Sidebar` files too.
|
|
||||||
|
|
||||||
There is also a wiki option `:universal_toc` which will display a
|
|
||||||
table of contents at the top of all your wiki pages if it is enabled.
|
|
||||||
The `:universal_toc` is not enabled by default. To set the option,
|
|
||||||
add the option to the `:wiki_options` hash before starting the
|
|
||||||
frontend app:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
Precious::App.set(:wiki_options, {:universal_toc => true})
|
|
||||||
```
|
|
||||||
|
|
||||||
## SYNTAX HIGHLIGHTING
|
|
||||||
|
|
||||||
In page files you can get automatic syntax highlighting for a wide range of
|
|
||||||
languages (courtesy of [Pygments](http://pygments.org/) - must install
|
|
||||||
separately) by using the following syntax:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
def foo
|
|
||||||
puts 'bar'
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
The block must start with three backticks, at the beginning of a line or
|
|
||||||
indented with any number of spaces or tabs.
|
|
||||||
After that comes the name of the language that is contained by the
|
|
||||||
block. The language must be one of the `short name` lexer strings supported by
|
|
||||||
Pygments. See the [list of lexers](http://pygments.org/docs/lexers/) for valid
|
|
||||||
options.
|
|
||||||
|
|
||||||
The block contents should be indented at the same level than the opening backticks.
|
|
||||||
If the block contents are indented with an additional two spaces or one tab,
|
|
||||||
then that whitespace will be ignored (this makes the blocks easier to read in plaintext).
|
|
||||||
|
|
||||||
The block must end with three backticks indented at the same level than the opening
|
|
||||||
backticks.
|
|
||||||
|
|
||||||
### GITHUB SYNTAX HIGHLIGHTING
|
|
||||||
|
|
||||||
As an extra feature, you can syntax highlight a file from your repository, allowing
|
|
||||||
you keep some of your sample code in the main repository. The code-snippet is
|
|
||||||
updated when the wiki is rebuilt. You include github code like this:
|
|
||||||
|
|
||||||
```html:github:gollum/gollum/master/test/file_view/1_file.txt```
|
|
||||||
|
|
||||||
This will make the builder look at the **gollum user**, in the **gollum project**,
|
|
||||||
in the **master branch**, at path **test/file_view/1_file.txt**. It will be
|
|
||||||
rewritten to:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<ol class="tree">
|
|
||||||
<li class="file"><a href="0">0</a></li>
|
|
||||||
</ol>
|
|
||||||
```
|
|
||||||
|
|
||||||
Which will be parsed as HTML code during the Pygments run, and thereby coloured
|
|
||||||
appropriately.
|
|
||||||
|
|
||||||
## MATHEMATICAL EQUATIONS
|
|
||||||
|
|
||||||
Start gollum with the `--mathjax` flag. Read more about [MathJax](http://docs.mathjax.org/en/latest/index.html) on the web. Gollum uses the `TeX-AMS-MML_HTMLorMML` config with the `autoload-all` extension.
|
|
||||||
|
|
||||||
Inline math:
|
|
||||||
|
|
||||||
- \\(2^2\\)
|
|
||||||
|
|
||||||
Display math:
|
|
||||||
|
|
||||||
- $$2^2$$
|
|
||||||
- \\[2^2\\]
|
|
||||||
|
|
||||||
## SEQUENCE DIAGRAMS
|
|
||||||
|
|
||||||
You may imbed sequence diagrams into your wiki page (rendered by
|
|
||||||
[WebSequenceDiagrams](http://www.websequencediagrams.com) by using the
|
|
||||||
following syntax:
|
|
||||||
|
|
||||||
{{{{{{ blue-modern
|
|
||||||
alice->bob: Test
|
|
||||||
bob->alice: Test response
|
|
||||||
}}}}}}
|
|
||||||
|
|
||||||
You can replace the string "blue-modern" with any supported style.
|
|
||||||
|
|
||||||
## API DOCUMENTATION
|
|
||||||
|
|
||||||
The [Gollum API](https://github.com/gollum/gollum-lib/) allows you to retrieve
|
|
||||||
raw or formatted wiki content from a Git repository, write new content to the
|
|
||||||
repository, and collect various meta data about the wiki as a whole.
|
|
||||||
|
|
||||||
Initialize the `Gollum::Repo` object:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# Require rubygems if necessary
|
|
||||||
require 'rubygems'
|
|
||||||
|
|
||||||
# Require the Gollum library
|
|
||||||
require 'gollum-lib'
|
|
||||||
|
|
||||||
# Create a new Gollum::Wiki object by initializing it with the path to the
|
|
||||||
# Git repository.
|
|
||||||
wiki = Gollum::Wiki.new("my-gollum-repo.git")
|
|
||||||
# => <Gollum::Wiki>
|
|
||||||
```
|
|
||||||
|
|
||||||
By default, internal wiki links are all absolute from the root. To specify a different
|
|
||||||
base path, you can specify the `:base_path` option:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
wiki = Gollum::Wiki.new("my-gollum-repo.git", :base_path => "/wiki")
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that base_path just modifies the links. To map gollum to a non-root location:
|
|
||||||
|
|
||||||
- Use the gollum binary: `gollum path/to/wiki --base-path mywiki`
|
|
||||||
- Define config.ru with `map`. See [#532](https://github.com/gollum/gollum/issues/532) for an example.
|
|
||||||
|
|
||||||
> :base_path - String base path for all Wiki links.
|
|
||||||
>
|
|
||||||
> 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 "/".
|
|
||||||
|
|
||||||
Get the latest version of the given human or canonical page name:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
page = wiki.page('page-name')
|
|
||||||
# => <Gollum::Page>
|
|
||||||
|
|
||||||
page.raw_data
|
|
||||||
# => "# My wiki page"
|
|
||||||
|
|
||||||
page.formatted_data
|
|
||||||
# => "<h1>My wiki page</h1>"
|
|
||||||
|
|
||||||
page.format
|
|
||||||
# => :markdown
|
|
||||||
|
|
||||||
vsn = page.version
|
|
||||||
# => <Grit::Commit>
|
|
||||||
|
|
||||||
vsn.id
|
|
||||||
# => '3ca43e12377ea1e32ea5c9ce5992ec8bf266e3e5'
|
|
||||||
```
|
|
||||||
|
|
||||||
Get the footer (if any) for a given page:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
page.footer
|
|
||||||
# => <Gollum::Page>
|
|
||||||
```
|
|
||||||
|
|
||||||
Get the header (if any) for a given page:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
page.header
|
|
||||||
# => <Gollum::Page>
|
|
||||||
```
|
|
||||||
|
|
||||||
Get a list of versions for a given page:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
vsns = wiki.page('page-name').versions
|
|
||||||
# => [<Grit::Commit, <Grit::Commit, <Grit::Commit>]
|
|
||||||
|
|
||||||
vsns.first.id
|
|
||||||
# => '3ca43e12377ea1e32ea5c9ce5992ec8bf266e3e5'
|
|
||||||
|
|
||||||
vsns.first.authored_date
|
|
||||||
# => Sun Mar 28 19:11:21 -0700 2010
|
|
||||||
```
|
|
||||||
|
|
||||||
Get a specific version of a given canonical page file:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
wiki.page('page-name', '5ec521178e0eec4dc39741a8978a2ba6616d0f0a')
|
|
||||||
```
|
|
||||||
|
|
||||||
Get the latest version of a given static file:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
file = wiki.file('asset.js')
|
|
||||||
# => <Gollum::File>
|
|
||||||
|
|
||||||
file.raw_data
|
|
||||||
# => "alert('hello');"
|
|
||||||
|
|
||||||
file.version
|
|
||||||
# => <Grit::Commit>
|
|
||||||
```
|
|
||||||
|
|
||||||
Get a specific version of a given static file:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
wiki.file('asset.js', '5ec521178e0eec4dc39741a8978a2ba6616d0f0a')
|
|
||||||
```
|
|
||||||
|
|
||||||
Get an in-memory Page preview (useful for generating previews for web
|
|
||||||
interfaces):
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
preview = wiki.preview_page("My Page", "# Contents", :markdown)
|
|
||||||
preview.formatted_data
|
|
||||||
# => "<h1>Contents</h1>"
|
|
||||||
```
|
|
||||||
|
|
||||||
Methods that write to the repository require a Hash of commit data that takes
|
|
||||||
the following form:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
commit = { :message => 'commit message',
|
|
||||||
:name => 'Tom Preston-Werner',
|
|
||||||
:email => 'tom@github.com' }
|
|
||||||
```
|
|
||||||
|
|
||||||
Write a new version of a page (the file will be created if it does not already
|
|
||||||
exist) and commit the change. The file will be written at the repo root.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
wiki.write_page('Page Name', :markdown, 'Page contents', commit)
|
|
||||||
```
|
|
||||||
|
|
||||||
Update an existing page. If the format is different than the page's current
|
|
||||||
format, the file name will be changed to reflect the new format.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
page = wiki.page('Page Name')
|
|
||||||
wiki.update_page(page, page.name, page.format, 'Page contents', commit)
|
|
||||||
```
|
|
||||||
|
|
||||||
To delete a page and commit the change:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
wiki.delete_page(page, commit)
|
|
||||||
```
|
|
||||||
|
|
||||||
### RACK
|
### RACK
|
||||||
|
|
||||||
You can also run gollum with any rack-compatible server by placing this config.ru
|
You can also run gollum with any rack-compatible server by placing this config.ru
|
||||||
@@ -597,10 +113,6 @@ run Precious::App
|
|||||||
|
|
||||||
Your Rack middleware can pass author details to Gollum in a Hash in the session under the 'gollum.author' key.
|
Your Rack middleware can pass author details to Gollum in a Hash in the session under the 'gollum.author' key.
|
||||||
|
|
||||||
## WINDOWS FILENAME VALIDATION
|
|
||||||
|
|
||||||
Note that filenames on windows must not contain any of the following characters `\ / : * ? " < > |`. See [this support article](http://support.microsoft.com/kb/177506) for details.
|
|
||||||
|
|
||||||
## CONFIG FILE
|
## CONFIG FILE
|
||||||
|
|
||||||
Gollum optionally takes a `--config file`. See [config.rb](https://github.com/gollum/gollum/blob/master/config.rb) for an example.
|
Gollum optionally takes a `--config file`. See [config.rb](https://github.com/gollum/gollum/blob/master/config.rb) for an example.
|
||||||
@@ -611,6 +123,14 @@ The `--css` flag will inject `custom.css` from the root of your git repository i
|
|||||||
|
|
||||||
The `--js` flag will inject `custom.js` from the root of your git repository into each page. `custom.js` must be commited to git or you will get a 302 redirect to the create page.
|
The `--js` flag will inject `custom.js` from the root of your git repository into each page. `custom.js` must be commited to git or you will get a 302 redirect to the create page.
|
||||||
|
|
||||||
|
|
||||||
|
## API DOCUMENTATION
|
||||||
|
|
||||||
|
The [Gollum API](https://github.com/gollum/gollum-lib/) allows you to retrieve
|
||||||
|
raw or formatted wiki content from a Git repository, write new content to the
|
||||||
|
repository, and collect various meta data about the wiki as a whole.
|
||||||
|
|
||||||
|
|
||||||
## CONTRIBUTE
|
## CONTRIBUTE
|
||||||
|
|
||||||
If you'd like to hack on Gollum, start by forking the repo on GitHub:
|
If you'd like to hack on Gollum, start by forking the repo on GitHub:
|
||||||
|
|||||||
+14
-3
@@ -19,7 +19,10 @@ require 'gollum'
|
|||||||
|
|
||||||
exec = {}
|
exec = {}
|
||||||
options = { 'port' => 4567, 'bind' => '0.0.0.0' }
|
options = { 'port' => 4567, 'bind' => '0.0.0.0' }
|
||||||
wiki_options = {}
|
wiki_options = {
|
||||||
|
:live_preview => false,
|
||||||
|
:allow_uploads => false,
|
||||||
|
}
|
||||||
|
|
||||||
opts = OptionParser.new do |opts|
|
opts = OptionParser.new do |opts|
|
||||||
opts.banner = help
|
opts.banner = help
|
||||||
@@ -73,6 +76,14 @@ opts = OptionParser.new do |opts|
|
|||||||
wiki_options[:live_preview] = false
|
wiki_options[:live_preview] = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
opts.on("--live-preview", "Enables livepreview.") do
|
||||||
|
wiki_options[:live_preview] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
opts.on("--allow-uploads", "Allows file uploads.") do
|
||||||
|
wiki_options[:allow_uploads] = true
|
||||||
|
end
|
||||||
|
|
||||||
opts.on("--mathjax", "Enables mathjax.") do
|
opts.on("--mathjax", "Enables mathjax.") do
|
||||||
wiki_options[:mathjax] = true
|
wiki_options[:mathjax] = true
|
||||||
end
|
end
|
||||||
@@ -135,7 +146,7 @@ if options['irb']
|
|||||||
begin
|
begin
|
||||||
require 'gollum-lib'
|
require 'gollum-lib'
|
||||||
wiki = Gollum::Wiki.new(gollum_path, wiki_options)
|
wiki = Gollum::Wiki.new(gollum_path, wiki_options)
|
||||||
if !wiki.exist? then raise Grit::InvalidGitRepositoryError end
|
if !wiki.exist? then raise Gollum::InvalidGitRepositoryError end
|
||||||
puts "Loaded Gollum wiki at #{File.expand_path(gollum_path).inspect}."
|
puts "Loaded Gollum wiki at #{File.expand_path(gollum_path).inspect}."
|
||||||
puts
|
puts
|
||||||
puts %( page = wiki.page('page-name'))
|
puts %( page = wiki.page('page-name'))
|
||||||
@@ -149,7 +160,7 @@ if options['irb']
|
|||||||
puts
|
puts
|
||||||
puts "Check out the Gollum README for more."
|
puts "Check out the Gollum README for more."
|
||||||
IRB.start_session(binding)
|
IRB.start_session(binding)
|
||||||
rescue Grit::InvalidGitRepositoryError, Grit::NoSuchPathError
|
rescue Gollum::InvalidGitRepositoryError, Gollum::NoSuchPathError
|
||||||
puts "Invalid Gollum wiki at #{File.expand_path(gollum_path).inspect}"
|
puts "Invalid Gollum wiki at #{File.expand_path(gollum_path).inspect}"
|
||||||
exit 0
|
exit 0
|
||||||
end
|
end
|
||||||
|
|||||||
+12
-8
@@ -2,12 +2,13 @@ Gem::Specification.new do |s|
|
|||||||
s.specification_version = 2 if s.respond_to? :specification_version=
|
s.specification_version = 2 if s.respond_to? :specification_version=
|
||||||
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
||||||
s.rubygems_version = '1.3.5'
|
s.rubygems_version = '1.3.5'
|
||||||
s.required_ruby_version = ">= 1.8.7"
|
s.required_ruby_version = ">= 1.9"
|
||||||
|
|
||||||
s.name = 'gollum'
|
s.name = 'gollum'
|
||||||
s.version = '2.4.13'
|
s.version = '2.5.2'
|
||||||
s.date = '2013-04-03'
|
s.date = '2013-11-02'
|
||||||
s.rubyforge_project = 'gollum'
|
s.rubyforge_project = 'gollum'
|
||||||
|
s.license = 'MIT'
|
||||||
|
|
||||||
s.summary = "A simple, Git-powered wiki."
|
s.summary = "A simple, Git-powered wiki."
|
||||||
s.description = "A simple, Git-powered wiki with a sweet API and local frontend."
|
s.description = "A simple, Git-powered wiki with a sweet API and local frontend."
|
||||||
@@ -23,14 +24,15 @@ 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 'gollum-lib', '~> 1.0.0'
|
s.add_dependency 'gollum-lib', '~> 1.0.9'
|
||||||
s.add_dependency 'sinatra', '~> 1.4.2'
|
s.add_dependency 'github-markdown', '~> 0.5.5'
|
||||||
|
s.add_dependency 'sinatra', '~> 1.4.3'
|
||||||
s.add_dependency 'mustache', ['>= 0.99.4', '< 1.0.0']
|
s.add_dependency 'mustache', ['>= 0.99.4', '< 1.0.0']
|
||||||
s.add_dependency 'useragent', '~> 0.4.16'
|
s.add_dependency 'useragent', '~> 0.8.2'
|
||||||
|
|
||||||
s.add_development_dependency 'rack-test', '~> 0.6.2'
|
s.add_development_dependency 'rack-test', '~> 0.6.2'
|
||||||
s.add_development_dependency 'shoulda', '~> 3.4.0'
|
s.add_development_dependency 'shoulda', ['>= 3.4.0', '< 3.5.0']
|
||||||
s.add_development_dependency 'minitest-reporters', '~> 0.14.10'
|
s.add_development_dependency 'minitest-reporters', '~> 0.14.16'
|
||||||
|
|
||||||
# = MANIFEST =
|
# = MANIFEST =
|
||||||
s.files = %w[
|
s.files = %w[
|
||||||
@@ -528,6 +530,8 @@ Gem::Specification.new do |s|
|
|||||||
licenses/css_tree_menu_thecssninja/license.txt
|
licenses/css_tree_menu_thecssninja/license.txt
|
||||||
licenses/licenses.txt
|
licenses/licenses.txt
|
||||||
licenses/unity_asset_pool/COPYRIGHT
|
licenses/unity_asset_pool/COPYRIGHT
|
||||||
|
openrc/conf.d/gollum
|
||||||
|
openrc/init.d/gollum
|
||||||
templates/formatting.html
|
templates/formatting.html
|
||||||
templates/helper_wiki.rb
|
templates/helper_wiki.rb
|
||||||
]
|
]
|
||||||
|
|||||||
+1
-2
@@ -5,7 +5,6 @@ require 'digest/sha1'
|
|||||||
require 'ostruct'
|
require 'ostruct'
|
||||||
|
|
||||||
# external
|
# external
|
||||||
require 'grit'
|
|
||||||
require 'github/markup'
|
require 'github/markup'
|
||||||
require 'sanitize'
|
require 'sanitize'
|
||||||
|
|
||||||
@@ -17,7 +16,7 @@ require File.expand_path('../gollum/uri_encode_component', __FILE__)
|
|||||||
$KCODE = 'U' if RUBY_VERSION[0,3] == '1.8'
|
$KCODE = 'U' if RUBY_VERSION[0,3] == '1.8'
|
||||||
|
|
||||||
module Gollum
|
module Gollum
|
||||||
VERSION = '2.4.13'
|
VERSION = '2.5.2'
|
||||||
|
|
||||||
def self.assets_path
|
def self.assets_path
|
||||||
::File.expand_path('gollum/public', ::File.dirname(__FILE__))
|
::File.expand_path('gollum/public', ::File.dirname(__FILE__))
|
||||||
|
|||||||
+67
-10
@@ -13,13 +13,17 @@ require 'gollum/views/has_page'
|
|||||||
|
|
||||||
require File.expand_path '../helpers', __FILE__
|
require File.expand_path '../helpers', __FILE__
|
||||||
|
|
||||||
|
#required to upload bigger binary files
|
||||||
|
Gollum::set_git_timeout(120)
|
||||||
|
Gollum::set_git_max_filesize(190 * 10**6)
|
||||||
|
|
||||||
# Fix to_url
|
# Fix to_url
|
||||||
class String
|
class String
|
||||||
alias :upstream_to_url :to_url
|
alias :upstream_to_url :to_url
|
||||||
# _Header => header which causes errors
|
# _Header => header which causes errors
|
||||||
def to_url
|
def to_url
|
||||||
return nil if self.nil?
|
return nil if self.nil?
|
||||||
upstream_to_url :exclude => ['_Header', '_Footer', '_Sidebar']
|
upstream_to_url :exclude => ['_Header', '_Footer', '_Sidebar'], :force_downcase => false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -101,7 +105,7 @@ module Precious
|
|||||||
# name, path, version
|
# name, path, version
|
||||||
def wiki_page(name, path = nil, version = nil, exact = true)
|
def wiki_page(name, path = nil, version = nil, exact = true)
|
||||||
wiki = wiki_new
|
wiki = wiki_new
|
||||||
|
|
||||||
path = name if path.nil?
|
path = name if path.nil?
|
||||||
name = extract_name(name) || wiki.index_page
|
name = extract_name(name) || wiki.index_page
|
||||||
path = extract_path(path)
|
path = extract_path(path)
|
||||||
@@ -125,7 +129,7 @@ module Precious
|
|||||||
wikip = wiki_page(params[:splat].first)
|
wikip = wiki_page(params[:splat].first)
|
||||||
@name = wikip.name
|
@name = wikip.name
|
||||||
@path = wikip.path
|
@path = wikip.path
|
||||||
|
|
||||||
wiki = wikip.wiki
|
wiki = wikip.wiki
|
||||||
if page = wikip.page
|
if page = wikip.page
|
||||||
if wiki.live_preview && page.format.to_s.include?('markdown') && supported_useragent?(request.user_agent)
|
if wiki.live_preview && page.format.to_s.include?('markdown') && supported_useragent?(request.user_agent)
|
||||||
@@ -137,8 +141,7 @@ module Precious
|
|||||||
else
|
else
|
||||||
@page = page
|
@page = page
|
||||||
@page.version = wiki.repo.log(wiki.ref, @page.path).first
|
@page.version = wiki.repo.log(wiki.ref, @page.path).first
|
||||||
raw_data = page.raw_data
|
@content = page.text_data
|
||||||
@content = raw_data.respond_to?(:force_encoding) ? raw_data.force_encoding('UTF-8') : raw_data
|
|
||||||
mustache :edit
|
mustache :edit
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@@ -146,6 +149,53 @@ module Precious
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
post '/uploadFile' do
|
||||||
|
wiki = wiki_new
|
||||||
|
|
||||||
|
unless wiki.allow_uploads
|
||||||
|
@message = "File uploads are disabled"
|
||||||
|
mustache :error
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if params[:file]
|
||||||
|
fullname = params[:file][:filename]
|
||||||
|
tempfile = params[:file][:tempfile]
|
||||||
|
end
|
||||||
|
|
||||||
|
dir = 'uploads'
|
||||||
|
ext = ::File.extname(fullname)
|
||||||
|
format = ext.split('.').last || 'txt'
|
||||||
|
filename = ::File.basename(fullname, ext)
|
||||||
|
contents = ::File.read(tempfile)
|
||||||
|
reponame = filename + '.' + format
|
||||||
|
|
||||||
|
head = wiki.repo.head
|
||||||
|
|
||||||
|
options = {
|
||||||
|
:message => "Uploaded file to uploads/#{reponame}",
|
||||||
|
:parent => wiki.repo.head.commit,
|
||||||
|
}
|
||||||
|
author = session['gollum.author']
|
||||||
|
unless author.nil?
|
||||||
|
options.merge! author
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
committer = Gollum::Committer.new(wiki, options)
|
||||||
|
committer.add_to_index(dir, filename, format, contents)
|
||||||
|
committer.after_commit do |committer, sha|
|
||||||
|
wiki.clear_cache
|
||||||
|
committer.update_working_dir(dir, filename, format)
|
||||||
|
end
|
||||||
|
committer.commit
|
||||||
|
redirect to('/')
|
||||||
|
rescue Gollum::DuplicatePageError => e
|
||||||
|
@message = "Duplicate page: #{e.message}"
|
||||||
|
mustache :error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
post '/rename/*' do
|
post '/rename/*' do
|
||||||
wikip = wiki_page(params[:splat].first)
|
wikip = wiki_page(params[:splat].first)
|
||||||
halt 500 if wikip.nil?
|
halt 500 if wikip.nil?
|
||||||
@@ -222,6 +272,7 @@ module Precious
|
|||||||
# not /docs/Home because write_page will append /docs
|
# not /docs/Home because write_page will append /docs
|
||||||
@path = @path.sub(page_dir, '/') if @path.start_with? page_dir
|
@path = @path.sub(page_dir, '/') if @path.start_with? page_dir
|
||||||
end
|
end
|
||||||
|
@path = clean_path(@path)
|
||||||
|
|
||||||
page = wikip.page
|
page = wikip.page
|
||||||
if page
|
if page
|
||||||
@@ -282,6 +333,7 @@ module Precious
|
|||||||
@mathjax = wiki.mathjax
|
@mathjax = wiki.mathjax
|
||||||
@h1_title = wiki.h1_title
|
@h1_title = wiki.h1_title
|
||||||
@editable = false
|
@editable = false
|
||||||
|
@allow_uploads = wiki.allow_uploads
|
||||||
mustache :page
|
mustache :page
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -393,18 +445,23 @@ module Precious
|
|||||||
@page = page
|
@page = page
|
||||||
@name = name
|
@name = name
|
||||||
@content = page.formatted_data
|
@content = page.formatted_data
|
||||||
|
|
||||||
# Extensions and layout data
|
# Extensions and layout data
|
||||||
@editable = true
|
@editable = true
|
||||||
@toc_content = wiki.universal_toc ? @page.toc_data : nil
|
@toc_content = wiki.universal_toc ? @page.toc_data : nil
|
||||||
@mathjax = wiki.mathjax
|
@mathjax = wiki.mathjax
|
||||||
@h1_title = wiki.h1_title
|
@h1_title = wiki.h1_title
|
||||||
@bar_side = wiki.bar_side
|
@bar_side = wiki.bar_side
|
||||||
|
@allow_uploads = wiki.allow_uploads
|
||||||
|
|
||||||
mustache :page
|
mustache :page
|
||||||
elsif file = wiki.file(fullpath)
|
elsif file = wiki.file(fullpath, wiki.ref, true)
|
||||||
content_type file.mime_type
|
if file.on_disk?
|
||||||
file.raw_data
|
send_file file.on_disk_path, :disposition => 'inline'
|
||||||
|
else
|
||||||
|
content_type file.mime_type
|
||||||
|
file.raw_data
|
||||||
|
end
|
||||||
else
|
else
|
||||||
page_path = [path, name].compact.join('/')
|
page_path = [path, name].compact.join('/')
|
||||||
redirect to("/create/#{clean_url(encodeURIComponent(page_path))}")
|
redirect to("/create/#{clean_url(encodeURIComponent(page_path))}")
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ module Precious
|
|||||||
if file_path[-1, 1] == "/"
|
if file_path[-1, 1] == "/"
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
# File.basename is too eager to please and will return the last
|
# File.basename is too eager to please and will return the last
|
||||||
# component of the path even if it ends with a directory separator.
|
# component of the path even if it ends with a directory separator.
|
||||||
::File.basename(file_path)
|
::File.basename(file_path)
|
||||||
end
|
end
|
||||||
@@ -25,6 +25,13 @@ module Precious
|
|||||||
[nil,''].include?(param) ? nil : CGI.unescape(param)
|
[nil,''].include?(param) ? nil : CGI.unescape(param)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Ensure path begins with a single leading slash
|
||||||
|
def clean_path(path)
|
||||||
|
if path
|
||||||
|
(path[0] != '/' ? path.insert(0, '/') : path).gsub(/\/{2,}/,'/')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Remove all slashes from the start of string.
|
# Remove all slashes from the start of string.
|
||||||
# Remove all double slashes
|
# Remove all double slashes
|
||||||
def clean_url url
|
def clean_url url
|
||||||
|
|||||||
@@ -124,7 +124,8 @@ a.absent {
|
|||||||
.markdown-body ol,
|
.markdown-body ol,
|
||||||
.markdown-body dl,
|
.markdown-body dl,
|
||||||
.markdown-body table,
|
.markdown-body table,
|
||||||
.markdown-body pre {
|
.markdown-body pre,
|
||||||
|
.markdown-body hr {
|
||||||
margin: 0px 0;
|
margin: 0px 0;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,11 +37,14 @@
|
|||||||
fieldMarkup += '<div class="field">';
|
fieldMarkup += '<div class="field">';
|
||||||
switch ( fieldArray[i].type ) {
|
switch ( fieldArray[i].type ) {
|
||||||
|
|
||||||
// only text is supported for now
|
|
||||||
case 'text':
|
case 'text':
|
||||||
fieldMarkup += Dialog.createFieldText( fieldArray[i] );
|
fieldMarkup += Dialog.createFieldText( fieldArray[i] );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'file':
|
||||||
|
fieldMarkup += Dialog.createFieldFile( fieldArray[i] );
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -60,7 +63,7 @@
|
|||||||
if ( fieldAttributes.name ) {
|
if ( fieldAttributes.name ) {
|
||||||
html += '<label';
|
html += '<label';
|
||||||
if ( fieldAttributes.id ) {
|
if ( fieldAttributes.id ) {
|
||||||
html += ' for="' + fieldAttributes.name + '"';
|
html += ' for="gollum-dialog-dialog-generated-field-' + fieldAttributes.id + '"';
|
||||||
}
|
}
|
||||||
html += '>' + fieldAttributes.name + '</label>';
|
html += '>' + fieldAttributes.name + '</label>';
|
||||||
}
|
}
|
||||||
@@ -86,6 +89,26 @@
|
|||||||
return html;
|
return html;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
createFieldFile: function( fieldAttributes ) {
|
||||||
|
// Not actually a field, but an embedded form.
|
||||||
|
var html = '';
|
||||||
|
|
||||||
|
var id = fieldAttributes.id || 'upload';
|
||||||
|
var name = fieldAttributes.name || 'file';
|
||||||
|
var action = fieldAttributes.action || '/uploadFile';
|
||||||
|
|
||||||
|
html += '<form method=post enctype="multipart/form-data" ' +
|
||||||
|
'action="' + action + '" ' + 'id="' + id + '">';
|
||||||
|
html += '<input type=file name="' + name + '">';
|
||||||
|
html += '</form>';
|
||||||
|
|
||||||
|
if( fieldAttributes.context ){
|
||||||
|
html += '<span class="context">' + fieldAttributes.context + '</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return html;
|
||||||
|
},
|
||||||
|
|
||||||
createMarkup: function( title, body ) {
|
createMarkup: function( title, body ) {
|
||||||
Dialog.markupCreated = true;
|
Dialog.markupCreated = true;
|
||||||
if ($.facebox) {
|
if ($.facebox) {
|
||||||
|
|||||||
@@ -145,6 +145,26 @@ $(document).ready(function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($('#minibutton-upload-page').length) {
|
||||||
|
$('#minibutton-upload-page').parent().removeClass('jaws');
|
||||||
|
$('#minibutton-upload-page').click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
$.GollumDialog.init({
|
||||||
|
title: 'Upload File',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'file',
|
||||||
|
context: 'Your uploaded file will be accessible at /uploads/[filename]'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
OK: function( res ) {
|
||||||
|
$('#upload').submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if ($('#minibutton-rename-page').length) {
|
if ($('#minibutton-rename-page').length) {
|
||||||
$('#minibutton-rename-page').parent().removeClass('jaws');
|
$('#minibutton-rename-page').parent().removeClass('jaws');
|
||||||
$('#minibutton-rename-page').click(function(e) {
|
$('#minibutton-rename-page').click(function(e) {
|
||||||
@@ -152,7 +172,7 @@ $(document).ready(function() {
|
|||||||
|
|
||||||
var path = pagePath();
|
var path = pagePath();
|
||||||
var oldName = pageName();
|
var oldName = pageName();
|
||||||
var context_blurb =
|
var context_blurb =
|
||||||
"Renamed page will be under " +
|
"Renamed page will be under " +
|
||||||
"<span class='path'>" + htmlEscape('/' + path) + "</span>" +
|
"<span class='path'>" + htmlEscape('/' + path) + "</span>" +
|
||||||
" unless an absolute path is given."
|
" unless an absolute path is given."
|
||||||
@@ -206,7 +226,7 @@ $(document).ready(function() {
|
|||||||
// For consistency remove the trailing /
|
// For consistency remove the trailing /
|
||||||
path = path.replace(/\/$/,'')
|
path = path.replace(/\/$/,'')
|
||||||
}
|
}
|
||||||
var context_blurb =
|
var context_blurb =
|
||||||
"Page will be created under " +
|
"Page will be created under " +
|
||||||
"<span class='path'>" + htmlEscape('/' + path) + "</span>" +
|
"<span class='path'>" + htmlEscape('/' + path) + "</span>" +
|
||||||
" unless an absolute path is given."
|
" unless an absolute path is given."
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<script>
|
<script>
|
||||||
var baseUrl = '{{base_url}}';
|
var baseUrl = '{{base_url}}';
|
||||||
{{#page}}
|
{{#page}}
|
||||||
var pageFullPath = '{{url_path}}';
|
var pageFullPath = '{{url_path_display}}';
|
||||||
{{/page}}
|
{{/page}}
|
||||||
</script>
|
</script>
|
||||||
<script type="text/javascript" src="{{base_url}}/javascript/jquery-1.7.2.min.js"></script>
|
<script type="text/javascript" src="{{base_url}}/javascript/jquery-1.7.2.min.js"></script>
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ Mousetrap.bind(['e'], function( e ) {
|
|||||||
class="action-fileview">Files</a></li>
|
class="action-fileview">Files</a></li>
|
||||||
<li class="minibutton jaws">
|
<li class="minibutton jaws">
|
||||||
<a href="#" id="minibutton-new-page">New</a></li>
|
<a href="#" id="minibutton-new-page">New</a></li>
|
||||||
|
{{#allow_uploads}}
|
||||||
|
<li class="minibutton jaws">
|
||||||
|
<a href="#" id="minibutton-upload-page">Upload</a></li>
|
||||||
|
{{/allow_uploads}}
|
||||||
{{#editable}}
|
{{#editable}}
|
||||||
<li class="minibutton jaws">
|
<li class="minibutton jaws">
|
||||||
<a href="#" id="minibutton-rename-page">Rename</a></li>
|
<a href="#" id="minibutton-rename-page">Rename</a></li>
|
||||||
@@ -77,4 +81,4 @@ Mousetrap.bind(['e'], function( e ) {
|
|||||||
<form name="rename" method="POST" action="{{base_url}}/rename/{{escaped_url_path}}">
|
<form name="rename" method="POST" action="{{base_url}}/rename/{{escaped_url_path}}">
|
||||||
<input type="hidden" name="rename"/>
|
<input type="hidden" name="rename"/>
|
||||||
<input type="hidden" name="message"/>
|
<input type="hidden" name="message"/>
|
||||||
</from>
|
</form>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
# component must be String
|
# component must be String
|
||||||
def encodeURIComponent(componentString)
|
def encodeURIComponent(componentString)
|
||||||
URI::URIEncodeComponent(componentString)
|
::URI::URIEncodeComponent(componentString)
|
||||||
end
|
end
|
||||||
|
|
||||||
# define charCodeAt on String
|
# define charCodeAt on String
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ module Precious
|
|||||||
def header
|
def header
|
||||||
if @header.nil?
|
if @header.nil?
|
||||||
if page = @page.header
|
if page = @page.header
|
||||||
@header = page.raw_data
|
@header = page.text_data
|
||||||
else
|
else
|
||||||
@header = false
|
@header = false
|
||||||
end
|
end
|
||||||
@@ -33,7 +33,7 @@ module Precious
|
|||||||
def footer
|
def footer
|
||||||
if @footer.nil?
|
if @footer.nil?
|
||||||
if page = @page.footer
|
if page = @page.footer
|
||||||
@footer = page.raw_data
|
@footer = page.text_data
|
||||||
else
|
else
|
||||||
@footer = false
|
@footer = false
|
||||||
end
|
end
|
||||||
@@ -44,7 +44,7 @@ module Precious
|
|||||||
def sidebar
|
def sidebar
|
||||||
if @sidebar.nil?
|
if @sidebar.nil?
|
||||||
if page = @page.sidebar
|
if page = @page.sidebar
|
||||||
@sidebar = page.raw_data
|
@sidebar = page.text_data
|
||||||
else
|
else
|
||||||
@sidebar = false
|
@sidebar = false
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ module Precious
|
|||||||
:author => v.author.name.respond_to?(:force_encoding) ? v.author.name.force_encoding('UTF-8') : v.author.name,
|
:author => v.author.name.respond_to?(:force_encoding) ? v.author.name.force_encoding('UTF-8') : v.author.name,
|
||||||
:message => v.message.respond_to?(:force_encoding) ? v.message.force_encoding('UTF-8') : v.message,
|
:message => v.message.respond_to?(:force_encoding) ? v.message.force_encoding('UTF-8') : v.message,
|
||||||
:date => v.authored_date.strftime("%B %d, %Y"),
|
:date => v.authored_date.strftime("%B %d, %Y"),
|
||||||
:gravatar => Digest::MD5.hexdigest(v.author.email),
|
:gravatar => Digest::MD5.hexdigest(v.author.email.strip.downcase),
|
||||||
:identicon => self._identicon_code(v.author.email),
|
:identicon => self._identicon_code(v.author.email),
|
||||||
:date_full=> v.authored_date,
|
:date_full=> v.authored_date,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ module Precious
|
|||||||
@editable
|
@editable
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def allow_uploads
|
||||||
|
@allow_uploads
|
||||||
|
end
|
||||||
|
|
||||||
def has_header
|
def has_header
|
||||||
@header = (@page.header || false) if @header.nil?
|
@header = (@page.header || false) if @header.nil?
|
||||||
!!@header
|
!!@header
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
GOLLUM_USER=gollum
|
||||||
|
GOLLUM_BASE=/home/gollum/wiki
|
||||||
|
GOLLUM_OPTS="--config /home/gollum/config.rb"
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#!/sbin/runscript
|
||||||
|
# Distributed under the terms of the MIT License
|
||||||
|
|
||||||
|
NAME=gollum
|
||||||
|
PID=/var/run/${NAME}.pid
|
||||||
|
EXEC=/usr/local/bin/gollum
|
||||||
|
LOG=/var/log/gollum.log
|
||||||
|
|
||||||
|
depend() {
|
||||||
|
need net
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
# Change log file to be owned by GOLLUM_USER
|
||||||
|
touch "${LOG}"
|
||||||
|
chown "${GOLLUM_USER}" "${LOG}"
|
||||||
|
|
||||||
|
ebegin "Starting Gollum"
|
||||||
|
start-stop-daemon --start \
|
||||||
|
--name "${NAME}" \
|
||||||
|
--user "${GOLLUM_USER}" \
|
||||||
|
--pidfile "${PID}" \
|
||||||
|
--make-pidfile --background \
|
||||||
|
--stderr "${LOG}" \
|
||||||
|
--exec "${EXEC}" -- $GOLLUM_OPTS "$GOLLUM_BASE"
|
||||||
|
eend $?
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
ebegin "Stopping Gollum"
|
||||||
|
start-stop-daemon --stop \
|
||||||
|
--name "${NAME}" \
|
||||||
|
--user "${GOLLUM_USER}" \
|
||||||
|
--pidfile "${PID}"
|
||||||
|
eend $?
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
class WikiFactory
|
class WikiFactory
|
||||||
def self.create p
|
def self.create p
|
||||||
path = testpath "examples/test.git"
|
examples = testpath "examples"
|
||||||
Grit::Repo.init_bare(@path)
|
path = File.join(examples, "test.git")
|
||||||
|
FileUtils.cp_r File.join(examples, "empty.git"), path, :remove_destination => true
|
||||||
Gollum::Wiki.default_options = {:universal_toc => false}
|
Gollum::Wiki.default_options = {:universal_toc => false}
|
||||||
cleanup = Proc.new { FileUtils.rm_r File.join(File.dirname(__FILE__), *%w[examples test.git]) }
|
cleanup = Proc.new { FileUtils.rm_r File.join(File.dirname(__FILE__), *%w[examples test.git]) }
|
||||||
Gollum::Wiki.new(@path), @path, cleanup
|
Gollum::Wiki.new(@path), @path, cleanup
|
||||||
|
|||||||
+57
-14
@@ -29,13 +29,13 @@ context "Frontend" do
|
|||||||
# spaces are converted to dashes in URLs
|
# spaces are converted to dashes in URLs
|
||||||
# and in file names saved to disk
|
# and in file names saved to disk
|
||||||
# urls are not case sensitive
|
# urls are not case sensitive
|
||||||
assert_equal 'title-space', 'Title Space'.to_url
|
assert_equal 'Title-Space', 'Title Space'.to_url
|
||||||
|
|
||||||
# ascii only file names prevent UTF8 issues
|
# ascii only file names prevent UTF8 issues
|
||||||
# when using git repos across operating systems
|
# when using git repos across operating systems
|
||||||
# as this test demonstrates, translation is not
|
# as this test demonstrates, translation is not
|
||||||
# great
|
# great
|
||||||
assert_equal 'm-plus-f', 'μ†ℱ'.to_url
|
assert_equal 'm-plus-F', 'μ†ℱ'.to_url
|
||||||
end
|
end
|
||||||
|
|
||||||
test "utf-8 kcode" do
|
test "utf-8 kcode" do
|
||||||
@@ -253,7 +253,7 @@ context "Frontend" do
|
|||||||
test "creates pages with escaped characters in title" do
|
test "creates pages with escaped characters in title" do
|
||||||
post "/create", :content => 'abc', :page => 'Title with spaces',
|
post "/create", :content => 'abc', :page => 'Title with spaces',
|
||||||
:format => 'markdown', :message => 'foo'
|
:format => 'markdown', :message => 'foo'
|
||||||
assert_equal 'http://example.org/title-with-spaces', last_response.headers['Location']
|
assert_equal 'http://example.org/Title-with-spaces', last_response.headers['Location']
|
||||||
get "/Title-with-spaces"
|
get "/Title-with-spaces"
|
||||||
assert_match /abc/, last_response.body
|
assert_match /abc/, last_response.body
|
||||||
end
|
end
|
||||||
@@ -278,8 +278,8 @@ context "Frontend" do
|
|||||||
post "/create", :content => 'abc', :page => 'Home', :path => '/foo/',
|
post "/create", :content => 'abc', :page => 'Home', :path => '/foo/',
|
||||||
:format => 'markdown', :message => 'foo'
|
:format => 'markdown', :message => 'foo'
|
||||||
|
|
||||||
assert_equal "http://example.org/foo/home", last_response.headers['Location']
|
assert_equal "http://example.org/foo/Home", last_response.headers['Location']
|
||||||
|
|
||||||
follow_redirect!
|
follow_redirect!
|
||||||
assert last_response.ok?
|
assert last_response.ok?
|
||||||
end
|
end
|
||||||
@@ -300,6 +300,25 @@ context "Frontend" do
|
|||||||
assert last_response.ok?
|
assert last_response.ok?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "create sets the correct path for a relative path subdirectory" do
|
||||||
|
dir = "foodir"
|
||||||
|
name = "#{dir}/bar"
|
||||||
|
get "/create/#{name}"
|
||||||
|
assert_match(/\/#{dir}/, last_response.body)
|
||||||
|
assert_no_match(/[^\/]#{dir}/, last_response.body)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "create sets the correct path for a relative path subdirectory with the page file directory set" do
|
||||||
|
Precious::App.set(:wiki_options, {:page_file_dir => "foo"})
|
||||||
|
dir = "bardir"
|
||||||
|
name = "#{dir}/baz"
|
||||||
|
get "/create/foo/#{name}"
|
||||||
|
assert_match(/\/#{dir}/, last_response.body)
|
||||||
|
assert_no_match(/[^\/]#{dir}/, last_response.body)
|
||||||
|
# reset page_file_dir
|
||||||
|
Precious::App.set(:wiki_options, {:page_file_dir => nil})
|
||||||
|
end
|
||||||
|
|
||||||
test "edit returns nil for non-existant page" do
|
test "edit returns nil for non-existant page" do
|
||||||
# post '/edit' fails. post '/edit/' works.
|
# post '/edit' fails. post '/edit/' works.
|
||||||
page = 'not-real-page'
|
page = 'not-real-page'
|
||||||
@@ -442,20 +461,20 @@ context "Frontend" do
|
|||||||
Precious::App.set(:wiki_options, { :base_path => nil })
|
Precious::App.set(:wiki_options, { :base_path => nil })
|
||||||
end
|
end
|
||||||
=end
|
=end
|
||||||
|
|
||||||
test "author details in session are used" do
|
test "author details in session are used" do
|
||||||
page1 = @wiki.page('A')
|
page1 = @wiki.page('A')
|
||||||
|
|
||||||
gollum_author = { :name => 'ghi', :email => 'jkl' }
|
gollum_author = { :name => 'ghi', :email => 'jkl' }
|
||||||
session = { 'gollum.author' => gollum_author }
|
session = { 'gollum.author' => gollum_author }
|
||||||
|
|
||||||
post "/edit/A", { :content => 'abc', :page => 'A', :format => page1.format, :message => 'def' }, { 'rack.session' => session }
|
post "/edit/A", { :content => 'abc', :page => 'A', :format => page1.format, :message => 'def' }, { 'rack.session' => session }
|
||||||
follow_redirect!
|
follow_redirect!
|
||||||
assert last_response.ok?
|
assert last_response.ok?
|
||||||
|
|
||||||
@wiki.clear_cache
|
@wiki.clear_cache
|
||||||
page2 = @wiki.page(page1.name)
|
page2 = @wiki.page(page1.name)
|
||||||
|
|
||||||
author = page2.version.author
|
author = page2.version.author
|
||||||
assert_equal 'ghi', author.name
|
assert_equal 'ghi', author.name
|
||||||
assert_equal 'jkl', author.email
|
assert_equal 'jkl', author.email
|
||||||
@@ -485,6 +504,24 @@ context "Frontend" do
|
|||||||
Precious::App.set(:wiki_options, { :js => nil })
|
Precious::App.set(:wiki_options, { :js => nil })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "show edit page with header and footer and sidebar of multibyte" do
|
||||||
|
post "/create",
|
||||||
|
:content => 'りんご',
|
||||||
|
:page => 'Multibyte', :format => :markdown, :message => 'mesg'
|
||||||
|
|
||||||
|
post "/edit/Multibyte",
|
||||||
|
:content => 'りんご', :header => 'みかん', :footer => 'バナナ', :sidebar => 'スイカ',
|
||||||
|
:page => 'Multibyte', :format => :markdown, :message => 'mesg'
|
||||||
|
|
||||||
|
get "edit/Multibyte"
|
||||||
|
|
||||||
|
assert last_response.ok?
|
||||||
|
assert_match /りんご/, last_response.body
|
||||||
|
assert_match /みかん/, last_response.body
|
||||||
|
assert_match /バナナ/, last_response.body
|
||||||
|
assert_match /スイカ/, last_response.body
|
||||||
|
end
|
||||||
|
|
||||||
def app
|
def app
|
||||||
Precious::App
|
Precious::App
|
||||||
end
|
end
|
||||||
@@ -559,6 +596,12 @@ context "Frontend with lotr" do
|
|||||||
assert_match /Bilbo Baggins/, last_response.body
|
assert_match /Bilbo Baggins/, last_response.body
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "streaming files to browser" do
|
||||||
|
get "/Data.csv"
|
||||||
|
assert last_response.ok?
|
||||||
|
assert last_response.headers.include? 'Content-Disposition'
|
||||||
|
end
|
||||||
|
|
||||||
# base path requires 'map' in a config.ru to work correctly.
|
# base path requires 'map' in a config.ru to work correctly.
|
||||||
test "create pages within sub-directories using base path" do
|
test "create pages within sub-directories using base path" do
|
||||||
Precious::App.set(:wiki_options, { :base_path => 'wiki' })
|
Precious::App.set(:wiki_options, { :base_path => 'wiki' })
|
||||||
@@ -587,13 +630,13 @@ context "Frontend with lotr" do
|
|||||||
test "create pages within sub-directories" do
|
test "create pages within sub-directories" do
|
||||||
post "/create", :content => 'big smelly creatures', :page => 'Orc',
|
post "/create", :content => 'big smelly creatures', :page => 'Orc',
|
||||||
:path => 'Mordor', :format => 'markdown', :message => 'oooh, scary'
|
:path => 'Mordor', :format => 'markdown', :message => 'oooh, scary'
|
||||||
assert_equal 'http://example.org/Mordor/orc', last_response.headers['Location']
|
assert_equal 'http://example.org/Mordor/Orc', last_response.headers['Location']
|
||||||
get "/Mordor/Orc"
|
get "/Mordor/Orc"
|
||||||
assert_match /big smelly creatures/, last_response.body
|
assert_match /big smelly creatures/, last_response.body
|
||||||
|
|
||||||
post "/create", :content => 'really big smelly creatures', :page => 'Uruk Hai',
|
post "/create", :content => 'really big smelly creatures', :page => 'Uruk Hai',
|
||||||
:path => 'Mordor', :format => 'markdown', :message => 'oooh, very scary'
|
:path => 'Mordor', :format => 'markdown', :message => 'oooh, very scary'
|
||||||
assert_equal 'http://example.org/Mordor/uruk-hai', last_response.headers['Location']
|
assert_equal 'http://example.org/Mordor/Uruk-Hai', last_response.headers['Location']
|
||||||
get "/Mordor/Uruk-Hai"
|
get "/Mordor/Uruk-Hai"
|
||||||
assert_match /really big smelly creatures/, last_response.body
|
assert_match /really big smelly creatures/, last_response.body
|
||||||
end
|
end
|
||||||
@@ -602,11 +645,11 @@ context "Frontend with lotr" do
|
|||||||
post "/create", :content => 'big smelly creatures', :page => 'Orc',
|
post "/create", :content => 'big smelly creatures', :page => 'Orc',
|
||||||
:path => 'Mordor', :format => 'markdown', :message => 'oooh, scary'
|
:path => 'Mordor', :format => 'markdown', :message => 'oooh, scary'
|
||||||
|
|
||||||
assert_equal 'http://example.org/Mordor/orc', last_response.headers['Location']
|
assert_equal 'http://example.org/Mordor/Orc', last_response.headers['Location']
|
||||||
|
|
||||||
post "/edit/Mordor/Orc", :content => 'not so big smelly creatures',
|
post "/edit/Mordor/Orc", :content => 'not so big smelly creatures',
|
||||||
:page => 'Orc', :path => 'Mordor', :message => 'minor edit'
|
:page => 'Orc', :path => 'Mordor', :message => 'minor edit'
|
||||||
assert_equal 'http://example.org/Mordor/orc', last_response.headers['Location']
|
assert_equal 'http://example.org/Mordor/Orc', last_response.headers['Location']
|
||||||
|
|
||||||
get "/Mordor/Orc"
|
get "/Mordor/Orc"
|
||||||
assert_match /not so big smelly creatures/, last_response.body
|
assert_match /not so big smelly creatures/, last_response.body
|
||||||
|
|||||||
@@ -9,5 +9,17 @@ context "Precious::Helpers" do
|
|||||||
assert_equal 'Mordor', extract_path('Mordor/Sauron')
|
assert_equal 'Mordor', extract_path('Mordor/Sauron')
|
||||||
assert_equal 'Mordor/Sauron', extract_path('Mordor/Sauron/Evil')
|
assert_equal 'Mordor/Sauron', extract_path('Mordor/Sauron/Evil')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "clean path without leading slash" do
|
||||||
|
assert_equal '/Mordor', clean_path('Mordor')
|
||||||
|
end
|
||||||
|
|
||||||
|
test "clean path with leading slash" do
|
||||||
|
assert_equal '/Mordor', clean_path('/Mordor')
|
||||||
|
end
|
||||||
|
|
||||||
|
test "clean path with double leading slash" do
|
||||||
|
assert_equal '/Mordor', clean_path('//Mordor')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ require File.expand_path '../../lib/gollum/views/page', __FILE__
|
|||||||
|
|
||||||
context "Precious::Views::Page" do
|
context "Precious::Views::Page" do
|
||||||
setup do
|
setup do
|
||||||
@path = testpath("examples/test.git")
|
examples = testpath "examples"
|
||||||
FileUtils.rm_rf(@path)
|
@path = File.join(examples, "test.git")
|
||||||
@repo = Grit::Repo.init_bare(@path)
|
FileUtils.cp_r File.join(examples, "empty.git"), @path, :remove_destination => true
|
||||||
@wiki = Gollum::Wiki.new(@path)
|
@wiki = Gollum::Wiki.new(@path)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -43,4 +43,4 @@ context "Precious::Views::Page" do
|
|||||||
actual = @view.title
|
actual = @view.title
|
||||||
assert_equal 'H1', title
|
assert_equal 'H1', title
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user