Merge branch '5.x' into critic_markup_styling
This commit is contained in:
@@ -81,10 +81,10 @@ Installation examples for individual systems can be seen [here](https://github.c
|
|||||||
### Markups
|
### Markups
|
||||||
|
|
||||||
Gollum presently ships with support for the following markups:
|
Gollum presently ships with support for the following markups:
|
||||||
* [Markdown](http://daringfireball.net/projects/markdown/syntax)
|
* [Markdown](http://daringfireball.net/projects/markdown/syntax) (see [below](#Markdown-flavors) for more information on Markdown flavors)
|
||||||
* [RDoc](http://rdoc.sourceforge.net/)
|
* [RDoc](http://rdoc.sourceforge.net/)
|
||||||
|
|
||||||
Since all markups are rendered by the [github-markup](https://github.com/github/markup) gem, you can easily add support for other markups by additional installation:
|
You can easily activate support for other markups by installing additional renderers (any that are supported by [github-markup](https://github.com/github/markup)):
|
||||||
* [AsciiDoc](http://asciidoctor.org/docs/asciidoc-syntax-quick-reference/) -- `[sudo] gem install asciidoctor`
|
* [AsciiDoc](http://asciidoctor.org/docs/asciidoc-syntax-quick-reference/) -- `[sudo] gem install asciidoctor`
|
||||||
* [Creole](http://www.wikicreole.org/wiki/CheatSheet) -- `[sudo] gem install creole`
|
* [Creole](http://www.wikicreole.org/wiki/CheatSheet) -- `[sudo] gem install creole`
|
||||||
* [MediaWiki](http://www.mediawiki.org/wiki/Help:Formatting) -- `[sudo] gem install wikicloth`
|
* [MediaWiki](http://www.mediawiki.org/wiki/Help:Formatting) -- `[sudo] gem install wikicloth`
|
||||||
@@ -92,10 +92,14 @@ Since all markups are rendered by the [github-markup](https://github.com/github/
|
|||||||
* [Pod](http://perldoc.perl.org/perlpod.html) -- requires Perl >= 5.10 (the `perl` command must be available on your command line)
|
* [Pod](http://perldoc.perl.org/perlpod.html) -- requires Perl >= 5.10 (the `perl` command must be available on your command line)
|
||||||
* Lower versions should install `Pod::Simple` from CPAN.
|
* Lower versions should install `Pod::Simple` from CPAN.
|
||||||
* [ReStructuredText](http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html) -- requires python >= 2 (the `python2` command must be available on your command line)
|
* [ReStructuredText](http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html) -- requires python >= 2 (the `python2` command must be available on your command line)
|
||||||
* Note that Gollum will also need you to install `docutils` for your Python 2. Installation procedure can, again, vary depending on operating system and package manager.
|
* Note that Gollum will also need you to install `docutils` for your Python 2.
|
||||||
* [Textile](http://redcloth.org/hobix.com/textile/quick.html) -- `[sudo] gem install RedCloth`
|
* [Textile](http://redcloth.org/hobix.com/textile/quick.html) -- `[sudo] gem install RedCloth`
|
||||||
|
|
||||||
By default, Gollum ships with the `kramdown` gem to render Markdown. However, you can use any [Markdown renderer supported by github-markup](https://github.com/github/markup/blob/master/lib/github/markup/markdown.rb). The thing to remember is that the first installed renderer from the list will be used. So, for example, `redcarpet` will NOT be used if `github/markdown` is installed.
|
### Markdown flavors
|
||||||
|
|
||||||
|
By default, Gollum ships with the `kramdown` gem to render Markdown. However, you can use any [Markdown renderer supported by github-markup](https://github.com/github/markup/blob/master/lib/github/markup/markdown.rb). This includes [CommonMark](https://commonmark.org/) support via the `commonmarker` gem. The first installed renderer from the list will be used (e.g., `redcarpet` will NOT be used if `github/markdown` is installed). Just `gem install` the renderer of your choice.
|
||||||
|
|
||||||
|
See [here](https://github.com/gollum/gollum/wiki/Custom-rendering-gems) for instructions on how to use custom rendering gems and set custom options.
|
||||||
|
|
||||||
## RUNNING
|
## RUNNING
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -127,7 +127,7 @@ MSG
|
|||||||
end
|
end
|
||||||
opts.on("--allow-uploads [MODE]", [:dir, :page], "Enable file uploads.",
|
opts.on("--allow-uploads [MODE]", [:dir, :page], "Enable file uploads.",
|
||||||
"If set to 'dir', Gollum will store all uploads in the '<git-repo>/uploads/' directory.",
|
"If set to 'dir', Gollum will store all uploads in the '<git-repo>/uploads/' directory.",
|
||||||
"If set to 'page', Gollum will store each upload at the currently edited page.") do |mode|
|
"If set to 'page', Gollum will store uploads per page in '<git-repo>/uploads/[pagename]'.") do |mode|
|
||||||
wiki_options[:allow_uploads] = true
|
wiki_options[:allow_uploads] = true
|
||||||
wiki_options[:per_page_uploads] = true if mode == :page
|
wiki_options[:per_page_uploads] = true if mode == :page
|
||||||
end
|
end
|
||||||
|
|||||||
+22
-20
@@ -85,9 +85,11 @@ module Precious
|
|||||||
settings.wiki_options[:allow_editing] = settings.wiki_options.fetch(:allow_editing, true)
|
settings.wiki_options[:allow_editing] = settings.wiki_options.fetch(:allow_editing, true)
|
||||||
@allow_editing = settings.wiki_options[:allow_editing]
|
@allow_editing = settings.wiki_options[:allow_editing]
|
||||||
@critic_markup = settings.wiki_options[:critic_markup]
|
@critic_markup = settings.wiki_options[:critic_markup]
|
||||||
|
@per_page_uploads = settings.wiki_options[:per_page_uploads]
|
||||||
|
|
||||||
forbid unless @allow_editing || request.request_method == "GET"
|
forbid unless @allow_editing || request.request_method == "GET"
|
||||||
Precious::App.set(:mustache, {:templates => settings.wiki_options[:template_dir]}) if settings.wiki_options[:template_dir]
|
Precious::App.set(:mustache, {:templates => settings.wiki_options[:template_dir]}) if settings.wiki_options[:template_dir]
|
||||||
@base_url = url('/', false).chomp('/')
|
@base_url = url('/', false).chomp('/').force_encoding('utf-8')
|
||||||
@page_dir = settings.wiki_options[:page_file_dir].to_s
|
@page_dir = settings.wiki_options[:page_file_dir].to_s
|
||||||
# above will detect base_path when it's used with map in a config.ru
|
# above will detect base_path when it's used with map in a config.ru
|
||||||
settings.wiki_options.merge!({ :base_path => @base_url })
|
settings.wiki_options.merge!({ :base_path => @base_url })
|
||||||
@@ -167,7 +169,6 @@ module Precious
|
|||||||
wikip = wiki_page(params[:splat].first)
|
wikip = wiki_page(params[:splat].first)
|
||||||
@name = join_page_name(wikip.name, wikip.ext)
|
@name = join_page_name(wikip.name, wikip.ext)
|
||||||
@path = wikip.path
|
@path = wikip.path
|
||||||
@upload_dest = find_upload_dest(@path)
|
|
||||||
|
|
||||||
wiki = wikip.wiki
|
wiki = wikip.wiki
|
||||||
@allow_uploads = wiki.allow_uploads
|
@allow_uploads = wiki.allow_uploads
|
||||||
@@ -181,14 +182,11 @@ module Precious
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# AJAX calls only
|
||||||
post '/upload_file' do
|
post '/upload_file' do
|
||||||
wiki = wiki_new
|
|
||||||
|
|
||||||
unless wiki.allow_uploads
|
wiki = wiki_new
|
||||||
@message = "File uploads are disabled"
|
halt 405 unless wiki.allow_uploads
|
||||||
mustache :error
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if params[:file]
|
if params[:file]
|
||||||
fullname = params[:file][:filename]
|
fullname = params[:file][:filename]
|
||||||
@@ -196,8 +194,21 @@ module Precious
|
|||||||
end
|
end
|
||||||
halt 500 unless tempfile.is_a? Tempfile
|
halt 500 unless tempfile.is_a? Tempfile
|
||||||
|
|
||||||
# Remove page file dir prefix from upload path if necessary -- committer handles this itself
|
if wiki.per_page_uploads
|
||||||
dir = wiki.per_page_uploads ? params[:upload_dest] : ::File.join([wiki.page_file_dir, 'uploads'].compact)
|
# remove base_url and gollum/* subpath if necessary
|
||||||
|
dir = request.referer.
|
||||||
|
sub(request.base_url, '').
|
||||||
|
sub(/.*gollum\/[-\w]+\//, '')
|
||||||
|
# remove file extension
|
||||||
|
dir = dir.sub(::File.extname(dir), '')
|
||||||
|
dir = ::File.join("uploads", dir)
|
||||||
|
else
|
||||||
|
# Remove page file dir prefix from upload path if necessary -- committer handles this itself
|
||||||
|
dir = ::File.join([wiki.page_file_dir, 'uploads'].compact)
|
||||||
|
end
|
||||||
|
halt 500 if dir.include?('..')
|
||||||
|
halt 500 unless Pathname(dir).relative?
|
||||||
|
|
||||||
ext = ::File.extname(fullname)
|
ext = ::File.extname(fullname)
|
||||||
format = ext.split('.').last || 'txt'
|
format = ext.split('.').last || 'txt'
|
||||||
filename = ::File.basename(fullname, ext)
|
filename = ::File.basename(fullname, ext)
|
||||||
@@ -225,8 +236,7 @@ module Precious
|
|||||||
committer.commit
|
committer.commit
|
||||||
redirect to(request.referer)
|
redirect to(request.referer)
|
||||||
rescue Gollum::DuplicatePageError => e
|
rescue Gollum::DuplicatePageError => e
|
||||||
@message = "Duplicate page: #{e.message}"
|
halt 409 # Signal conflict
|
||||||
mustache :error
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -309,7 +319,6 @@ module Precious
|
|||||||
@ext = wikip.ext
|
@ext = wikip.ext
|
||||||
@path = wikip.path
|
@path = wikip.path
|
||||||
@allow_uploads = wikip.wiki.allow_uploads
|
@allow_uploads = wikip.wiki.allow_uploads
|
||||||
@upload_dest = find_upload_dest(@path)
|
|
||||||
|
|
||||||
page_dir = settings.wiki_options[:page_file_dir].to_s
|
page_dir = settings.wiki_options[:page_file_dir].to_s
|
||||||
unless page_dir.empty?
|
unless page_dir.empty?
|
||||||
@@ -501,7 +510,6 @@ module Precious
|
|||||||
@page = page
|
@page = page
|
||||||
@name = name
|
@name = name
|
||||||
@content = page.formatted_data
|
@content = page.formatted_data
|
||||||
@upload_dest = find_upload_dest(path)
|
|
||||||
|
|
||||||
# Extensions and layout data
|
# Extensions and layout data
|
||||||
@editable = true
|
@editable = true
|
||||||
@@ -578,11 +586,5 @@ module Precious
|
|||||||
commit_message
|
commit_message
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_upload_dest(path)
|
|
||||||
settings.wiki_options[:allow_uploads] ?
|
|
||||||
(settings.wiki_options[:per_page_uploads] ?
|
|
||||||
"#{path}/#{@name}".sub(/^\/\//, '') : 'uploads'
|
|
||||||
) : ''
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -205,7 +205,6 @@
|
|||||||
|
|
||||||
var file = e.dataTransfer.files[0],
|
var file = e.dataTransfer.files[0],
|
||||||
formData = new FormData();
|
formData = new FormData();
|
||||||
formData.append('upload_dest', uploadDest);
|
|
||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
@@ -228,8 +227,8 @@
|
|||||||
}
|
}
|
||||||
window.ace_editor.insert(text);
|
window.ace_editor.insert(text);
|
||||||
},
|
},
|
||||||
error: function(r, textStatus) {
|
error: function(r, textStatus, errorThrown) {
|
||||||
alert('Error uploading file: ' + textStatus);
|
alert('Error uploading file: ' + textStatus + ' ' + errorThrown);
|
||||||
$editorBody.removeClass('uploading');
|
$editorBody.removeClass('uploading');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -105,8 +105,6 @@
|
|||||||
|
|
||||||
html += '<form method=post enctype="multipart/form-data" ' +
|
html += '<form method=post enctype="multipart/form-data" ' +
|
||||||
'action="' + action + '" ' + 'id="' + id + '">';
|
'action="' + action + '" ' + 'id="' + id + '">';
|
||||||
html += '<input type="hidden" name="upload_dest" value="' +
|
|
||||||
uploadDest + '">';
|
|
||||||
html += '<input type=file name="' + name + '">';
|
html += '<input type=file name="' + name + '">';
|
||||||
html += '</form>';
|
html += '</form>';
|
||||||
|
|
||||||
@@ -125,9 +123,9 @@
|
|||||||
title +'</h4></div>' +
|
title +'</h4></div>' +
|
||||||
'<div id="gollum-dialog-dialog-body">' + body + '</div>' +
|
'<div id="gollum-dialog-dialog-body">' + body + '</div>' +
|
||||||
'<div id="gollum-dialog-dialog-buttons">' +
|
'<div id="gollum-dialog-dialog-buttons">' +
|
||||||
'<a href="#" title="Cancel" id="gollum-dialog-action-cancel" ' +
|
'<button name="Cancel" id="gollum-dialog-action-cancel" ' +
|
||||||
'class="gollum-minibutton">Cancel</a>' +
|
'class="gollum-minibutton">Cancel</a>' +
|
||||||
'<a href="#" title="OK" id="gollum-dialog-action-ok" '+
|
'<button name="OK" id="gollum-dialog-action-ok" '+
|
||||||
'class="gollum-minibutton">OK</a>' +
|
'class="gollum-minibutton">OK</a>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
@@ -139,9 +137,9 @@
|
|||||||
title +'</h4></div>' +
|
title +'</h4></div>' +
|
||||||
'<div id="gollum-dialog-dialog-body">' + body + '</div>' +
|
'<div id="gollum-dialog-dialog-body">' + body + '</div>' +
|
||||||
'<div id="gollum-dialog-dialog-buttons">' +
|
'<div id="gollum-dialog-dialog-buttons">' +
|
||||||
'<a href="#" title="Cancel" id="gollum-dialog-action-cancel" ' +
|
'<button name="Cancel" id="gollum-dialog-action-cancel" ' +
|
||||||
'class="minibutton">Cancel</a>' +
|
'class="minibutton">Cancel</button>' +
|
||||||
'<a href="#" title="OK" id="gollum-dialog-action-ok" '+
|
'<button name="OK" id="gollum-dialog-action-ok" '+
|
||||||
'class="minibutton">OK</a>' +
|
'class="minibutton">OK</a>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ $(document).ready(function() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// ua detection
|
// ua detection
|
||||||
if ($.browser.mozilla) {
|
if ($.browser.mozilla) {
|
||||||
$('body').addClass('ff');
|
$('body').addClass('ff');
|
||||||
@@ -187,9 +188,40 @@ $(document).ready(function() {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
OK: function( res ) {
|
OK: function( res ) {
|
||||||
$('#upload').submit();
|
$('#wiki-content').addClass('uploading');
|
||||||
|
var formData = new FormData($('#upload').get(0));
|
||||||
|
var endpoint = $('#upload').attr("action");
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: endpoint,
|
||||||
|
type: 'POST',
|
||||||
|
data: formData,
|
||||||
|
processData: false,
|
||||||
|
contentType: false,
|
||||||
|
success: function(data) {
|
||||||
|
// File successfully uploaded
|
||||||
|
$('#wiki-content').removeClass('uploading');
|
||||||
|
},
|
||||||
|
error: function(data, textStatus, errorThrown) {
|
||||||
|
$('#wiki-content').removeClass('uploading');
|
||||||
|
if (data.status == 409) {
|
||||||
|
alert('This file already exists.');
|
||||||
|
} else {
|
||||||
|
alert('Error uploading file: ' + textStatus + ' ' + errorThrown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
$('#gollum-dialog-action-ok').attr('disabled', true);
|
||||||
|
$('input:file').on('change', function() {
|
||||||
|
if ($(this).val()) {
|
||||||
|
news = 'Your uploaded file will be accessible at<br>/' + uploadDest + '/' + $('input[type=file]').val().split('\\').pop();
|
||||||
|
$(".context").html(news);
|
||||||
|
$('#gollum-dialog-action-ok').attr('disabled', false);
|
||||||
|
|
||||||
|
};
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,12 @@
|
|||||||
height: 1%;
|
height: 1%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.uploading {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @section body */
|
/* @section body */
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.minibutton {
|
button.minibutton {
|
||||||
float: right;
|
float: right;
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
width: auto;
|
width: auto;
|
||||||
@@ -42,6 +42,12 @@
|
|||||||
|
|
||||||
@include button-base;
|
@include button-base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.minibutton:disabled {
|
||||||
|
opacity: 0.65;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#gollum-dialog-dialog-inner {
|
#gollum-dialog-dialog-inner {
|
||||||
|
|||||||
@@ -15,12 +15,17 @@
|
|||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var baseUrl = '{{base_url}}';
|
var criticMarkup = '{{critic_markup}}';
|
||||||
var uploadDest = '{{upload_dest}}';
|
var baseUrl = '{{base_url}}';
|
||||||
{{#page}}
|
var uploadDest = 'uploads';
|
||||||
var pageFullPath = '{{url_path}}';
|
var perPageUploads = '{{per_page_uploads}}';
|
||||||
{{/page}}
|
if (perPageUploads == 'true') {
|
||||||
var criticMarkup = '{{critic_markup}}';
|
uploadDest = uploadDest + window.location.pathname.replace(/.*gollum\/[-\w]+\//, "/").replace(/\.[^/.]+$/, "")
|
||||||
|
}
|
||||||
|
{{#page}}
|
||||||
|
var pageFullPath = '{{url_path}}';
|
||||||
|
{{/page}}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{{#sprockets_javascript_tag}}app{{/sprockets_javascript_tag}}
|
{{#sprockets_javascript_tag}}app{{/sprockets_javascript_tag}}
|
||||||
{{#use_identicon}}
|
{{#use_identicon}}
|
||||||
|
|||||||
@@ -21,10 +21,6 @@ module Precious
|
|||||||
@allow_uploads
|
@allow_uploads
|
||||||
end
|
end
|
||||||
|
|
||||||
def upload_dest
|
|
||||||
@upload_dest
|
|
||||||
end
|
|
||||||
|
|
||||||
def format
|
def format
|
||||||
@format ||= find_format.to_s.downcase
|
@format ||= find_format.to_s.downcase
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -65,10 +65,6 @@ module Precious
|
|||||||
@allow_uploads
|
@allow_uploads
|
||||||
end
|
end
|
||||||
|
|
||||||
def upload_dest
|
|
||||||
@upload_dest
|
|
||||||
end
|
|
||||||
|
|
||||||
def format
|
def format
|
||||||
@format = (@page.format || false) if @format.nil?
|
@format = (@page.format || false) if @format.nil?
|
||||||
@format.to_s.downcase
|
@format.to_s.downcase
|
||||||
|
|||||||
@@ -48,6 +48,10 @@ module Precious
|
|||||||
@critic_markup
|
@critic_markup
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def per_page_uploads
|
||||||
|
@per_page_uploads
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -57,10 +57,6 @@ module Precious
|
|||||||
@allow_uploads
|
@allow_uploads
|
||||||
end
|
end
|
||||||
|
|
||||||
def upload_dest
|
|
||||||
@upload_dest
|
|
||||||
end
|
|
||||||
|
|
||||||
def has_header
|
def has_header
|
||||||
if @header
|
if @header
|
||||||
@header.formatted_data.strip.empty? ? false : true
|
@header.formatted_data.strip.empty? ? false : true
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ context "Precious::Views::Editing" do
|
|||||||
|
|
||||||
assert_match /Delete this Page/, last_response.body, "'Delete this Page' link is blocked in page template"
|
assert_match /Delete this Page/, last_response.body, "'Delete this Page' link is blocked in page template"
|
||||||
assert_match /New/, last_response.body, "'New' button is blocked in page template"
|
assert_match /New/, last_response.body, "'New' button is blocked in page template"
|
||||||
assert_match /Upload/, last_response.body, "'Upload' link is blocked in page template"
|
assert_match /Upload\b/, last_response.body, "'Upload' link is blocked in page template"
|
||||||
assert_match /Rename/, last_response.body, "'Rename' link is blocked in page template"
|
assert_match /Rename/, last_response.body, "'Rename' link is blocked in page template"
|
||||||
assert_match /Edit/, last_response.body, "'Edit' link is blocked in page template"
|
assert_match /Edit/, last_response.body, "'Edit' link is blocked in page template"
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ context "Precious::Views::Editing" do
|
|||||||
|
|
||||||
assert_no_match /Delete this Page/, last_response.body, "'Delete this Page' link not blocked in page template"
|
assert_no_match /Delete this Page/, last_response.body, "'Delete this Page' link not blocked in page template"
|
||||||
assert_no_match /New/, last_response.body, "'New' button not blocked in page template"
|
assert_no_match /New/, last_response.body, "'New' button not blocked in page template"
|
||||||
assert_no_match /Upload/, last_response.body, "'Upload' link not blocked in page template"
|
assert_no_match /Upload\b/, last_response.body, "'Upload' link not blocked in page template"
|
||||||
assert_no_match /Rename/, last_response.body, "'Rename' link not blocked in page template"
|
assert_no_match /Rename/, last_response.body, "'Rename' link not blocked in page template"
|
||||||
assert_no_match /Edit/, last_response.body, "'Edit' link not blocked in page template"
|
assert_no_match /Edit/, last_response.body, "'Edit' link not blocked in page template"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user