Compare commits

..

1 Commits

55 changed files with 252 additions and 1018 deletions
-22
View File
@@ -44,25 +44,3 @@ jobs:
bundler-cache: true
- name: Run tests
run: bundle exec rake
selenium:
name: Selenium on MRI
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v2
- name: Install Chromedriver
uses: nanasess/setup-chromedriver@v1
- run: |
export DISPLAY=:99
chromedriver --url-base=/wd/hub &
sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 & # optional
- name: List files in the repository
run: |
ls ${{ github.workspace }}
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.1'
bundler-cache: true
- name: Run tests
run: bundle exec rake test:capybara
+9 -94
View File
@@ -29,85 +29,14 @@ Lastly, please **consider helping out** by opening a Pull Request!
You can triage issues which may include reproducing bug reports or asking for vital information, such as version numbers or reproduction instructions. If you would like to start triaging issues, one easy way to get started is to [subscribe to gollum on CodeTriage](https://www.codetriage.com/gollum/gollum).
## Set up your development environment
If you want to hack on Gollum, you'll need to set up a development
environment.
To get started, you'll need:
- A recent version of [Git][git]
- A recent version of [Ruby][rubylang].
- A recent version of [Node JS][nodejs].
Refer to their installation instructions. Installation methods differ depending
on your operating system.
Once you have those:
- Install Bundler, the Ruby package manager. In a terminal:
```sh
gem install bundler
```
- Install Yarn, a JavaScript package manager. [See Yarn's install
guide][yarn-install].
Now, you can start setting up Gollum to run locally:
1. Clone the git repository. In a terminal:
```sh
git clone https://github.com/gollum/gollum.git
```
2. Change directory into the cloned project:
```sh
cd gollum
```
3. Bundle the project's Ruby dependencies using Bundler:
```sh
[sudo] bundle install
```
4. Install the project's JavaScript dependencies using Yarn:
```sh
yarn install
```
If all went well, you should now be able to run the test suite using the
following command:
```sh
bundle exec rake
```
If you already have a Gollum wiki, you can also browse it via your local version
of Gollum:
```sh
bundle exec gollum <path/to/my/wiki/root>
```
Or you can clone an example wiki and browse that:
```sh
git clone test/examples/lotr.git ~/lotr-wiki
bundle exec gollum ~/lotr-wiki
```
With this, you're ready to start contributing and open your first [pull
request](#opening-a-pull-request).
[git]: https://git-scm.com/downloads
[nodejs]: https://nodejs.org
[rubylang]: https://www.ruby-lang.org
[yarn-install]: https://yarnpkg.com/getting-started/install
## Opening a Pull Request
Pull Requests fixing bugs, implementing new features, or updating documentation and dependencies are all very welcome! If you would like to help out with the project, you can pick an open issue from the issue tracker. We're more than happy to help you get started! Here's how you can proceed:
1. Fork and clone Gollum. See [Set up your development
environment](#set-up-your-development-environment).
1. Fork and clone Gollum.
2. Create a thoughtfully named topic branch to contain your changes.
3. If you haven't installed dependencies yet, navigate to your clone and execute:
3. If you haven't installed dependencies yet, navigate to your clone and execute:
```
[sudo] bundle install
```
@@ -118,32 +47,23 @@ Pull Requests fixing bugs, implementing new features, or updating documentation
8. Push the branch to your fork on GitHub.
9. Create a pull request for Gollum.
Do not change Gollum's version number, we will do that on our own.
**Notes:**
* Do not change Gollum's version numbers, we will do that on our own.
### Running tests
1. Install [Bundler](http://bundler.io/).
2. Navigate to the cloned source of Gollum.
3. Install dependencies:
3. Install dependencies:
```
[sudo] bundle install
```
4. Run the tests:
4. Run the tests:
```
bundle exec rake test
```
To profile slow tests, you can use the `--verbose` flag:
```sh
bundle exec rake test TESTOPTS="--verbose"
```
You can also run a single test file with the following command:
```sh
bundle exec ruby <test/test_the_file_i_want_to_run.rb>
```
To profile slow tests, you can use `bundle exec rake test TESTOPTS="--verbose"`.
### Working with test repositories
@@ -162,12 +82,7 @@ git push ../lotr.git/ master
## Updating static assets
This is necessary whenever changes have been made to the assets in
`lib/gollum/public/gollum/javascript` (mostly SASS, CSS, and JS files), to
ensure the changes are also present in the [released](#releasing-the-gem)
version of the gem.
Steps:
This is necessary whenever changes have been made to the assets in `lib/gollum/public/gollum/javascript` (mostly SASS, CSS, and JS files), to ensure the changes are also present in the [released](#releasing-the-gem) version of the gem. Steps:
1. `git rm -r lib/gollum/public/assets`
1. `bundle exec rake precompile`
-5
View File
@@ -18,11 +18,6 @@ gem 'warbler', platforms: :jruby
# current CI workflows pass, we should only try to install this version of
# Nokogiri for newer Ruby versions.
group :test do
gem 'selenium-webdriver', require: false
gem 'capybara', require: false
end
gemspec
gem 'rake', '~> 13.0'
+4 -1
View File
@@ -136,7 +136,6 @@ Gollum comes with the following command line options:
| --template-dir | [PATH] | Specify custom mustache template directory. Only overrides templates that exist in this directory. |
| --template-page | none | Use _Template in root as a template for new pages. Must be committed. |
| --emoji | none | Parse and interpret emoji tags (e.g. `:heart:`) except when the leading colon is backslashed (e.g. `\:heart:`). |
| --default-keybind | none | Set the default keybinding for the editor. Can be set to `vim`, or `emacs`. |
| --lenient-tag-lookup | none | Internal links resolve case-insensitively, will treat spaces as hyphens, and will match the first page found with a certain filename, anywhere in the repository. Provides compatibility with Gollum 4.x. |
| --help | none | Display the list of options on the command line. |
| --version | none | Display the current version of Gollum. |
@@ -158,3 +157,7 @@ When `--config` option is used, certain inner parts of Gollum can be customized.
## CONTRIBUTING
Please consider helping out! See [CONTRIBUTING](CONTRIBUTING.md) for information on how to submit issues, and how to start hacking on gollum.
## THANKS TO
[![Testing Powered By SauceLabs](https://opensource.saucelabs.com/images/opensauce/powered-by-saucelabs-badge-gray.png?sanitize=true "Testing Powered By SauceLabs")](https://saucelabs.com)
+7 -38
View File
@@ -72,21 +72,9 @@ end
task :default => :test
require 'rake/testtask'
namespace :test do
Rake::TestTask.new('capybara') do |test|
test.libs << 'lib' << 'test' << '.'
test.pattern = 'test/integration/**/test_*.rb'
test.verbose = true
test.warning = false
end
end
Rake::TestTask.new(:test) do |test|
test.libs << 'lib' << 'test' << '.'
test.test_files = FileList.new('test/**/test_*.rb') do |fl|
fl.exclude('test/integration/**/test_*.rb')
end
test.pattern = 'test/**/test_*.rb'
test.verbose = true
test.warning = false
end
@@ -202,10 +190,10 @@ task :changelog do
exit!
end
end
latest_changes = File.open(latest_changes_file)
version_pattern = "# #{version}"
if !`grep "#{version_pattern}" #{history_file}`.empty?
puts "#{version} is already described in #{history_file}"
exit!
@@ -220,13 +208,13 @@ task :changelog do
puts "#{latest_changes_file} is empty!"
exit!
end
body = latest_changes.read
body.scan(/\s*#\s+\d\.\d.*/) do |match|
puts "#{latest_changes_file} may not contain multiple markdown headers!"
exit!
end
temp = Tempfile.new
temp.puts("#{version_pattern} / #{date}\n#{body}\n")
temp.close
@@ -236,29 +224,11 @@ end
desc 'Precompile assets'
task :precompile do
# Attempt to install JavaScript dependencies managed by Yarn via the
# `package.json` file in Gollum's project root. If it fails, raise an error
# and exit the task early.
puts "\n Installing `yarn`-managed JavaScript dependencies... \n\n"
system "yarn install"
unless $?.success?
raise "This task tried to run `yarn install` to get up-to-date " \
"JavaScript dependencies before precompilation. But it failed. Please " \
"run `yarn install` manually from your shell and resolve any issues. " \
"It's possible that you just need to install `yarn` on your system."
end
require './lib/gollum/app.rb'
# Next, configure the Sprockets asset pipeline and precompile production-
# ready assets.
Precious::App.set(:environment, :production)
env = Precious::Assets.sprockets
path = ENV.fetch 'GOLLUM_ASSETS_PATH',
File.join(File.dirname(__FILE__), 'lib/gollum/public/assets')
path = ENV.fetch('GOLLUM_ASSETS_PATH', ::File.join(File.dirname(__FILE__), 'lib/gollum/public/assets'))
manifest = Sprockets::Manifest.new(env, path)
Sprockets::Helpers.configure do |config|
config.environment = env
config.prefix = Precious::Assets::ASSET_URL
@@ -266,7 +236,6 @@ task :precompile do
config.public_path = path
config.manifest = manifest
end
puts "\n Precompiling assets to #{path}... \n\n"
puts "Precompiling assets to #{path}..."
manifest.compile(Precious::Assets::MANIFEST)
end
+1 -5
View File
@@ -15,7 +15,7 @@ options = {
}
wiki_options = {
:allow_uploads => false,
:allow_editing => true
:allow_editing => true,
}
opts = OptionParser.new do |opts|
@@ -166,10 +166,6 @@ MSG
opts.on('--emoji', 'Parse and interpret emoji tags (e.g. :heart:) except when the leading colon is backslashed (e.g. \\:heart:).') do
wiki_options[:emoji] = true
end
opts.on('--default-keybinding [KEYBINDING]', Gollum::KEYBINDINGS.drop(1), 'Set the default keybinding for the editor. Can be set to \'vim\', or \'emacs\'.') do |keybinding|
wiki_options[:default_keybinding] = keybinding.to_s
puts wiki_options[:default_keybinding]
end
opts.separator ''
opts.separator ' Common:'
+1
View File
@@ -1212,6 +1212,7 @@ Gem::Specification.new do |s|
lib/gollum/templates/pagination.mustache
lib/gollum/templates/search.mustache
lib/gollum/templates/searchbar.mustache
lib/gollum/templates/user.mustache
lib/gollum/templates/wiki_content.mustache
lib/gollum/uri_encode_component.rb
lib/gollum/views/commit.rb
+1 -2
View File
@@ -14,8 +14,7 @@ require ::File.expand_path('../gollum/uri_encode_component', __FILE__)
module Gollum
VERSION = '5.3.0'
KEYBINDINGS = ['default', 'vim', 'emacs']
::I18n.available_locales = [:en]
::I18n.load_path = Dir[::File.expand_path("lib/gollum/locales") + "/*.yml"]
+23 -5
View File
@@ -110,7 +110,6 @@ module Precious
@show_local_time = settings.wiki_options.fetch(:show_local_time, false)
@wiki_title = settings.wiki_options.fetch(:title, 'Gollum Wiki')
@default_keybinding = settings.wiki_options.fetch(:default_keybinding, 'default')
if settings.wiki_options[:template_dir]
Precious::Views::Layout.extend Precious::Views::TemplateCascade
@@ -212,7 +211,7 @@ module Precious
end
get '/edit/*' do
forbid unless @allow_editing
forbid unless @allow_editing && @user_authed
wikip = wiki_page(params[:splat].first)
@name = wikip.fullname
@path = wikip.path
@@ -235,15 +234,29 @@ module Precious
wiki = wiki_new
halt 405 unless wiki.allow_uploads
forbid unless @user_authed
if params[:file]
fullname = params[:file][:filename]
tempfile = params[:file][:tempfile]
end
halt 500 unless tempfile.is_a? Tempfile
dir = wiki.per_page_uploads ? find_per_page_upload_subdir(request.referer, request.host_with_port, wiki.base_path) : 'uploads'
if wiki.per_page_uploads
dir = request.referer.match(/^https?:\/\/#{request.host_with_port}\/(.*)/)[1]
# remove base path if it is set
dir.sub!(/^#{wiki.base_path}/, '') if wiki.base_path
# remove base_url and gollum/* subpath if necessary
dir.sub!(/^\/gollum\/[-\w]+\//, '')
# remove file extension
dir.sub!(/#{::File.extname(dir)}$/, '')
# revert escaped whitespaces
dir.gsub!(/%20/, ' ')
dir = ::File.join('uploads', dir)
else
# store all uploads together
dir = 'uploads'
end
halt 500 if dir.include?('..')
halt 500 unless Pathname(dir).relative?
@@ -278,6 +291,7 @@ module Precious
post '/rename/*' do
wikip = wiki_page(params[:splat].first)
halt 500 if wikip.nil?
forbid unless @user_authed
wiki = wikip.wiki
page = wikip.page
rename = params[:rename]
@@ -321,6 +335,7 @@ module Precious
path = "/#{clean_url(sanitize_empty_params(params[:path]))}"
wiki = wiki_new
page = wiki.page(::File.join(path, params[:page]))
forbid unless @user_authed
return if page.nil?
if etag != page.sha
@@ -341,6 +356,7 @@ module Precious
post '/delete/*' do
forbid unless @allow_editing
forbid unless @user_authed
wiki = wiki_new
filepath = params[:splat].first
unless filepath.nil?
@@ -352,6 +368,7 @@ module Precious
get '/create/*' do
forbid unless @allow_editing
forbid unless @user_authed
wikip = wiki_page(params[:splat].first)
@name = wikip.name
@ext = wikip.ext
@@ -377,6 +394,7 @@ module Precious
path = sanitize_empty_params(params[:path]) || ''
format = params[:format].intern
wiki = wiki_new
forbid unless @user_authed
path.gsub!(/^\//, '')
@@ -627,7 +645,7 @@ module Precious
elsif @redirects_enabled && redirect_path = wiki.redirects[fullpath]
redirect to("#{encodeURIComponent(redirect_path)}?redirected_from=#{encodeURIComponent(fullpath)}")
else
if @allow_editing
if @allow_editing && @user_authed
path = fullpath[-1] == '/' ? "#{fullpath}#{wiki.index_page}" : fullpath # Append default index page if no page name is supplied
redirect to("/gollum/create/#{clean_url(encodeURIComponent(path))}")
else
+1 -5
View File
@@ -7,12 +7,8 @@ module Precious
def self.sprockets(dir = File.dirname(File.expand_path(__FILE__)))
env = Sprockets::Environment.new
env.append_path ::File.join(dir, '../../node_modules')
env.append_path ::File.join(dir, 'public/gollum/javascript')
env.append_path ::File.join(dir, 'public/gollum/stylesheets/')
env.append_path ::File.join(dir, 'public/gollum/javascript')
env.append_path ::File.join(dir, 'public/gollum/images')
env.append_path ::File.join(dir, 'public/gollum/fonts')
-18
View File
@@ -5,20 +5,6 @@ module Precious
module Helpers
EMOJI_PATHNAME = Pathname.new(Gemojione.images_path).freeze
def find_per_page_upload_subdir(referer, host_with_port, base_path)
base = base_path ? remove_leading_and_trailing_slashes(base_path) : ''
dir = referer.match(/^https?:\/\/#{host_with_port}\/#{base}\/?(.*)/)[1]
# remove gollum/* subpath if necessary
dir.sub!(/^gollum\/[-\w]+\//, '')
# remove file extension
dir.sub!(/#{::File.extname(dir)}$/, '')
# revert escaped whitespaces
dir.gsub!(/%20/, ' ')
return ::File.join('uploads', dir)
end
def sanitize_empty_params(param)
[nil, ''].include?(param) ? nil : CGI.unescape(param)
@@ -28,10 +14,6 @@ module Precious
# Check if name already has a format extension, and if so, strip it.
Gollum::Page.valid_extension?(name) ? Gollum::Page.strip_filename(name) : name
end
def remove_leading_and_trailing_slashes(str)
str.sub(%r{^(/+)}, '').sub(%r{/+$}, '')
end
# Remove all slashes from the start of string.
# Remove all double slashes
-38
View File
@@ -1,38 +0,0 @@
en:
pagination:
aria:
label: Pagination
next_page: Next page
previous_page: Previous page
next: Next
previous: Previous
precious/views/compare:
back_to_page_history: Back to Page History
back_to_top: Back to Top
comparison_of: Comparison of
comparing_versions_of: Comparing versions of
comparing_from: "Comparing %{before} to %{after}"
revert: Revert Changes
precious/views/error:
error: Error
precious/views/history:
browse_in_history_description: Browse the page at this point in the history
compare_revisions: Compare Revisions
history_for: History for
precious/views/latest_changes:
title: Latest Changes (Globally)
precious/views/layout:
title: Home
precious/views/overview:
back_to_top: Back to Top
delete_confirmation: "Are you sure you want to delete %{name}?"
no_pages_in: There are no pages in
on: "on"
title: "Overview of %{ref}"
precious/views/search:
aria:
show_all: Show all hits on this page
back_to_top: Back to Top
no_results: There are no results for your search
search_results_for: Search results for
title: "Search results for %{query}"
@@ -0,0 +1 @@
{"files":{"app-f05401ee374f0c7f48fc2bc08e30b4f4db705861fd5895ed70998683b383bfb5.js":{"logical_path":"app.js","mtime":"2021-11-15T20:08:30-08:00","size":136040,"digest":"f05401ee374f0c7f48fc2bc08e30b4f4db705861fd5895ed70998683b383bfb5","integrity":"sha256-8FQB7jdPDH9I/CvAjjC09NtwWGH9WJXtcJmGg7ODv7U="},"editor-9881d0c7ae663293f0e3a7e72729eec7e940fa613185c076709b76d292f5703a.js":{"logical_path":"editor.js","mtime":"2021-11-15T20:08:30-08:00","size":744886,"digest":"9881d0c7ae663293f0e3a7e72729eec7e940fa613185c076709b76d292f5703a","integrity":"sha256-mIHQx65mMpPw46fnJynux+lA+mExhcB2cJt20pL1cDo="},"app-309be032396e783b13a47df58f389b7c8e11c2b2d42640560b874f677c25f6e5.css":{"logical_path":"app.css","mtime":"2021-11-15T20:08:30-08:00","size":396731,"digest":"309be032396e783b13a47df58f389b7c8e11c2b2d42640560b874f677c25f6e5","integrity":"sha256-MJvgMjlueDsTpH31jzibfI4RwrLUJkBWC4dPZ3wl9uU="},"criticmarkup-31ae5d3282bbb8e7b7c3c9917e9fb68e3315a6b4a75da6cec48d21b8846905c4.css":{"logical_path":"criticmarkup.css","mtime":"2021-08-22T15:24:51-07:00","size":646,"digest":"31ae5d3282bbb8e7b7c3c9917e9fb68e3315a6b4a75da6cec48d21b8846905c4","integrity":"sha256-Ma5dMoK7uOe3w8mRfp+2jjMVprSnXabOxI0huIRpBcQ="},"print-512498c368be0d3fb1ba105dfa84289ae48380ec9fcbef948bd4e23b0b095bfb.css":{"logical_path":"print.css","mtime":"2021-08-22T15:24:51-07:00","size":75,"digest":"512498c368be0d3fb1ba105dfa84289ae48380ec9fcbef948bd4e23b0b095bfb","integrity":"sha256-USSYw2i+DT+xuhBd+oQomuSDgOyfy++Ui9TiOwsJW/s="}},"assets":{"app.js":"app-f05401ee374f0c7f48fc2bc08e30b4f4db705861fd5895ed70998683b383bfb5.js","editor.js":"editor-9881d0c7ae663293f0e3a7e72729eec7e940fa613185c076709b76d292f5703a.js","app.css":"app-309be032396e783b13a47df58f389b7c8e11c2b2d42640560b874f677c25f6e5.css","criticmarkup.css":"criticmarkup-31ae5d3282bbb8e7b7c3c9917e9fb68e3315a6b4a75da6cec48d21b8846905c4.css","print.css":"print-512498c368be0d3fb1ba105dfa84289ae48380ec9fcbef948bd4e23b0b095bfb.css"}}
@@ -1 +0,0 @@
{"files":{"app-6bef6b19014c6620eab4daf7bc581f9d43fff778bdc78aa9fe53aaa2c927c8cc.js":{"logical_path":"app.js","mtime":"2022-06-03T19:51:43-07:00","size":188397,"digest":"6bef6b19014c6620eab4daf7bc581f9d43fff778bdc78aa9fe53aaa2c927c8cc","integrity":"sha256-a+9rGQFMZiDqtNr3vFgfnUP/93i9x4qp/lOqosknyMw="},"editor-ebdf534a0063fe3b05a7e7daaf7aa40b78fab9863142b161d88f32a3f035378a.js":{"logical_path":"editor.js","mtime":"2022-06-03T19:51:43-07:00","size":745160,"digest":"ebdf534a0063fe3b05a7e7daaf7aa40b78fab9863142b161d88f32a3f035378a","integrity":"sha256-699TSgBj/jsFp+far3qkC3j6uYYxQrFh2I8yo/A1N4o="},"app-309be032396e783b13a47df58f389b7c8e11c2b2d42640560b874f677c25f6e5.css":{"logical_path":"app.css","mtime":"2022-06-03T19:51:43-07:00","size":396731,"digest":"309be032396e783b13a47df58f389b7c8e11c2b2d42640560b874f677c25f6e5","integrity":"sha256-MJvgMjlueDsTpH31jzibfI4RwrLUJkBWC4dPZ3wl9uU="},"criticmarkup-31ae5d3282bbb8e7b7c3c9917e9fb68e3315a6b4a75da6cec48d21b8846905c4.css":{"logical_path":"criticmarkup.css","mtime":"2021-02-24T23:16:14-08:00","size":646,"digest":"31ae5d3282bbb8e7b7c3c9917e9fb68e3315a6b4a75da6cec48d21b8846905c4","integrity":"sha256-Ma5dMoK7uOe3w8mRfp+2jjMVprSnXabOxI0huIRpBcQ="},"print-512498c368be0d3fb1ba105dfa84289ae48380ec9fcbef948bd4e23b0b095bfb.css":{"logical_path":"print.css","mtime":"2021-02-24T23:16:14-08:00","size":75,"digest":"512498c368be0d3fb1ba105dfa84289ae48380ec9fcbef948bd4e23b0b095bfb","integrity":"sha256-USSYw2i+DT+xuhBd+oQomuSDgOyfy++Ui9TiOwsJW/s="}},"assets":{"app.js":"app-6bef6b19014c6620eab4daf7bc581f9d43fff778bdc78aa9fe53aaa2c927c8cc.js","editor.js":"editor-ebdf534a0063fe3b05a7e7daaf7aa40b78fab9863142b161d88f32a3f035378a.js","app.css":"app-309be032396e783b13a47df58f389b7c8e11c2b2d42640560b874f677c25f6e5.css","criticmarkup.css":"criticmarkup-31ae5d3282bbb8e7b7c3c9917e9fb68e3315a6b4a75da6cec48d21b8846905c4.css","print.css":"print-512498c368be0d3fb1ba105dfa84289ae48380ec9fcbef948bd4e23b0b095bfb.css"}}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -4
View File
@@ -1,10 +1,7 @@
// Require vendor assets from installed Node modules.
//= require mousetrap/mousetrap.min
// Require application assets.
//= require jquery-1.7.2.min
//= require identicon
//= require date.min
//= require mousetrap.min
//= require clipboard.min
//= require gollum
//= require gollum.dialog
@@ -55,20 +55,6 @@
}, 2000);
}
function setEditorKeyboardHandler(mode) {
var editor = window.ace_editor;
var storage = window.localStorage;
storage.setItem('gollum-kbm', mode)
if (mode == "default") {
editor.setKeyboardHandler();
} else if (mode == "vim" || mode == "emacs") {
editor.setKeyboardHandler("ace/keyboard/" + mode);
} else {
editor.setKeyboardHandler();
}
editor.focus();
}
/**
* $.GollumEditor
*
@@ -111,22 +97,9 @@
});
$('#gollum-autorecover-msg')[0].hidden = false;
}
// Check for user last keybind and ensure ui is correct
var storage = window.localStorage;
var userDefaultKeybind = storage.getItem('gollum-kbm');
if (userDefaultKeybind) {
default_keybinding = userDefaultKeybind;
}
var keybinding = document.getElementById('keybinding')
for (var i = 0; i < keybinding.options.length; ++i) {
if (keybinding.options[i].text === default_keybinding) {
keybinding.options[i].selected = true;
}
}
editor.setTheme("ace/theme/tomorrow");
setEditorKeyboardHandler(default_keybinding);
editor.setKeyboardHandler();
editor.renderer.setShowGutter(false);
editor.getSession().setUseWrapMode(true);
editor.getSession().setValue(textarea.val());
@@ -178,7 +151,15 @@
$("#keybinding").change(function() {
var mode = $(this).val();
setEditorKeyboardHandler(mode)
var editor = window.ace_editor;
if (mode == "default") {
editor.setKeyboardHandler();
} else if (mode == "vim" || mode == "emacs") {
editor.setKeyboardHandler("ace/keyboard/" + mode);
} else {
editor.setKeyboardHandler();
}
editor.focus();
});
// Remove any autosaved text when we hit save or cancel
+11
View File
@@ -0,0 +1,11 @@
/* mousetrap v1.6.1 craig.is/killing/mice */
(function(r,v,f){function w(a,b,g){a.addEventListener?a.addEventListener(b,g,!1):a.attachEvent("on"+b,g)}function A(a){if("keypress"==a.type){var b=String.fromCharCode(a.which);a.shiftKey||(b=b.toLowerCase());return b}return p[a.which]?p[a.which]:t[a.which]?t[a.which]:String.fromCharCode(a.which).toLowerCase()}function F(a){var b=[];a.shiftKey&&b.push("shift");a.altKey&&b.push("alt");a.ctrlKey&&b.push("ctrl");a.metaKey&&b.push("meta");return b}function x(a){return"shift"==a||"ctrl"==a||"alt"==a||
"meta"==a}function B(a,b){var g,c,d,f=[];g=a;"+"===g?g=["+"]:(g=g.replace(/\+{2}/g,"+plus"),g=g.split("+"));for(d=0;d<g.length;++d)c=g[d],C[c]&&(c=C[c]),b&&"keypress"!=b&&D[c]&&(c=D[c],f.push("shift")),x(c)&&f.push(c);g=c;d=b;if(!d){if(!n){n={};for(var q in p)95<q&&112>q||p.hasOwnProperty(q)&&(n[p[q]]=q)}d=n[g]?"keydown":"keypress"}"keypress"==d&&f.length&&(d="keydown");return{key:c,modifiers:f,action:d}}function E(a,b){return null===a||a===v?!1:a===b?!0:E(a.parentNode,b)}function c(a){function b(a){a=
a||{};var b=!1,l;for(l in n)a[l]?b=!0:n[l]=0;b||(y=!1)}function g(a,b,u,e,c,g){var l,m,k=[],f=u.type;if(!h._callbacks[a])return[];"keyup"==f&&x(a)&&(b=[a]);for(l=0;l<h._callbacks[a].length;++l)if(m=h._callbacks[a][l],(e||!m.seq||n[m.seq]==m.level)&&f==m.action){var d;(d="keypress"==f&&!u.metaKey&&!u.ctrlKey)||(d=m.modifiers,d=b.sort().join(",")===d.sort().join(","));d&&(d=e&&m.seq==e&&m.level==g,(!e&&m.combo==c||d)&&h._callbacks[a].splice(l,1),k.push(m))}return k}function f(a,b,c,e){h.stopCallback(b,
b.target||b.srcElement,c,e)||!1!==a(b,c)||(b.preventDefault?b.preventDefault():b.returnValue=!1,b.stopPropagation?b.stopPropagation():b.cancelBubble=!0)}function d(a){"number"!==typeof a.which&&(a.which=a.keyCode);var b=A(a);b&&("keyup"==a.type&&z===b?z=!1:h.handleKey(b,F(a),a))}function p(a,c,u,e){function l(c){return function(){y=c;++n[a];clearTimeout(r);r=setTimeout(b,1E3)}}function g(c){f(u,c,a);"keyup"!==e&&(z=A(c));setTimeout(b,10)}for(var d=n[a]=0;d<c.length;++d){var m=d+1===c.length?g:l(e||
B(c[d+1]).action);q(c[d],m,e,a,d)}}function q(a,b,c,e,d){h._directMap[a+":"+c]=b;a=a.replace(/\s+/g," ");var f=a.split(" ");1<f.length?p(a,f,b,c):(c=B(a,c),h._callbacks[c.key]=h._callbacks[c.key]||[],g(c.key,c.modifiers,{type:c.action},e,a,d),h._callbacks[c.key][e?"unshift":"push"]({callback:b,modifiers:c.modifiers,action:c.action,seq:e,level:d,combo:a}))}var h=this;a=a||v;if(!(h instanceof c))return new c(a);h.target=a;h._callbacks={};h._directMap={};var n={},r,z=!1,t=!1,y=!1;h._handleKey=function(a,
c,d){var e=g(a,c,d),k;c={};var h=0,l=!1;for(k=0;k<e.length;++k)e[k].seq&&(h=Math.max(h,e[k].level));for(k=0;k<e.length;++k)e[k].seq?e[k].level==h&&(l=!0,c[e[k].seq]=1,f(e[k].callback,d,e[k].combo,e[k].seq)):l||f(e[k].callback,d,e[k].combo);e="keypress"==d.type&&t;d.type!=y||x(a)||e||b(c);t=l&&"keydown"==d.type};h._bindMultiple=function(a,b,c){for(var d=0;d<a.length;++d)q(a[d],b,c)};w(a,"keypress",d);w(a,"keydown",d);w(a,"keyup",d)}if(r){var p={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",
18:"alt",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"ins",46:"del",91:"meta",93:"meta",224:"meta"},t={106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},D={"~":"`","!":"1","@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\"},C={option:"alt",command:"meta","return":"enter",
escape:"esc",plus:"+",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},n;for(f=1;20>f;++f)p[111+f]="f"+f;for(f=0;9>=f;++f)p[f+96]=f.toString();c.prototype.bind=function(a,b,c){a=a instanceof Array?a:[a];this._bindMultiple.call(this,a,b,c);return this};c.prototype.unbind=function(a,b){return this.bind.call(this,a,function(){},b)};c.prototype.trigger=function(a,b){if(this._directMap[a+":"+b])this._directMap[a+":"+b]({},a);return this};c.prototype.reset=function(){this._callbacks={};
this._directMap={};return this};c.prototype.stopCallback=function(a,b){return-1<(" "+b.className+" ").indexOf(" mousetrap ")||E(b,this.target)?!1:"INPUT"==b.tagName||"SELECT"==b.tagName||"TEXTAREA"==b.tagName||b.isContentEditable};c.prototype.handleKey=function(){return this._handleKey.apply(this,arguments)};c.addKeycodes=function(a){for(var b in a)a.hasOwnProperty(b)&&(p[b]=a[b]);n=null};c.init=function(){var a=c(v),b;for(b in a)"_"!==b.charAt(0)&&(c[b]=function(b){return function(){return a[b].apply(a,
arguments)}}(b))};c.init();r.Mousetrap=c;"undefined"!==typeof module&&module.exports&&(module.exports=c);"function"===typeof define&&define.amd&&define(function(){return c})}})("undefined"!==typeof window?window:null,"undefined"!==typeof window?document:null);
@@ -734,3 +734,12 @@ nav.actions {
display: none;
}
}
/* @section user */
#user p {
text-align: right;
padding-right:0.5em;
font-size: .8em;
line-height: 2.0em;
color: #999;
}
+40 -69
View File
@@ -4,84 +4,55 @@
<h1 class="header-title text-center text-md-left py-4">
<span class="f1-light text-gray-light">
{{t.comparing_versions_of}}
Comparing versions of
</span>
{{name}}
</h1>
</div>
{{#message}}
<p>{{message}}</p>
<p>{{message}}</p>
{{/message}}
<div id="compare-content">
<div id="compare-content">
<div class="py-4" id="actions">
{{#show_revert}}
{{#allow_editing}}
<form name="gollum-revert" action="{{revert_path}}/{{escaped_url_path}}/{{before}}/{{after}}" method="post" id="gollum-revert-form"></form>
<span class="pb-4">
<button
class="btn btn-sm"
onclick="$('#gollum-revert-form').submit()"
type="submit"
>
{{t.revert}}
</button>
</span>
{{/allow_editing}}
{{/show_revert}}
<div class="py-4" id="actions">
{{#show_revert}}
{{#allow_editing}}
<form name="gollum-revert" action="{{revert_path}}/{{escaped_url_path}}/{{before}}/{{after}}" method="post" id="gollum-revert-form"></form>
<span class="pb-4">
<button class="btn btn-sm" type="submit" onclick="$('#gollum-revert-form').submit()">Revert Changes</button>
</span>
{{/allow_editing}}
{{/show_revert}}
<a href="{{history_path}}/{{escaped_url_path}}" class="btn btn-sm action-page-history">Back to Page History</a>
</div>
<a
class="btn btn-sm action-page-history"
href="{{history_path}}/{{escaped_url_path}}"
>
{{t.back_to_page_history}}
</a>
</div>
<div class="Box data highlight">
<div class="Box-header Box--condensed Box-header--gray">{{path}} <span class="px-2 float-right">Comparing {{before}} to {{after}}</span></div>
<table >
{{#lines}}
<tr>
<td class="line_numbers">{{ldln}}</td>
<td class="line_numbers">{{rdln}}</td>
<td>
<div class="{{class}} pl-2">{{line}}</div>
</td>
</tr>
{{/lines}}
</table>
</div>
</div>
<div class="Box data highlight">
<div class="Box-header Box--condensed Box-header--gray">
{{path}}
<span class="px-2 float-right">
{{t.comparing_from}}
</span>
</div>
<table>
{{#lines}}
<tr>
<td class="line_numbers">{{ldln}}</td>
<td class="line_numbers">{{rdln}}</td>
<td>
<div class="{{class}} pl-2">{{line}}</div>
</td>
</tr>
{{/lines}}
</table>
</div>
</div>
<div class="pt-4" id="footer">
{{#show_revert}}
{{#allow_editing}}
<span class="pt-4">
<button
class="btn btn-sm gollum-revert-button"
onclick="$('#gollum-revert-form').submit()"
type="submit"
>
{{t.revert}}
</button>
</span>
{{/allow_editing}}
{{/show_revert}}
<div class="pt-4">
<a href="#">
{{t.back_to_top}}
</a>
</div>
</div>
<div class="pt-4" id="footer">
{{#show_revert}}
{{#allow_editing}}
<span class="pt-4"><button class="btn btn-sm gollum-revert-button" type="submit" onclick="$('#gollum-revert-form').submit()">Revert Changes</button></span>
{{/allow_editing}}
{{/show_revert}}
<div class="pt-4">
<a href="#">Back to Top</a>
</div>
</div>
</div>
+3 -3
View File
@@ -51,9 +51,9 @@
<div id="gollum-editor-format-selector">
<label for="format">Keybinding</label>
<select id="keybinding" name="keybinding" class="form-select input-sm">
{{#keybindings}}
<option {{#selected}}selected="selected" {{/selected}}value="{{name}}">{{name}}</option>
{{/keybindings}}
<option selected="selected">default</option>
<option>vim</option>
<option>emacs</option>
</select>
<label for="format">Markup</label>
<select id="wiki_format" name="format" class="form-select input-sm">
+2 -2
View File
@@ -1,8 +1,8 @@
<div id="wiki-wrapper" class="error">
<div id="error">
<h1>{{t.error}}</h1>
<h1>Error</h1>
<p>
{{message}}
</p>
</div>
</div>
</div>
+27 -36
View File
@@ -4,7 +4,7 @@
<h1 class="header-title text-center text-md-left py-4">
<span class="f1-light text-gray-light">
{{t.history_for}}
History for
</span>
{{name}}
</h1>
@@ -14,41 +14,32 @@
{{>pagination}}
<form name="selection-form" id="selection-form" method="get" action="{{compare_path}}/{{escaped_url_path}}"></form>
<form name="selection-form" id="selection-form" method="get" action="{{compare_path}}/{{escaped_url_path}}"></form>
<div id="page-history-list" class="Box Box--condensed flex-auto">
<form id="version-form">
<ul>
{{#versions}}
<li class="Box-row border-top Box-row--hover-gray d-flex flex-items-center">
<span class="pr-2"><input class="checkbox" type="checkbox" name="versions[]" value="{{id}}"></span>
<span class="float-left col-2" id="user-icons">{{>author_template}}</span>
<time class="flex-auto col-1 text-gray-light" datetime="{{datetime}}" data-format="{{date_format}}">{{date}}</time>
<span class="flex-auto col-5">{{message}}</span>
<span class="pl-4 float-right">
<a href="{{href}}" class="btn btn-outline text-mono">{{id7}}</a>
<a
class="btn btn-outline"
href="{{href_page}}"
title="{{t.browse_in_history_description}}"
>
{{#octicon}}code{{/octicon}}
</a>
</span>
</li>
{{/versions}}
</ul>
</form>
</div>
<div id="page-history-list" class="Box Box--condensed flex-auto">
<form id="version-form">
<ul>
{{#versions}}
<li class="Box-row border-top Box-row--hover-gray d-flex flex-items-center">
<span class="pr-2"><input class="checkbox" type="checkbox" name="versions[]" value="{{id}}"></span>
<span class="float-left col-2" id="user-icons">{{>author_template}}</span>
<time class="flex-auto col-1 text-gray-light" datetime="{{datetime}}" data-format="{{date_format}}">{{date}}</time>
<span class="flex-auto col-5">{{message}}</span>
<span class="pl-4 float-right">
<a href="{{href}}" class="btn btn-outline text-mono">{{id7}}</a>
<a href="{{href_page}}" title="Browse the page at this point in the history" class="btn btn-outline">{{#octicon}}code{{/octicon}}</a>
</span>
</li>
{{/versions}}
</ul>
</form>
</div>
<div id="footer">
<div class="pt-4">
<button
class="btn btn-sm action-compare-revision"
type="submit"
>
{{t.compare_revisions}}
</button>
</div>
</div>
<div id="footer">
<div class="pt-4">
<button class="btn btn-sm action-compare-revision" type="submit">Compare Revisions</button>
</div>
</div>
</div>
+1 -3
View File
@@ -31,9 +31,6 @@
{{#is_create_page}}
var default_markup = '{{default_markup}}';
{{/is_create_page}}
{{#has_editor}}
var default_keybinding = '{{default_keybinding}}';
{{/has_editor}}
</script>
{{#sprockets_javascript_tag}}app{{/sprockets_javascript_tag}}
@@ -63,6 +60,7 @@
<body>
<div class="container-lg clearfix">
{{{yield}}}
{{< user}}
</div>
</body>
</html>
+6 -19
View File
@@ -21,38 +21,25 @@
<span class="pr-2">{{{icon}}}</span>
<span><a href="{{url}}">{{name}}</a></span>
{{#allow_editing}}
{{#is_file}}
<button
class="btn btn-sm float-right delete-file"
data-confirm="{{t.delete_confirmation}}"
data-file-path="{{file_path}}"
>
{{#octicon}}trash{{/octicon}}
</button>
{{/is_file}}
{{/allow_editing}}
{{#is_file}}<button class="btn btn-sm float-right delete-file" data-file-path="{{file_path}}" data-confirm="Are you sure you want to delete {{name}}?">{{#octicon}}trash{{/octicon}}</button>{{/is_file}}
{{/allow_editing}}
</li>
{{/files_folders}}
</ul>
</div>
{{/has_results}}
{{#no_results}}
<p id="no-results">
{{t.no_pages_in}}
<strong>{{current_path}}</strong>
{{t.on}}
<strong>{{ref}}</strong>.
There are no pages in <strong>{{current_path}}</strong> on <strong>{{ref}}</strong>.
</p>
{{/no_results}}
</div>
<div class="pt-4" id="footer">
<a href="#">
{{t.back_to_top}}
</a>
<a href="#">Back to Top</a>
</div>
</div>
+4 -21
View File
@@ -1,23 +1,6 @@
<nav class="paginate-container" aria-label="{{tt.pagination.aria.label}}">
<nav class="paginate-container" aria-label="Pagination">
<div class="pagination" id="pagination">
<a
aria-label="{{td.pagination.aria.previous_page}}"
class="previous_page {{^previous_page}}disabled{{/previous_page}}"
href="?page_num={{previous_page}}{{query_string}}"
id="prev"
rel="prev"
>
{{tt.pagination.previous}}
</a>
<a
aria-label="{{td.pagination.aria.next_page}}"
class="next_page {{^next_page}}disabled{{/next_page}}"
href="?page_num={{next_page}}{{query_string}}"
id="next"
rel="next"
>
{{tt.pagination.next}}
</a>
<a id="prev" href="?page_num={{previous_page}}{{query_string}}" class="previous_page {{^previous_page}}disabled{{/previous_page}}">Previous</span>
<a id="next" href="?page_num={{next_page}}{{query_string}}" class="next_page {{^next_page}}disabled{{/next_page}}" rel="next" aria-label="Next Page">Next</a>
</div>
</nav>
</nav>
+5 -9
View File
@@ -4,7 +4,7 @@
<h1 class="header-title text-center text-md-left py-4">
<span class="f1-light text-gray-light">
{{t.search_results_for}}
Search results for
</span>
{{name}}
</h1>
@@ -21,12 +21,7 @@
<li class="Box-row Box-row--gray">
<span class="Counter Counter--gray tooltipped tooltipped-w" aria-label="{{filename_count}} hits in filename - {{count}} hits in content">{{filename_count}} - {{count}}</span>&nbsp;
<span class="text-bold"><a href="{{href}}">{{name}}</a></span>
<button
class="btn-link tooltipped tooltipped-w float-right toggle-context"
aria-label="{{t.aria.show_all}}"
>
{{#octicon}}search{{/octicon}}
</button>
<button class="btn-link tooltipped tooltipped-w float-right toggle-context" aria-label="Show all {{count}} hits in this page">{{#octicon}}search{{/octicon}}</button>
</li>
<div class="search-context">
@@ -34,6 +29,7 @@
<li class="Box-row border-0"><span class="text-italic">{{.}}</span></li>
{{/context}}
</div>
{{/results}}
</ul>
</div>
@@ -41,12 +37,12 @@
{{#no_results}}
<p id="no-results">
{{t.no_results}} <strong>{{query}}</strong>.
There are no results for your search <strong>{{query}}</strong>.
</p>
{{/no_results}}
<div id="footer" class="mt-4">
<a class="btn" href="#">{{t.back_to_top}}</a>
<a class="btn" href="#">Back to Top</a>
</div>
</div>
+10
View File
@@ -0,0 +1,10 @@
<div id="user">
<p>
{{#user_authed}}
{{user_name}} | {{user_provider}} | <strong><a href="/__omnigollum__/logout">[Logout]</a></strong>
{{/user_authed}}
{{^user_authed}}
not logged in | <strong><a href="/__omnigollum__/login">[Login]</a></strong>
{{/user_authed}}
<p>
</div>
+1 -1
View File
@@ -6,7 +6,7 @@ module Precious
attr_reader :page, :diff, :versions, :message, :allow_editing
def title
[t[:comparison_of], @page.title].join(" ")
"Comparison of #{@page.title}"
end
def before
-12
View File
@@ -4,18 +4,6 @@ module Precious
true
end
def default_keybinding
@default_keybinding
end
def keybindings
Gollum::KEYBINDINGS.map do |kb|
{ :name => kb,
:selected => default_keybinding == kb
}
end
end
def formats(selected = @page.format)
Gollum::Markup.formats.map do |key, val|
{ :name => val[:name],
@@ -21,13 +21,6 @@ module Precious
autofill I18n.t(locale_klass_name)
end
# Returns all I18n translation strings from the root of an I18n YAML file.
# Otherwise, it works exactly like the `#t` method that's also defined in
# this file.
def tt
autofill I18n.t('.')
end
private
# Recursively looks up I18n translation values and autofills any YAML
+1 -1
View File
@@ -9,7 +9,7 @@ module Precious
attr_reader :wiki
def title
t[:title]
"Latest Changes (Globally)"
end
def versions
+19 -6
View File
@@ -20,7 +20,7 @@ module Precious
end
def title
t[:title]
"Home"
end
def has_path
@@ -58,11 +58,11 @@ module Precious
def js # custom js
@js
end
def critic_markup
@critic_markup
end
def per_page_uploads
@per_page_uploads
end
@@ -75,19 +75,32 @@ module Precious
def search
false
end
def history
false
end
def overview
false
end
def latest_changes
false
end
# Passthrough additional omniauth parameters for status bar
def user_authed
@user_authed
end
def user_provider
@user.provider
end
def user_name
@user.name
end
end
end
end
+10 -10
View File
@@ -3,11 +3,11 @@ require 'pathname'
module Precious
module Views
class Overview < Layout
attr_reader :name, :results, :ref, :allow_editing, :newable
attr_reader :results, :ref, :allow_editing, :newable
HIDDEN_PATHS = ['.gitkeep']
def title
t[:title]
"Overview of #{@ref}"
end
# def editable
@@ -38,21 +38,21 @@ module Precious
end
end
def files_folders
if has_results
files_and_folders = []
@results.each do |result|
result_path = result.url_path
result_path = result_path.sub(/^#{Regexp.escape(@path)}\//, '') unless @path.nil?
result_path = result_path.sub(/^#{Regexp.escape(@path)}\//, '') unless @path.nil?
if result_path.include?('/')
# result contains a folder
folder_name = result_path.split('/').first
folder_path = @path ? "#{@path}/#{folder_name}" : folder_name
folder_url = "#{overview_path}/#{folder_path}/"
files_and_folders << {name: folder_name, icon: rocticon('file-directory'), type: 'dir', url: folder_url, is_file: false}
elsif !HIDDEN_PATHS.include?(result_path)
elsif !HIDDEN_PATHS.include?(result_path)
file_url = page_route(result.escaped_url_path)
files_and_folders << {name: result.filename, icon: rocticon('file'), type: 'file', url: file_url, file_path: result.escaped_url_path, is_file: true}
end
@@ -61,7 +61,7 @@ module Precious
files_and_folders.uniq{|f| f[:name]}.sort_by!{|f| [f[:type], f[:name]]}
end
end
def has_results
!@results.empty?
@@ -70,12 +70,12 @@ module Precious
def no_results
@results.empty?
end
def latest_changes
true
end
end
end
end
+1 -1
View File
@@ -10,4 +10,4 @@ module Precious
@page_num == 1 ? nil : (@page_num - 1).to_s
end
end
end
end
+19 -60
View File
@@ -1,84 +1,43 @@
require 'rss'
class RSSView
include Precious::Views::AppHelpers
include Precious::Views::RouteHelpers
attr_reader :base_url
def initialize(base_url, wiki_title, url, changes)
@base_url = base_url
@wiki_title = wiki_title
@url = url
@changes = changes
end
def render
latest_changes = "#{@url}#{latest_changes_path}"
RSS::Maker.make('2.0') do |maker|
maker.channel.author = 'Gollum Wiki'
maker.channel.description = "Latest Changes in #{@wiki_title}"
maker.channel.link = latest_changes_url
maker.channel.title = "#{@wiki_title} Latest Changes"
maker.channel.updated = @changes.first.authored_date
maker.channel.title = "#{@wiki_title} Latest Changes"
maker.channel.description = "Latest Changes in #{@wiki_title}"
maker.channel.link = latest_changes
@changes.each do |change|
maker.items.new_item do |item|
item.description = feed_item_description(change)
item.link = latest_changes_url
item.title = feed_item_title(change)
item.link = latest_changes
item.title = change.message
item.updated = change.authored_date
id = change.id
files = change.stats.files.map do |files|
[files[:old_file], files[:new_file]].compact.map do |file|
f = extract_page_dir(file)
"<li><a href=\"#{@url}#{page_route(f)}/#{id}\">#{f}</a></li>"
end
end
item.description = "Commited by: <a href=\"mailto:#{change.author.email}\">#{change.author.name}</a><br/>Commit ID: #{id[0..6]}<br/><br/>Affected files:<ul>#{files.join}</ul>"
end
end
end.to_s
end
private
def feed_item_commit_body(change)
body = change.message.lines[1..-1].join
body = body.split(/\n{2}/).map { |paragraph| "<p>#{paragraph}</p>" }.join
body.gsub!(/\n/, ' ')
body
end
def feed_item_description(change)
ERB.new(<<~HTML_PARTIAL)
<%= feed_item_commit_body(change) %>
Committed by: <a href="mailto:<%= change.author.email %>">
<%= change.author.name %>
</a><br />
Commit ID: <%= change.id[0..6] %><br /><br />
<%= feed_item_files(change) %>
HTML_PARTIAL
.result(binding)
end
def feed_item_files(change)
file_list = change.stats.files.map { |change_files|
[
change_files[:old_file],
change_files[:new_file]
].compact
}
ERB.new(<<~HTML_PARTIAL)
Affected files: <ul>
<% file_list.each do |change_files| %>
<% change_files.each do |file| %>
<% file_href = "%s%s/%s" % [@url, page_route(file), change.id] %>
<li><a href="<%= file_href %>"><%= file %></a></li>
<% end %>
<% end %>
</ul>
HTML_PARTIAL
.result(binding)
end
def feed_item_title(change)
change.message.lines.first.strip
end
def latest_changes_url
"%s%s" % [@url, latest_changes_path]
end
end
end
+1 -1
View File
@@ -23,7 +23,7 @@ module Precious
end
def title
t[:title]
"Search results for " + @query
end
def search
-8
View File
@@ -1,8 +0,0 @@
{
"dependencies": {
"mousetrap": "^1.6.5"
},
"engines": {
"node" : ">=16.15.0"
}
}
-23
View File
@@ -1,23 +0,0 @@
require_relative 'helper'
require 'selenium-webdriver'
require 'capybara/dsl'
Selenium::WebDriver::Chrome.path = ENV['CHROME_PATH'] if ENV['CHROME_PATH']
CAPYBARA_DRIVER =
if ENV['CI']
:selenium_chrome_headless
else
ENV.fetch('CAPYBARA_DRIVER', :selenium_chrome).to_sym
end
Capybara.default_driver = CAPYBARA_DRIVER
Capybara.enable_aria_label = true
Capybara.server = :webrick
def console_log(page, level = :severe)
page.driver.browser.logs.get(:browser).select { |log| log.level == level.to_s.upcase }
end
+2 -98
View File
@@ -15,13 +15,12 @@ describe Precious::Views::LocaleHelpers do
end
def setup
I18n.available_locales = [:en, :de]
I18n.load_path = Dir[File.expand_path("test/support/locales" + "/*.yml")]
::I18n.available_locales = [:en, :de]
::I18n.load_path = Dir[File.expand_path("test/support/locales" + "/*.yml")]
end
def teardown
I18n.locale = :en
I18n.load_path = Dir[::File.expand_path("lib/gollum/locales") + "/*.yml"]
end
let(:dummy_instance) { TestClass.new }
@@ -132,99 +131,4 @@ describe Precious::Views::LocaleHelpers do
end
end
end
describe "#tt" do
describe "mustache usage" do
let(:subject) { dummy_instance.render(mustache_template) }
let(:mustache_template) { "{{ tt.test_class.hello_world }}" }
describe "in the default locale" do
it "returns the translation string" do
_(subject).must_equal "Hello world"
end
end
describe "in the configured locale" do
it "returns the translation string" do
I18n.locale = :de
_(subject).must_equal "Hallo Welt"
end
end
describe "translations with YAML arguments" do
let(:mustache_template) { "{{ tt.test_class.author_info.full }}" }
describe "in the default locale" do
it "autofills YAML arguments" do
_(subject).must_equal "Author J.R.R. is from Bloemfontein"
end
end
describe "in the configured locale" do
it "autofills YAML arguments" do
I18n.locale = :de
_(subject).must_equal "Autor J.R.R. ist vom Bloemfontein"
end
end
end
describe "translations with invalid arguments" do
let(:mustache_template) { "{{ tt.test_class.has_invalid_argument }}" }
it "fails gracefully with embedded error message" do
expected_string = "Welcome to " \
"[#{TestClass::NO_METHOD_MESSAGE}: no_matching_method]"
_(subject).must_equal expected_string
end
end
describe "missing translations" do
let(:mustache_template) {
"{{ tt.test_class.nested.nonexistent_key }}"
}
it "outputs an empty string" do
_(subject).must_be_empty
end
end
end
describe "usage" do
let(:subject) { dummy_instance.tt }
it "returns a hash" do
_(subject).must_be_kind_of Hash
end
it "returns all present translation keys" do
i18n_keys = I18n.t(".").keys
_(subject.keys).must_equal i18n_keys
end
it "returns nested keys" do
nested_keys = subject[:test_class][:author_info].keys
_(nested_keys).must_equal [:full]
end
describe "auto-filled YAML arguments" do
let(:subject) { dummy_instance.tt[:test_class][:author_info][:full] }
it "auto-fills in the default locale" do
_(subject).must_equal "Author J.R.R. is from Bloemfontein"
end
it "auto-fills in a configured locale" do
I18n.locale = :de
_(subject).must_equal "Autor J.R.R. ist vom Bloemfontein"
end
end
end
end
end
-1
View File
@@ -32,7 +32,6 @@ ENV['RACK_ENV'] = 'test'
require 'gollum'
require 'gollum/app'
# Disable the metadata feature
$METADATA = false
-44
View File
@@ -1,44 +0,0 @@
require_relative '../capybara_helper'
def console_log(page, level = :severe)
page.driver.browser.logs.get(:browser).select{|log| log.level == level.to_s.upcase }
end
def expected_errors
Regexp.union([
%r{Refused to apply style from 'http:.*/gollum/create/custom.css'}
])
end
def assert_only_expected_errors(log)
assert_equal [], log.reject {|err| err.message.match?(expected_errors) }
end
context 'Frontend with mathjax' do
include Capybara::DSL
setup do
@path = cloned_testpath("examples/lotr.git")
@wiki = Gollum::Wiki.new(@path)
Precious::App.set(:gollum_path, @path)
Precious::App.set(:wiki_options, {mathjax: true})
Capybara.app = Precious::App
end
test 'no unexpected errors on /' do
visit '/'
log = console_log(page)
assert_only_expected_errors(log)
end
test 'no unexpected errors on /create/' do
visit '/create/Foobar'
log = console_log(page)
assert_only_expected_errors(log)
end
teardown do
Capybara.reset_sessions!
Capybara.use_default_driver
end
end
-61
View File
@@ -1,61 +0,0 @@
require_relative '../capybara_helper'
context 'Localized frontend' do
include Capybara::DSL
setup do
@path = cloned_testpath "examples/lotr.git"
@wiki = Gollum::Wiki.new(@path)
Precious::App.set :gollum_path, @path
Precious::App.set :wiki_options, {mathjax: true}
Capybara.app = Precious::App
end
test 'can visit search results page' do
visit '/gollum/search'
fill_in('Search', with: 'something-to-return-no-results')
.native
.send_keys(:return)
assert_includes page.text,
'Search results for something-to-return-no-results'
assert_includes page.text,
'There are no results for your search something-to-return-no-results.'
click_on 'Back to Top'
visit '/gollum/search'
fill_in('Search', with: 'Bilbo').native.send_keys(:return)
assert_includes page.text, 'Search results for Bilbo'
click_on 'Show all hits on this page'
click_on 'Bilbo-Baggins.md'
assert page.current_path, '/Bilbo-Baggins.md'
end
test 'can visit overview page' do
visit "/gollum/overview"
assert_includes page.text, 'Overview of master'
assert_includes page.text, 'Home'
click_on 'Back to Top'
click_on 'Bilbo-Baggins.md'
assert page.current_path, '/Bilbo-Baggins.md'
end
teardown do
@path = nil
@wiki = nil
Capybara.reset_sessions!
Capybara.use_default_driver
end
end
+12 -104
View File
@@ -63,10 +63,18 @@ context "Frontend" do
end
test 'rss feed' do
channel_title = <<EOF
<title>Gollum Wiki Latest Changes</title>
EOF
item = <<EOF
<description>Commited by: &lt;a href=&quot;mailto:dawa.ometto@phil.uu.nl&quot;&gt;Dawa Ometto&lt;/a&gt;&lt;br/&gt;Commit ID: 02796b1&lt;br/&gt;&lt;br/&gt;Affected files:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://example.org/custom.css/02796b1450691f90db5d6dc6a816a4980ce80d07&quot;&gt;custom.css&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://example.org/custom.js/02796b1450691f90db5d6dc6a816a4980ce80d07&quot;&gt;custom.js&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</description>
EOF
get '/gollum/feed/'
assert last_response.ok?
assert_equal 'application/rss+xml', last_response.headers['Content-Type']
assert last_response.body.start_with?('<?xml')
assert last_response.body.include?(item)
assert last_response.body.include?(channel_title)
end
test "show sidebar, header, footer when present" do
@@ -469,7 +477,7 @@ context "Frontend" do
assert_equal 'abc', file.raw_data
Precious::App.set(:wiki_options, {allow_uploads: false})
end
test "upload a file with mode page" do
temp_upload_file = Tempfile.new(['upload', '.file']) << "abc\r"
temp_upload_file.close
@@ -483,7 +491,7 @@ context "Frontend" do
assert_equal "abc\r", file.raw_data
Precious::App.set(:wiki_options, {allow_uploads: false, per_page_uploads: false})
end
test "upload a file with valid extension" do
temp_upload_file = Tempfile.new(['upload', '.txt']) << "abc\r"
temp_upload_file.close
@@ -498,20 +506,6 @@ context "Frontend" do
Precious::App.set(:wiki_options, {allow_uploads: false, per_page_uploads: false})
end
test 'upload a file with mode page from the edit page (drag and drop)' do
temp_upload_file = Tempfile.new(['upload', '.file']) << "abc\r"
temp_upload_file.close
Precious::App.set(:wiki_options, {allow_uploads: true, per_page_uploads: true})
post "/gollum/upload_file", {:file => Rack::Test::UploadedFile.new(::File.open(temp_upload_file))}, {'HTTP_REFERER' => 'http://localhost:4567/gollum/edit/foo/Bar.md', 'HTTP_HOST' => 'localhost:4567'}
assert_equal 302, last_response.status # redirect is expected
@wiki.clear_cache
# Find the file in a page-specific subdir (here: foo/Bar), based on referer
file = @wiki.file("uploads/foo/Bar/#{::File.basename(temp_upload_file.path)}")
assert_equal "abc\r", file.raw_data
Precious::App.set(:wiki_options, {allow_uploads: false, per_page_uploads: false})
end
test "upload a file with https referer" do
temp_upload_file = Tempfile.new(['https_upload', '.file']) << 'abc'
temp_upload_file.close
@@ -526,6 +520,7 @@ context "Frontend" do
Precious::App.set(:wiki_options, {allow_uploads: false, per_page_uploads: false})
end
test "guard against uploading an existing file" do
temp_upload_file = Tempfile.new(['upload', '.file']) << 'abc'
temp_upload_file.close
@@ -1059,94 +1054,7 @@ context 'Frontend with base path' do
assert_equal '/wiki/gollum/history/Bilbo-Baggins.md', last_request.fullpath
end
test 'upload a file with mode page from the edit page (drag and drop)' do
temp_upload_file = Tempfile.new(['upload', '.file']) << "abc\r"
temp_upload_file.close
Precious::App.set(:wiki_options, {allow_uploads: true, per_page_uploads: true})
post "/wiki/gollum/upload_file", {:file => Rack::Test::UploadedFile.new(::File.open(temp_upload_file))}, {'HTTP_REFERER' => 'http://localhost:4567/wiki/gollum/edit/foo/Bar.md', 'HTTP_HOST' => 'localhost:4567'}
assert_equal 302, last_response.status # redirect is expected
@wiki.clear_cache
# Find the file in a page-specific subdir (here: foo/Bar), based on referer
file = @wiki.file("uploads/foo/Bar/#{::File.basename(temp_upload_file.path)}")
assert_equal "abc\r", file.raw_data
Precious::App.set(:wiki_options, {allow_uploads: false, per_page_uploads: false})
end
def app
Precious::MapGollum.new(@base_path)
end
end
context "Default keybindings" do
include Rack::Test::Methods
setup do
@path = cloned_testpath "examples/empty.git"
@wiki = Gollum::Wiki.new @path
@url = '/gollum/create/test'
Precious::App.set :gollum_path, @path
Precious::App.set :wiki_options, {default_keybinding: nil}
end
teardown do
FileUtils.rm_rf @path
@path = nil
@wiki = nil
@url = nil
end
test 'keybinding unset' do
get @url
assert_equal last_response.body.include?('selected="selected" value="default"'), false
assert_equal last_response.body.include?('selected="selected" value="vim"'), false
assert_equal last_response.body.include?('selected="selected" value="emacs"'), false
end
test 'nonexistent keybinding' do
Precious::App.set :wiki_options, {:default_keybinding => 'does-not-exist'}
get @url
assert_equal last_response.body.include?('selected="selected" value="default"'), false
assert_equal last_response.body.include?('selected="selected" value="vim"'), false
assert_equal last_response.body.include?('selected="selected" value="emacs"'), false
end
test 'keybinding `default`' do
Precious::App.set :wiki_options, {:default_keybinding => 'default'}
get @url
assert_equal last_response.body.include?('selected="selected" value="default"'), true
assert_equal last_response.body.include?('selected="selected" value="vim"'), false
assert_equal last_response.body.include?('selected="selected" value="emacs"'), false
end
test 'keybinding `vim`' do
Precious::App.set :wiki_options, {:default_keybinding => 'vim'}
get @url
assert_equal last_response.body.include?('selected="selected" value="default"'), false
assert_equal last_response.body.include?('selected="selected" value="vim"'), true
assert_equal last_response.body.include?('selected="selected" value="emacs"'), false
end
test 'keybinding `emacs`' do
Precious::App.set :wiki_options, {:default_keybinding => 'emacs'}
get @url
assert_equal last_response.body.include?('selected="selected" value="default"'), false
assert_equal last_response.body.include?('selected="selected" value="vim"'), false
assert_equal last_response.body.include?('selected="selected" value="emacs"'), true
end
def app
Precious::App
end
end
-32
View File
@@ -1,32 +0,0 @@
require File.expand_path(File.join(File.dirname(__FILE__), "helper"))
context 'Precious::Helpers' do
include Precious::Helpers
test 'remove trailing and leading slashes' do
['/wiki', '/wiki/', 'wiki/', '//wiki//'].each do |param|
assert_equal 'wiki', remove_leading_and_trailing_slashes(param)
end
assert_equal 'wi/ki', remove_leading_and_trailing_slashes('/wi/ki/')
assert_equal '', remove_leading_and_trailing_slashes('/')
end
test 'per page upload location helper' do
# https referer with and without base path
host_with_port = 'localhost:4567'
assert_equal 'uploads/Home', find_per_page_upload_subdir('https://localhost:4567/Home.md', host_with_port, nil)
assert_equal 'uploads/Home', find_per_page_upload_subdir('https://localhost:4567/wiki/Home.md', host_with_port, '/wiki')
# http referer with and without base path
assert_equal 'uploads/Home', find_per_page_upload_subdir('http://localhost:4567/Home.md', host_with_port, nil)
assert_equal 'uploads/Home', find_per_page_upload_subdir('http://localhost:4567/wiki/Home.md', host_with_port, '/wiki')
# edit page referer with and without base path
assert_equal 'uploads/foo/Home', find_per_page_upload_subdir('http://localhost:4567/gollum/edit/foo/Home.md', host_with_port, nil)
assert_equal 'uploads/foo/Home', find_per_page_upload_subdir('http://localhost:4567/wiki/gollum/edit/foo/Home.md', host_with_port, '/wiki')
# referer with base path with slashes in the wrong place
assert_equal 'uploads/Home', find_per_page_upload_subdir('http://localhost:4567/wiki/Home.md', host_with_port, '/wiki/')
assert_equal 'uploads/Home', find_per_page_upload_subdir('http://localhost:4567/wiki/Home.md', host_with_port, 'wiki')
end
end
-101
View File
@@ -1,101 +0,0 @@
require_relative "helper"
context "Precious::Views::RSS" do
# Simplisticially mimics a `Gollum::Git::Actor` object.
#
MockAuthor = Struct.new(:name, :email)
# Simplistically mimics a `Gollum::Git::Commit` object.
#
MockChange = Class.new do
def author
MockAuthor.new("committer name", "email@example.com")
end
def authored_date
Time.new(1999, 01, 01, 0, 0)
end
def files
["file 1", "file 2"]
end
def id
"f0f0f0f0"
end
def message
<<~COMMIT_MESSAGE
Multi-line commit message
This commit is multiple lines long so we can test how this is
rendered in the feed.
Git's documentation says that the first line of a commit should
be 50 characters or fewer, and the rest of the commit body's
lines should not exceed 72 characters in length.
COMMIT_MESSAGE
end
def stats
OpenStruct.new(files: [{old_file: "old", new_file: "new"}])
end
end
test "renders a valid RSS feed" do
feed = RSSView.new(
"/",
"Wiki Name",
"https://example.com",
[MockChange.new]
).render
# Assert that we have required RSS feed elements.
#
assert_match "<?xml version=\"1.0\" encoding=\"UTF-8\"?>", feed
assert_match /<rss version=\"2.0\"(.*)<\/rss>/m, feed
assert_match /<channel>(.*)<\/channel>/m, feed
# Assert that we have feed metadata.
#
assert_match "<title>Wiki Name Latest Changes</title>", feed
assert_match "<link>https://example.com/gollum/latest_changes</link>", feed
assert_match "<description>Latest Changes in Wiki Name</description>", feed
assert_match /<pubDate>(.*)<\/pubDate>/, feed
# Assert that we have an item in our feed.
#
assert_match /<item>(.*)<\/item>/m, feed
# And it has a title.
#
assert_match "<title>Multi-line commit message</title>", feed
# Assert that the description contains expected content.
#
assert_match /<description>(.*)<\/description>/m, feed
assert_match /&lt;p&gt; This commit(.*)&lt;\/p&gt;/, feed
assert_match /&lt;p&gt;Git's documentation(.*)&lt;\/p&gt;/, feed
# Assert that the description contains information about the commit.
# i.e.:
#
# <a href="mailto:email@example.com">committer name</a>
#
# Commit ID: f0f0f0f0
#
assert_match /Committed by: /, feed
assert_match /\&lt;a href=\&quot;mailto:email@example.com\&quot;\&gt;/, feed
assert_match /\&gt;\n committer name\n\&lt;\/a&gt;/, feed
assert_match "Commit ID: f0f0f0f", feed
# Assert that affected files include links to commits, i.e.:
#
# <a href="https://example.com/old/f0f0f0f0">new</a>
#
assert_match /Affected files: /, feed
assert_match /\&lt;a href=\&quot;https:\/\/example.com\/old\/f0f0f0f0\&quot;/,
feed
assert_match /f0f0f0f0&quot;&gt;new&lt;\/a&gt;/, feed
end
end
-8
View File
@@ -1,8 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
mousetrap@^1.6.5:
version "1.6.5"
resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.6.5.tgz#8a766d8c272b08393d5f56074e0b5ec183485bf9"
integrity sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==