Massive change of SVN properties to deal with EOL style problem
This commit is contained in:
parent
b747b611b3
commit
3b6566577c
108 changed files with 12417 additions and 12417 deletions
|
@ -1,71 +1,71 @@
|
|||
require 'fileutils'
|
||||
require 'application'
|
||||
require 'instiki_errors'
|
||||
|
||||
class FileController < ApplicationController
|
||||
|
||||
layout 'default'
|
||||
|
||||
before_filter :check_allow_uploads
|
||||
|
||||
def file
|
||||
check_path
|
||||
|
||||
if @params['file']
|
||||
# form supplied
|
||||
file_yard.upload_file(@file_name, @params['file'])
|
||||
flash[:info] = "File '#{@file_name}' successfully uploaded"
|
||||
@web.refresh_pages_with_references(@file_name)
|
||||
return_to_last_remembered
|
||||
elsif file_yard.has_file?(@file_name)
|
||||
send_file(file_yard.file_path(@file_name))
|
||||
else
|
||||
logger.debug("File not found: #{file_yard.files_path}/#{@file_name}")
|
||||
# go to the template, which is a file upload form
|
||||
end
|
||||
end
|
||||
|
||||
def cancel_upload
|
||||
return_to_last_remembered
|
||||
end
|
||||
|
||||
def pic
|
||||
check_path
|
||||
if @params['file']
|
||||
# form supplied
|
||||
file_yard.upload_file(@file_name, @params['file'])
|
||||
flash[:info] = "Image '#{@file_name}' successfully uploaded"
|
||||
@web.refresh_pages_with_references(@file_name)
|
||||
return_to_last_remembered
|
||||
elsif file_yard.has_file?(@file_name)
|
||||
send_file(file_yard.file_path(@file_name))
|
||||
else
|
||||
logger.debug("Image not found: #{file_yard.files_path}/#{@file_name}")
|
||||
render_action 'file'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
|
||||
def check_allow_uploads
|
||||
unless @web.allow_uploads
|
||||
render_text 'File uploads are blocked by the webmaster', '403 Forbidden'
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def check_path
|
||||
raise Instiki::ValidationError.new("Invalid path: no file name") unless @file_name
|
||||
raise Instiki::ValidationError.new("Invalid path: no web name") unless @web_name
|
||||
raise Instiki::ValidationError.new("Invalid path: unknown web name") unless @web
|
||||
end
|
||||
|
||||
def file_yard
|
||||
@wiki.file_yard(@web)
|
||||
end
|
||||
|
||||
end
|
||||
require 'fileutils'
|
||||
require 'application'
|
||||
require 'instiki_errors'
|
||||
|
||||
class FileController < ApplicationController
|
||||
|
||||
layout 'default'
|
||||
|
||||
before_filter :check_allow_uploads
|
||||
|
||||
def file
|
||||
check_path
|
||||
|
||||
if @params['file']
|
||||
# form supplied
|
||||
file_yard.upload_file(@file_name, @params['file'])
|
||||
flash[:info] = "File '#{@file_name}' successfully uploaded"
|
||||
@web.refresh_pages_with_references(@file_name)
|
||||
return_to_last_remembered
|
||||
elsif file_yard.has_file?(@file_name)
|
||||
send_file(file_yard.file_path(@file_name))
|
||||
else
|
||||
logger.debug("File not found: #{file_yard.files_path}/#{@file_name}")
|
||||
# go to the template, which is a file upload form
|
||||
end
|
||||
end
|
||||
|
||||
def cancel_upload
|
||||
return_to_last_remembered
|
||||
end
|
||||
|
||||
def pic
|
||||
check_path
|
||||
if @params['file']
|
||||
# form supplied
|
||||
file_yard.upload_file(@file_name, @params['file'])
|
||||
flash[:info] = "Image '#{@file_name}' successfully uploaded"
|
||||
@web.refresh_pages_with_references(@file_name)
|
||||
return_to_last_remembered
|
||||
elsif file_yard.has_file?(@file_name)
|
||||
send_file(file_yard.file_path(@file_name))
|
||||
else
|
||||
logger.debug("Image not found: #{file_yard.files_path}/#{@file_name}")
|
||||
render_action 'file'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
|
||||
def check_allow_uploads
|
||||
unless @web.allow_uploads
|
||||
render_text 'File uploads are blocked by the webmaster', '403 Forbidden'
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def check_path
|
||||
raise Instiki::ValidationError.new("Invalid path: no file name") unless @file_name
|
||||
raise Instiki::ValidationError.new("Invalid path: no web name") unless @web_name
|
||||
raise Instiki::ValidationError.new("Invalid path: unknown web name") unless @web
|
||||
end
|
||||
|
||||
def file_yard
|
||||
@wiki.file_yard(@web)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
766
app/controllers/wiki_controller.rb
Executable file → Normal file
766
app/controllers/wiki_controller.rb
Executable file → Normal file
|
@ -1,383 +1,383 @@
|
|||
require 'application'
|
||||
require 'fileutils'
|
||||
require 'redcloth_for_tex'
|
||||
|
||||
class WikiController < ApplicationController
|
||||
|
||||
layout 'default', :except => [:rss_feed, :rss_with_headlines, :tex, :export_tex, :export_html]
|
||||
|
||||
def index
|
||||
if @web_name
|
||||
redirect_show 'HomePage'
|
||||
elsif not @wiki.setup?
|
||||
redirect_to :action => 'new_system'
|
||||
elsif @wiki.webs.length == 1
|
||||
redirect_show 'HomePage', @wiki.webs.values.first.address
|
||||
else
|
||||
redirect_to :action => 'web_list'
|
||||
end
|
||||
end
|
||||
|
||||
# Administrating the Instiki setup --------------------------------------------
|
||||
|
||||
def create_system
|
||||
@wiki.setup(@params['password'], @params['web_name'], @params['web_address']) unless @wiki.setup?
|
||||
redirect_show('HomePage', @params['web_address'])
|
||||
end
|
||||
|
||||
def create_web
|
||||
if @wiki.authenticate(@params['system_password'])
|
||||
@wiki.create_web(@params['name'], @params['address'])
|
||||
redirect_show('HomePage', @params['address'])
|
||||
else
|
||||
redirect_to :action => 'index'
|
||||
end
|
||||
end
|
||||
|
||||
def edit_web
|
||||
# to template
|
||||
end
|
||||
|
||||
def new_system
|
||||
redirect_to(:action => 'index') if wiki.setup?
|
||||
# otherwise, to template
|
||||
end
|
||||
|
||||
def new_web
|
||||
redirect_to :action => 'index' if wiki.system['password'].nil?
|
||||
# otherwise, to template
|
||||
end
|
||||
|
||||
|
||||
# Outside a single web --------------------------------------------------------
|
||||
|
||||
def authenticate
|
||||
if password_check(@params['password'])
|
||||
redirect_show('HomePage')
|
||||
else
|
||||
redirect_to :action => 'login'
|
||||
end
|
||||
end
|
||||
|
||||
def login
|
||||
# to template
|
||||
end
|
||||
|
||||
def web_list
|
||||
@webs = wiki.webs.values.sort_by { |web| web.name }
|
||||
end
|
||||
|
||||
|
||||
# Within a single web ---------------------------------------------------------
|
||||
|
||||
def authors
|
||||
@authors = @web.select.authors
|
||||
end
|
||||
|
||||
def export_html
|
||||
export_pages_as_zip('html') { |page| @page = page; render_to_string 'wiki/print' }
|
||||
end
|
||||
|
||||
def export_markup
|
||||
export_pages_as_zip(@web.markup) { |page| page.content }
|
||||
end
|
||||
|
||||
def export_pdf
|
||||
file_name = "#{@web.address}-tex-#{@web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')}"
|
||||
file_path = @wiki.storage_path + file_name
|
||||
|
||||
export_web_to_tex "#{file_path}.tex" unless FileTest.exists? "#{file_path}.tex"
|
||||
convert_tex_to_pdf "#{file_path}.tex"
|
||||
send_file "#{file_path}.pdf"
|
||||
end
|
||||
|
||||
def export_tex
|
||||
file_name = "#{@web.address}-tex-#{@web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')}.tex"
|
||||
file_path = @wiki.storage_path + file_name
|
||||
|
||||
export_web_to_tex(file_path) unless FileTest.exists?(file_path)
|
||||
send_file file_path
|
||||
end
|
||||
|
||||
def feeds
|
||||
# to template
|
||||
end
|
||||
|
||||
def list
|
||||
parse_category
|
||||
@pages_by_name = @pages_in_category.by_name
|
||||
@page_names_that_are_wanted = @pages_in_category.wanted_pages
|
||||
@pages_that_are_orphaned = @pages_in_category.orphaned_pages
|
||||
end
|
||||
|
||||
def recently_revised
|
||||
parse_category
|
||||
@pages_by_revision = @pages_in_category.by_revision
|
||||
end
|
||||
|
||||
def remove_orphaned_pages
|
||||
if wiki.authenticate(@params['system_password'])
|
||||
wiki.remove_orphaned_pages(@web_name)
|
||||
redirect_to :action => 'list'
|
||||
else
|
||||
redirect_show 'HomePage'
|
||||
end
|
||||
end
|
||||
|
||||
def rss_with_content
|
||||
render_rss
|
||||
end
|
||||
|
||||
def rss_with_headlines
|
||||
render_rss(hide_description = true)
|
||||
end
|
||||
|
||||
def search
|
||||
@query = @params['query']
|
||||
@results = @web.select { |page| page.content =~ /#{@query}/i }.sort
|
||||
redirect_show(@results.first.name) if @results.length == 1
|
||||
end
|
||||
|
||||
def update_web
|
||||
if wiki.authenticate(@params['system_password'])
|
||||
wiki.update_web(
|
||||
@web.address, @params['address'], @params['name'],
|
||||
@params['markup'].intern,
|
||||
@params['color'], @params['additional_style'],
|
||||
@params['safe_mode'] ? true : false,
|
||||
@params['password'].empty? ? nil : @params['password'],
|
||||
@params['published'] ? true : false,
|
||||
@params['brackets_only'] ? true : false,
|
||||
@params['count_pages'] ? true : false,
|
||||
@params['allow_uploads'] ? true : false
|
||||
)
|
||||
redirect_show('HomePage', @params['address'])
|
||||
else
|
||||
redirect_show('HomePage')
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Within a single page --------------------------------------------------------
|
||||
|
||||
def cancel_edit
|
||||
@page.unlock
|
||||
redirect_show
|
||||
end
|
||||
|
||||
def edit
|
||||
if @page.nil?
|
||||
redirect_to :action => 'index'
|
||||
elsif @page.locked?(Time.now) and not @params['break_lock']
|
||||
redirect_to :web => @web_name, :action => 'locked', :id => @page_name
|
||||
else
|
||||
@page.lock(Time.now, @author)
|
||||
end
|
||||
end
|
||||
|
||||
def locked
|
||||
# to template
|
||||
end
|
||||
|
||||
def new
|
||||
# to template
|
||||
end
|
||||
|
||||
def pdf
|
||||
page = wiki.read_page(@web_name, @page_name)
|
||||
safe_page_name = @page.name.gsub(/\W/, '')
|
||||
file_name = "#{safe_page_name}-#{@web.address}-#{@page.created_at.strftime('%Y-%m-%d-%H-%M-%S')}"
|
||||
file_path = @wiki.storage_path + file_name
|
||||
|
||||
export_page_to_tex("#{file_path}.tex") unless FileTest.exists?("#{file_path}.tex")
|
||||
# NB: this is _very_ slow
|
||||
convert_tex_to_pdf("#{file_path}.tex")
|
||||
send_file "#{file_path}.pdf"
|
||||
end
|
||||
|
||||
def print
|
||||
# to template
|
||||
end
|
||||
|
||||
def published
|
||||
if @web.published
|
||||
@page = wiki.read_page(@web_name, @page_name || 'HomePage')
|
||||
else
|
||||
redirect_show('HomePage')
|
||||
end
|
||||
end
|
||||
|
||||
def revision
|
||||
get_page_and_revision
|
||||
end
|
||||
|
||||
def rollback
|
||||
get_page_and_revision
|
||||
end
|
||||
|
||||
def save
|
||||
redirect_to :action => 'index' if @page_name.nil?
|
||||
cookies['author'] = @params['author']
|
||||
|
||||
begin
|
||||
page = @web.pages[@page_name]
|
||||
if @web.pages[@page_name]
|
||||
wiki.revise_page(
|
||||
@web_name, @page_name, @params['content'], Time.now,
|
||||
Author.new(@params['author'], remote_ip)
|
||||
)
|
||||
page.unlock
|
||||
else
|
||||
wiki.write_page(
|
||||
@web_name, @page_name, @params['content'], Time.now,
|
||||
Author.new(@params['author'], remote_ip)
|
||||
)
|
||||
end
|
||||
redirect_show(@page_name)
|
||||
rescue Instiki::ValidationError => e
|
||||
page.unlock if defined? page
|
||||
flash[:error] = e
|
||||
return_to_last_remembered
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
if @page
|
||||
begin
|
||||
render_action 'page'
|
||||
# TODO this rescue should differentiate between errors due to rendering and errors in
|
||||
# the application itself (for application errors, it's better not to rescue the error at all)
|
||||
rescue => e
|
||||
logger.error e
|
||||
if in_a_web?
|
||||
redirect_to :web => @web_name, :action => 'edit',
|
||||
:action_suffix => "#{CGI.escape(@page_name)}?msg=#{CGI.escape(e.message)}"
|
||||
else
|
||||
raise e
|
||||
end
|
||||
end
|
||||
else
|
||||
redirect_to :web => @web_name, :action => 'new', :id => CGI.escape(@page_name)
|
||||
end
|
||||
end
|
||||
|
||||
def tex
|
||||
@tex_content = RedClothForTex.new(@page.content).to_tex
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def convert_tex_to_pdf(tex_path)
|
||||
# TODO remove earlier PDF files with the same prefix
|
||||
# TODO handle gracefully situation where pdflatex is not available
|
||||
logger.info `pdflatex --interaction=nonstopmode --output-directory #{File.dirname(tex_path)} #{File.basename(tex_path)}`
|
||||
end
|
||||
|
||||
def export_page_to_tex(file_path)
|
||||
tex
|
||||
File.open(file_path, 'w') { |f| f.write(render_to_string('wiki/tex')) }
|
||||
end
|
||||
|
||||
def export_pages_as_zip(file_type, &block)
|
||||
|
||||
file_prefix = "#{@web.address}-#{file_type}-"
|
||||
timestamp = @web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')
|
||||
file_path = @wiki.storage_path + file_prefix + timestamp + '.zip'
|
||||
tmp_path = "#{file_path}.tmp"
|
||||
|
||||
Zip::ZipOutputStream.open(tmp_path) do |zip_out|
|
||||
@web.select.by_name.each do |page|
|
||||
zip_out.put_next_entry("#{page.name}.#{file_type}")
|
||||
zip_out.puts(block.call(page))
|
||||
end
|
||||
# add an index file, if exporting to HTML
|
||||
if file_type.to_s.downcase == 'html'
|
||||
zip_out.put_next_entry 'index.html'
|
||||
zip_out.puts <<-EOL
|
||||
<html>
|
||||
<head>
|
||||
<META HTTP-EQUIV="Refresh" CONTENT="0;URL=HomePage.#{file_type}">
|
||||
</head>
|
||||
</html>
|
||||
EOL
|
||||
end
|
||||
end
|
||||
FileUtils.rm_rf(Dir[@wiki.storage_path + file_prefix + '*.zip'])
|
||||
FileUtils.mv(tmp_path, file_path)
|
||||
send_file file_path
|
||||
end
|
||||
|
||||
def export_web_to_tex(file_path)
|
||||
@tex_content = table_of_contents(@web.pages['HomePage'].content.dup, render_tex_web)
|
||||
File.open(file_path, 'w') { |f| f.write(render_to_string('wiki/tex_web')) }
|
||||
end
|
||||
|
||||
def get_page_and_revision
|
||||
@revision = @page.revisions[@params['rev'].to_i]
|
||||
end
|
||||
|
||||
def parse_category
|
||||
@categories = @web.categories
|
||||
@category = @params['category']
|
||||
if @categories.include?(@category)
|
||||
@pages_in_category = @web.select { |page| page.in_category?(@category) }
|
||||
@set_name = "category '#{@category}'"
|
||||
else
|
||||
@pages_in_category = PageSet.new(@web).by_name
|
||||
@set_name = 'the web'
|
||||
end
|
||||
@category_links = @categories.map { |c|
|
||||
if @category == c
|
||||
%{<span class="selected">#{c}</span>}
|
||||
else
|
||||
%{<a href="?category=#{c}">#{c}</a>}
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def password_check(password)
|
||||
if password == @web.password
|
||||
cookies['web_address'] = password
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def redirect_show(page_name = @page_name, web = @web_name)
|
||||
redirect_to :web => web, :action => 'show', :id => CGI.escape(page_name)
|
||||
end
|
||||
|
||||
def remote_ip
|
||||
ip = @request.remote_ip
|
||||
logger.info(ip)
|
||||
ip
|
||||
end
|
||||
|
||||
def render_rss(hide_description = false)
|
||||
@pages_by_revision = @web.select.by_revision.first(15)
|
||||
@hide_description = hide_description
|
||||
@response.headers['Content-Type'] = 'text/xml'
|
||||
render 'wiki/rss_feed'
|
||||
end
|
||||
|
||||
def render_tex_web
|
||||
@web.select.by_name.inject({}) do |tex_web, page|
|
||||
tex_web[page.name] = RedClothForTex.new(page.content).to_tex
|
||||
tex_web
|
||||
end
|
||||
end
|
||||
|
||||
def render_to_string(template_name)
|
||||
add_variables_to_assigns
|
||||
render template_name
|
||||
@performed_render = false
|
||||
@template.render_file(template_name)
|
||||
end
|
||||
|
||||
def truncate(text, length = 30, truncate_string = '...')
|
||||
if text.length > length then text[0..(length - 3)] + truncate_string else text end
|
||||
end
|
||||
|
||||
end
|
||||
require 'application'
|
||||
require 'fileutils'
|
||||
require 'redcloth_for_tex'
|
||||
|
||||
class WikiController < ApplicationController
|
||||
|
||||
layout 'default', :except => [:rss_feed, :rss_with_headlines, :tex, :export_tex, :export_html]
|
||||
|
||||
def index
|
||||
if @web_name
|
||||
redirect_show 'HomePage'
|
||||
elsif not @wiki.setup?
|
||||
redirect_to :action => 'new_system'
|
||||
elsif @wiki.webs.length == 1
|
||||
redirect_show 'HomePage', @wiki.webs.values.first.address
|
||||
else
|
||||
redirect_to :action => 'web_list'
|
||||
end
|
||||
end
|
||||
|
||||
# Administrating the Instiki setup --------------------------------------------
|
||||
|
||||
def create_system
|
||||
@wiki.setup(@params['password'], @params['web_name'], @params['web_address']) unless @wiki.setup?
|
||||
redirect_show('HomePage', @params['web_address'])
|
||||
end
|
||||
|
||||
def create_web
|
||||
if @wiki.authenticate(@params['system_password'])
|
||||
@wiki.create_web(@params['name'], @params['address'])
|
||||
redirect_show('HomePage', @params['address'])
|
||||
else
|
||||
redirect_to :action => 'index'
|
||||
end
|
||||
end
|
||||
|
||||
def edit_web
|
||||
# to template
|
||||
end
|
||||
|
||||
def new_system
|
||||
redirect_to(:action => 'index') if wiki.setup?
|
||||
# otherwise, to template
|
||||
end
|
||||
|
||||
def new_web
|
||||
redirect_to :action => 'index' if wiki.system['password'].nil?
|
||||
# otherwise, to template
|
||||
end
|
||||
|
||||
|
||||
# Outside a single web --------------------------------------------------------
|
||||
|
||||
def authenticate
|
||||
if password_check(@params['password'])
|
||||
redirect_show('HomePage')
|
||||
else
|
||||
redirect_to :action => 'login'
|
||||
end
|
||||
end
|
||||
|
||||
def login
|
||||
# to template
|
||||
end
|
||||
|
||||
def web_list
|
||||
@webs = wiki.webs.values.sort_by { |web| web.name }
|
||||
end
|
||||
|
||||
|
||||
# Within a single web ---------------------------------------------------------
|
||||
|
||||
def authors
|
||||
@authors = @web.select.authors
|
||||
end
|
||||
|
||||
def export_html
|
||||
export_pages_as_zip('html') { |page| @page = page; render_to_string 'wiki/print' }
|
||||
end
|
||||
|
||||
def export_markup
|
||||
export_pages_as_zip(@web.markup) { |page| page.content }
|
||||
end
|
||||
|
||||
def export_pdf
|
||||
file_name = "#{@web.address}-tex-#{@web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')}"
|
||||
file_path = @wiki.storage_path + file_name
|
||||
|
||||
export_web_to_tex "#{file_path}.tex" unless FileTest.exists? "#{file_path}.tex"
|
||||
convert_tex_to_pdf "#{file_path}.tex"
|
||||
send_file "#{file_path}.pdf"
|
||||
end
|
||||
|
||||
def export_tex
|
||||
file_name = "#{@web.address}-tex-#{@web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')}.tex"
|
||||
file_path = @wiki.storage_path + file_name
|
||||
|
||||
export_web_to_tex(file_path) unless FileTest.exists?(file_path)
|
||||
send_file file_path
|
||||
end
|
||||
|
||||
def feeds
|
||||
# to template
|
||||
end
|
||||
|
||||
def list
|
||||
parse_category
|
||||
@pages_by_name = @pages_in_category.by_name
|
||||
@page_names_that_are_wanted = @pages_in_category.wanted_pages
|
||||
@pages_that_are_orphaned = @pages_in_category.orphaned_pages
|
||||
end
|
||||
|
||||
def recently_revised
|
||||
parse_category
|
||||
@pages_by_revision = @pages_in_category.by_revision
|
||||
end
|
||||
|
||||
def remove_orphaned_pages
|
||||
if wiki.authenticate(@params['system_password'])
|
||||
wiki.remove_orphaned_pages(@web_name)
|
||||
redirect_to :action => 'list'
|
||||
else
|
||||
redirect_show 'HomePage'
|
||||
end
|
||||
end
|
||||
|
||||
def rss_with_content
|
||||
render_rss
|
||||
end
|
||||
|
||||
def rss_with_headlines
|
||||
render_rss(hide_description = true)
|
||||
end
|
||||
|
||||
def search
|
||||
@query = @params['query']
|
||||
@results = @web.select { |page| page.content =~ /#{@query}/i }.sort
|
||||
redirect_show(@results.first.name) if @results.length == 1
|
||||
end
|
||||
|
||||
def update_web
|
||||
if wiki.authenticate(@params['system_password'])
|
||||
wiki.update_web(
|
||||
@web.address, @params['address'], @params['name'],
|
||||
@params['markup'].intern,
|
||||
@params['color'], @params['additional_style'],
|
||||
@params['safe_mode'] ? true : false,
|
||||
@params['password'].empty? ? nil : @params['password'],
|
||||
@params['published'] ? true : false,
|
||||
@params['brackets_only'] ? true : false,
|
||||
@params['count_pages'] ? true : false,
|
||||
@params['allow_uploads'] ? true : false
|
||||
)
|
||||
redirect_show('HomePage', @params['address'])
|
||||
else
|
||||
redirect_show('HomePage')
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Within a single page --------------------------------------------------------
|
||||
|
||||
def cancel_edit
|
||||
@page.unlock
|
||||
redirect_show
|
||||
end
|
||||
|
||||
def edit
|
||||
if @page.nil?
|
||||
redirect_to :action => 'index'
|
||||
elsif @page.locked?(Time.now) and not @params['break_lock']
|
||||
redirect_to :web => @web_name, :action => 'locked', :id => @page_name
|
||||
else
|
||||
@page.lock(Time.now, @author)
|
||||
end
|
||||
end
|
||||
|
||||
def locked
|
||||
# to template
|
||||
end
|
||||
|
||||
def new
|
||||
# to template
|
||||
end
|
||||
|
||||
def pdf
|
||||
page = wiki.read_page(@web_name, @page_name)
|
||||
safe_page_name = @page.name.gsub(/\W/, '')
|
||||
file_name = "#{safe_page_name}-#{@web.address}-#{@page.created_at.strftime('%Y-%m-%d-%H-%M-%S')}"
|
||||
file_path = @wiki.storage_path + file_name
|
||||
|
||||
export_page_to_tex("#{file_path}.tex") unless FileTest.exists?("#{file_path}.tex")
|
||||
# NB: this is _very_ slow
|
||||
convert_tex_to_pdf("#{file_path}.tex")
|
||||
send_file "#{file_path}.pdf"
|
||||
end
|
||||
|
||||
def print
|
||||
# to template
|
||||
end
|
||||
|
||||
def published
|
||||
if @web.published
|
||||
@page = wiki.read_page(@web_name, @page_name || 'HomePage')
|
||||
else
|
||||
redirect_show('HomePage')
|
||||
end
|
||||
end
|
||||
|
||||
def revision
|
||||
get_page_and_revision
|
||||
end
|
||||
|
||||
def rollback
|
||||
get_page_and_revision
|
||||
end
|
||||
|
||||
def save
|
||||
redirect_to :action => 'index' if @page_name.nil?
|
||||
cookies['author'] = @params['author']
|
||||
|
||||
begin
|
||||
page = @web.pages[@page_name]
|
||||
if @web.pages[@page_name]
|
||||
wiki.revise_page(
|
||||
@web_name, @page_name, @params['content'], Time.now,
|
||||
Author.new(@params['author'], remote_ip)
|
||||
)
|
||||
page.unlock
|
||||
else
|
||||
wiki.write_page(
|
||||
@web_name, @page_name, @params['content'], Time.now,
|
||||
Author.new(@params['author'], remote_ip)
|
||||
)
|
||||
end
|
||||
redirect_show(@page_name)
|
||||
rescue Instiki::ValidationError => e
|
||||
page.unlock if defined? page
|
||||
flash[:error] = e
|
||||
return_to_last_remembered
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
if @page
|
||||
begin
|
||||
render_action 'page'
|
||||
# TODO this rescue should differentiate between errors due to rendering and errors in
|
||||
# the application itself (for application errors, it's better not to rescue the error at all)
|
||||
rescue => e
|
||||
logger.error e
|
||||
if in_a_web?
|
||||
redirect_to :web => @web_name, :action => 'edit',
|
||||
:action_suffix => "#{CGI.escape(@page_name)}?msg=#{CGI.escape(e.message)}"
|
||||
else
|
||||
raise e
|
||||
end
|
||||
end
|
||||
else
|
||||
redirect_to :web => @web_name, :action => 'new', :id => CGI.escape(@page_name)
|
||||
end
|
||||
end
|
||||
|
||||
def tex
|
||||
@tex_content = RedClothForTex.new(@page.content).to_tex
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def convert_tex_to_pdf(tex_path)
|
||||
# TODO remove earlier PDF files with the same prefix
|
||||
# TODO handle gracefully situation where pdflatex is not available
|
||||
logger.info `pdflatex --interaction=nonstopmode --output-directory #{File.dirname(tex_path)} #{File.basename(tex_path)}`
|
||||
end
|
||||
|
||||
def export_page_to_tex(file_path)
|
||||
tex
|
||||
File.open(file_path, 'w') { |f| f.write(render_to_string('wiki/tex')) }
|
||||
end
|
||||
|
||||
def export_pages_as_zip(file_type, &block)
|
||||
|
||||
file_prefix = "#{@web.address}-#{file_type}-"
|
||||
timestamp = @web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')
|
||||
file_path = @wiki.storage_path + file_prefix + timestamp + '.zip'
|
||||
tmp_path = "#{file_path}.tmp"
|
||||
|
||||
Zip::ZipOutputStream.open(tmp_path) do |zip_out|
|
||||
@web.select.by_name.each do |page|
|
||||
zip_out.put_next_entry("#{page.name}.#{file_type}")
|
||||
zip_out.puts(block.call(page))
|
||||
end
|
||||
# add an index file, if exporting to HTML
|
||||
if file_type.to_s.downcase == 'html'
|
||||
zip_out.put_next_entry 'index.html'
|
||||
zip_out.puts <<-EOL
|
||||
<html>
|
||||
<head>
|
||||
<META HTTP-EQUIV="Refresh" CONTENT="0;URL=HomePage.#{file_type}">
|
||||
</head>
|
||||
</html>
|
||||
EOL
|
||||
end
|
||||
end
|
||||
FileUtils.rm_rf(Dir[@wiki.storage_path + file_prefix + '*.zip'])
|
||||
FileUtils.mv(tmp_path, file_path)
|
||||
send_file file_path
|
||||
end
|
||||
|
||||
def export_web_to_tex(file_path)
|
||||
@tex_content = table_of_contents(@web.pages['HomePage'].content.dup, render_tex_web)
|
||||
File.open(file_path, 'w') { |f| f.write(render_to_string('wiki/tex_web')) }
|
||||
end
|
||||
|
||||
def get_page_and_revision
|
||||
@revision = @page.revisions[@params['rev'].to_i]
|
||||
end
|
||||
|
||||
def parse_category
|
||||
@categories = @web.categories
|
||||
@category = @params['category']
|
||||
if @categories.include?(@category)
|
||||
@pages_in_category = @web.select { |page| page.in_category?(@category) }
|
||||
@set_name = "category '#{@category}'"
|
||||
else
|
||||
@pages_in_category = PageSet.new(@web).by_name
|
||||
@set_name = 'the web'
|
||||
end
|
||||
@category_links = @categories.map { |c|
|
||||
if @category == c
|
||||
%{<span class="selected">#{c}</span>}
|
||||
else
|
||||
%{<a href="?category=#{c}">#{c}</a>}
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def password_check(password)
|
||||
if password == @web.password
|
||||
cookies['web_address'] = password
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def redirect_show(page_name = @page_name, web = @web_name)
|
||||
redirect_to :web => web, :action => 'show', :id => CGI.escape(page_name)
|
||||
end
|
||||
|
||||
def remote_ip
|
||||
ip = @request.remote_ip
|
||||
logger.info(ip)
|
||||
ip
|
||||
end
|
||||
|
||||
def render_rss(hide_description = false)
|
||||
@pages_by_revision = @web.select.by_revision.first(15)
|
||||
@hide_description = hide_description
|
||||
@response.headers['Content-Type'] = 'text/xml'
|
||||
render 'wiki/rss_feed'
|
||||
end
|
||||
|
||||
def render_tex_web
|
||||
@web.select.by_name.inject({}) do |tex_web, page|
|
||||
tex_web[page.name] = RedClothForTex.new(page.content).to_tex
|
||||
tex_web
|
||||
end
|
||||
end
|
||||
|
||||
def render_to_string(template_name)
|
||||
add_variables_to_assigns
|
||||
render template_name
|
||||
@performed_render = false
|
||||
@template.render_file(template_name)
|
||||
end
|
||||
|
||||
def truncate(text, length = 30, truncate_string = '...')
|
||||
if text.length > length then text[0..(length - 3)] + truncate_string else text end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
6
app/models/author.rb
Executable file → Normal file
6
app/models/author.rb
Executable file → Normal file
|
@ -1,4 +1,4 @@
|
|||
class Author < String
|
||||
attr_accessor :ip
|
||||
def initialize(name, ip) @ip = ip; super(name) end
|
||||
class Author < String
|
||||
attr_accessor :ip
|
||||
def initialize(name, ip) @ip = ip; super(name) end
|
||||
end
|
70
app/models/chunks/category.rb
Executable file → Normal file
70
app/models/chunks/category.rb
Executable file → Normal file
|
@ -1,35 +1,35 @@
|
|||
require 'chunks/chunk'
|
||||
|
||||
# The category chunk looks for "category: news" on a line by
|
||||
# itself and parses the terms after the ':' as categories.
|
||||
# Other classes can search for Category chunks within
|
||||
# rendered content to find out what categories this page
|
||||
# should be in.
|
||||
#
|
||||
# Category lines can be hidden using ':category: news', for example
|
||||
class Category < Chunk::Abstract
|
||||
def self.pattern() return /^(:)?category\s*:(.*)$/i end
|
||||
|
||||
attr_reader :hidden, :list
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@hidden = match_data[1]
|
||||
@list = match_data[2].split(',').map { |c| c.strip }
|
||||
end
|
||||
|
||||
# If the chunk is hidden, erase the mask and return this chunk
|
||||
# otherwise, surround it with a 'div' block.
|
||||
def unmask(content)
|
||||
return '' if hidden
|
||||
|
||||
category_urls = @list.map{|category| url(category) }.join(', ')
|
||||
replacement = '<div class="property"> category: ' + category_urls + '</div>'
|
||||
self if content.sub!(mask(content), replacement)
|
||||
end
|
||||
|
||||
# TODO move presentation of page metadata to controller/view
|
||||
def url(category)
|
||||
%{<a class="category_link" href="../list/?category=#{category}">#{category}</a>}
|
||||
end
|
||||
end
|
||||
require 'chunks/chunk'
|
||||
|
||||
# The category chunk looks for "category: news" on a line by
|
||||
# itself and parses the terms after the ':' as categories.
|
||||
# Other classes can search for Category chunks within
|
||||
# rendered content to find out what categories this page
|
||||
# should be in.
|
||||
#
|
||||
# Category lines can be hidden using ':category: news', for example
|
||||
class Category < Chunk::Abstract
|
||||
def self.pattern() return /^(:)?category\s*:(.*)$/i end
|
||||
|
||||
attr_reader :hidden, :list
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@hidden = match_data[1]
|
||||
@list = match_data[2].split(',').map { |c| c.strip }
|
||||
end
|
||||
|
||||
# If the chunk is hidden, erase the mask and return this chunk
|
||||
# otherwise, surround it with a 'div' block.
|
||||
def unmask(content)
|
||||
return '' if hidden
|
||||
|
||||
category_urls = @list.map{|category| url(category) }.join(', ')
|
||||
replacement = '<div class="property"> category: ' + category_urls + '</div>'
|
||||
self if content.sub!(mask(content), replacement)
|
||||
end
|
||||
|
||||
# TODO move presentation of page metadata to controller/view
|
||||
def url(category)
|
||||
%{<a class="category_link" href="../list/?category=#{category}">#{category}</a>}
|
||||
end
|
||||
end
|
||||
|
|
80
app/models/chunks/chunk.rb
Executable file → Normal file
80
app/models/chunks/chunk.rb
Executable file → Normal file
|
@ -1,40 +1,40 @@
|
|||
require 'digest/md5'
|
||||
require 'uri/common'
|
||||
|
||||
# A chunk is a pattern of text that can be protected
|
||||
# and interrogated by a renderer. Each Chunk class has a
|
||||
# +pattern+ that states what sort of text it matches.
|
||||
# Chunks are initalized by passing in the result of a
|
||||
# match by its pattern.
|
||||
module Chunk
|
||||
class Abstract
|
||||
attr_reader :text
|
||||
|
||||
def initialize(match_data) @text = match_data[0] end
|
||||
|
||||
# Find all the chunks of the given type in content
|
||||
# Each time the pattern is matched, create a new
|
||||
# chunk for it, and replace the occurance of the chunk
|
||||
# in this content with its mask.
|
||||
def self.apply_to(content)
|
||||
content.gsub!( self.pattern ) do |match|
|
||||
new_chunk = self.new($~)
|
||||
content.chunks << new_chunk
|
||||
new_chunk.mask(content)
|
||||
end
|
||||
end
|
||||
|
||||
def mask(content)
|
||||
"chunk#{self.object_id}#{self.class.to_s.delete(':').downcase}chunk"
|
||||
end
|
||||
|
||||
def revert(content)
|
||||
content.sub!( Regexp.new(mask(content)), text )
|
||||
end
|
||||
|
||||
def unmask(content)
|
||||
self if revert(content)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
require 'digest/md5'
|
||||
require 'uri/common'
|
||||
|
||||
# A chunk is a pattern of text that can be protected
|
||||
# and interrogated by a renderer. Each Chunk class has a
|
||||
# +pattern+ that states what sort of text it matches.
|
||||
# Chunks are initalized by passing in the result of a
|
||||
# match by its pattern.
|
||||
module Chunk
|
||||
class Abstract
|
||||
attr_reader :text
|
||||
|
||||
def initialize(match_data) @text = match_data[0] end
|
||||
|
||||
# Find all the chunks of the given type in content
|
||||
# Each time the pattern is matched, create a new
|
||||
# chunk for it, and replace the occurance of the chunk
|
||||
# in this content with its mask.
|
||||
def self.apply_to(content)
|
||||
content.gsub!( self.pattern ) do |match|
|
||||
new_chunk = self.new($~)
|
||||
content.chunks << new_chunk
|
||||
new_chunk.mask(content)
|
||||
end
|
||||
end
|
||||
|
||||
def mask(content)
|
||||
"chunk#{self.object_id}#{self.class.to_s.delete(':').downcase}chunk"
|
||||
end
|
||||
|
||||
def revert(content)
|
||||
content.sub!( Regexp.new(mask(content)), text )
|
||||
end
|
||||
|
||||
def unmask(content)
|
||||
self if revert(content)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
76
app/models/chunks/engines.rb
Executable file → Normal file
76
app/models/chunks/engines.rb
Executable file → Normal file
|
@ -1,38 +1,38 @@
|
|||
$: << File.dirname(__FILE__) + "../../libraries"
|
||||
|
||||
require 'redcloth'
|
||||
require 'bluecloth'
|
||||
require 'rdocsupport'
|
||||
require 'chunks/chunk'
|
||||
|
||||
# The markup engines are Chunks that call the one of RedCloth, BlueCloth
|
||||
# or RDoc to convert text. This markup occurs when the chunk is required
|
||||
# to mask itself.
|
||||
module Engines
|
||||
class Textile < Chunk::Abstract
|
||||
def self.pattern() /^(.*)$/m end
|
||||
def mask(content)
|
||||
RedCloth.new(text,content.options[:engine_opts]).to_html
|
||||
end
|
||||
def unmask(content) self end
|
||||
end
|
||||
|
||||
class Markdown < Chunk::Abstract
|
||||
def self.pattern() /^(.*)$/m end
|
||||
def mask(content)
|
||||
BlueCloth.new(text,content.options[:engine_opts]).to_html
|
||||
end
|
||||
def unmask(content) self end
|
||||
end
|
||||
|
||||
class RDoc < Chunk::Abstract
|
||||
def self.pattern() /^(.*)$/m end
|
||||
def mask(content)
|
||||
RDocSupport::RDocFormatter.new(text).to_html
|
||||
end
|
||||
def unmask(content) self end
|
||||
end
|
||||
|
||||
MAP = { :textile => Textile, :markdown => Markdown, :rdoc => RDoc }
|
||||
end
|
||||
|
||||
$: << File.dirname(__FILE__) + "../../libraries"
|
||||
|
||||
require 'redcloth'
|
||||
require 'bluecloth'
|
||||
require 'rdocsupport'
|
||||
require 'chunks/chunk'
|
||||
|
||||
# The markup engines are Chunks that call the one of RedCloth, BlueCloth
|
||||
# or RDoc to convert text. This markup occurs when the chunk is required
|
||||
# to mask itself.
|
||||
module Engines
|
||||
class Textile < Chunk::Abstract
|
||||
def self.pattern() /^(.*)$/m end
|
||||
def mask(content)
|
||||
RedCloth.new(text,content.options[:engine_opts]).to_html
|
||||
end
|
||||
def unmask(content) self end
|
||||
end
|
||||
|
||||
class Markdown < Chunk::Abstract
|
||||
def self.pattern() /^(.*)$/m end
|
||||
def mask(content)
|
||||
BlueCloth.new(text,content.options[:engine_opts]).to_html
|
||||
end
|
||||
def unmask(content) self end
|
||||
end
|
||||
|
||||
class RDoc < Chunk::Abstract
|
||||
def self.pattern() /^(.*)$/m end
|
||||
def mask(content)
|
||||
RDocSupport::RDocFormatter.new(text).to_html
|
||||
end
|
||||
def unmask(content) self end
|
||||
end
|
||||
|
||||
MAP = { :textile => Textile, :markdown => Markdown, :rdoc => RDoc }
|
||||
end
|
||||
|
||||
|
|
58
app/models/chunks/include.rb
Executable file → Normal file
58
app/models/chunks/include.rb
Executable file → Normal file
|
@ -1,29 +1,29 @@
|
|||
require 'chunks/wiki'
|
||||
|
||||
# Includes the contents of another page for rendering.
|
||||
# The include command looks like this: "[[!include PageName]]".
|
||||
# It is a WikiLink since it refers to another page (PageName)
|
||||
# and the wiki content using this command must be notified
|
||||
# of changes to that page.
|
||||
# If the included page could not be found, a warning is displayed.
|
||||
class Include < WikiChunk::WikiLink
|
||||
def self.pattern() /^\[\[!include(.*)\]\]\s*$/i end
|
||||
|
||||
attr_reader :page_name
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@page_name = match_data[1].strip
|
||||
end
|
||||
|
||||
# This replaces the [[!include PageName]] text with
|
||||
# the contents of PageName if it exists. Otherwise
|
||||
# a warning is displayed.
|
||||
def mask(content)
|
||||
page = content.web.pages[page_name]
|
||||
(page ? page.content : "<em>Could not include #{page_name}</em>")
|
||||
end
|
||||
|
||||
# Keep this chunk regardless of what happens.
|
||||
def unmask(content) self end
|
||||
end
|
||||
require 'chunks/wiki'
|
||||
|
||||
# Includes the contents of another page for rendering.
|
||||
# The include command looks like this: "[[!include PageName]]".
|
||||
# It is a WikiLink since it refers to another page (PageName)
|
||||
# and the wiki content using this command must be notified
|
||||
# of changes to that page.
|
||||
# If the included page could not be found, a warning is displayed.
|
||||
class Include < WikiChunk::WikiLink
|
||||
def self.pattern() /^\[\[!include(.*)\]\]\s*$/i end
|
||||
|
||||
attr_reader :page_name
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@page_name = match_data[1].strip
|
||||
end
|
||||
|
||||
# This replaces the [[!include PageName]] text with
|
||||
# the contents of PageName if it exists. Otherwise
|
||||
# a warning is displayed.
|
||||
def mask(content)
|
||||
page = content.web.pages[page_name]
|
||||
(page ? page.content : "<em>Could not include #{page_name}</em>")
|
||||
end
|
||||
|
||||
# Keep this chunk regardless of what happens.
|
||||
def unmask(content) self end
|
||||
end
|
||||
|
|
38
app/models/chunks/literal.rb
Executable file → Normal file
38
app/models/chunks/literal.rb
Executable file → Normal file
|
@ -1,19 +1,19 @@
|
|||
require 'chunks/chunk'
|
||||
|
||||
# These are basic chunks that have a pattern and can be protected.
|
||||
# They are used by rendering process to prevent wiki rendering
|
||||
# occuring within literal areas such as <code> and <pre> blocks
|
||||
# and within HTML tags.
|
||||
module Literal
|
||||
# A literal chunk that protects 'code' and 'pre' tags from wiki rendering.
|
||||
class Pre < Chunk::Abstract
|
||||
PRE_BLOCKS = "a|pre|code"
|
||||
def self.pattern() Regexp.new('<('+PRE_BLOCKS+')\b[^>]*?>.*?</\1>', Regexp::MULTILINE) end
|
||||
end
|
||||
|
||||
# A literal chunk that protects HTML tags from wiki rendering.
|
||||
class Tags < Chunk::Abstract
|
||||
TAGS = "a|img|em|strong|div|span|table|td|th|ul|ol|li|dl|dt|dd"
|
||||
def self.pattern() Regexp.new('<(?:'+TAGS+')[^>]*?>', Regexp::MULTILINE) end
|
||||
end
|
||||
end
|
||||
require 'chunks/chunk'
|
||||
|
||||
# These are basic chunks that have a pattern and can be protected.
|
||||
# They are used by rendering process to prevent wiki rendering
|
||||
# occuring within literal areas such as <code> and <pre> blocks
|
||||
# and within HTML tags.
|
||||
module Literal
|
||||
# A literal chunk that protects 'code' and 'pre' tags from wiki rendering.
|
||||
class Pre < Chunk::Abstract
|
||||
PRE_BLOCKS = "a|pre|code"
|
||||
def self.pattern() Regexp.new('<('+PRE_BLOCKS+')\b[^>]*?>.*?</\1>', Regexp::MULTILINE) end
|
||||
end
|
||||
|
||||
# A literal chunk that protects HTML tags from wiki rendering.
|
||||
class Tags < Chunk::Abstract
|
||||
TAGS = "a|img|em|strong|div|span|table|td|th|ul|ol|li|dl|dt|dd"
|
||||
def self.pattern() Regexp.new('<(?:'+TAGS+')[^>]*?>', Regexp::MULTILINE) end
|
||||
end
|
||||
end
|
||||
|
|
62
app/models/chunks/nowiki.rb
Executable file → Normal file
62
app/models/chunks/nowiki.rb
Executable file → Normal file
|
@ -1,31 +1,31 @@
|
|||
require 'chunks/chunk'
|
||||
|
||||
# This chunks allows certain parts of a wiki page to be hidden from the
|
||||
# rest of the rendering pipeline. It should be run at the beginning
|
||||
# of the pipeline in `wiki_content.rb`.
|
||||
#
|
||||
# An example use of this chunk is to markup double brackets or
|
||||
# auto URI links:
|
||||
# <nowiki>Here are [[double brackets]] and a URI: www.uri.org</nowiki>
|
||||
#
|
||||
# The contents of the chunks will not be processed by any other chunk
|
||||
# so the `www.uri.org` and the double brackets will appear verbatim.
|
||||
#
|
||||
# Author: Mark Reid <mark at threewordslong dot com>
|
||||
# Created: 8th June 2004
|
||||
class NoWiki < Chunk::Abstract
|
||||
|
||||
def self.pattern() Regexp.new('<nowiki>(.*?)</nowiki>') end
|
||||
|
||||
attr_reader :plain_text
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@plain_text = match_data[1]
|
||||
end
|
||||
|
||||
# The nowiki content is not unmasked. This means the chunk will be reverted
|
||||
# using the plain text.
|
||||
def unmask(content) nil end
|
||||
def revert(content) content.sub!(mask(content), plain_text) end
|
||||
end
|
||||
require 'chunks/chunk'
|
||||
|
||||
# This chunks allows certain parts of a wiki page to be hidden from the
|
||||
# rest of the rendering pipeline. It should be run at the beginning
|
||||
# of the pipeline in `wiki_content.rb`.
|
||||
#
|
||||
# An example use of this chunk is to markup double brackets or
|
||||
# auto URI links:
|
||||
# <nowiki>Here are [[double brackets]] and a URI: www.uri.org</nowiki>
|
||||
#
|
||||
# The contents of the chunks will not be processed by any other chunk
|
||||
# so the `www.uri.org` and the double brackets will appear verbatim.
|
||||
#
|
||||
# Author: Mark Reid <mark at threewordslong dot com>
|
||||
# Created: 8th June 2004
|
||||
class NoWiki < Chunk::Abstract
|
||||
|
||||
def self.pattern() Regexp.new('<nowiki>(.*?)</nowiki>') end
|
||||
|
||||
attr_reader :plain_text
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@plain_text = match_data[1]
|
||||
end
|
||||
|
||||
# The nowiki content is not unmasked. This means the chunk will be reverted
|
||||
# using the plain text.
|
||||
def unmask(content) nil end
|
||||
def revert(content) content.sub!(mask(content), plain_text) end
|
||||
end
|
||||
|
|
36
app/models/chunks/test.rb
Executable file → Normal file
36
app/models/chunks/test.rb
Executable file → Normal file
|
@ -1,18 +1,18 @@
|
|||
require 'test/unit'
|
||||
|
||||
class ChunkTest < Test::Unit::TestCase
|
||||
|
||||
# Asserts a number of tests for the given type and text.
|
||||
def match(type, test_text, expected)
|
||||
pattern = type.pattern
|
||||
assert_match(pattern, test_text)
|
||||
pattern =~ test_text # Previous assertion guarantees match
|
||||
chunk = type.new($~)
|
||||
|
||||
# Test if requested parts are correct.
|
||||
for method_sym, value in expected do
|
||||
assert_respond_to(chunk, method_sym)
|
||||
assert_equal(value, chunk.method(method_sym).call, "Checking value of '#{method_sym}'")
|
||||
end
|
||||
end
|
||||
end
|
||||
require 'test/unit'
|
||||
|
||||
class ChunkTest < Test::Unit::TestCase
|
||||
|
||||
# Asserts a number of tests for the given type and text.
|
||||
def match(type, test_text, expected)
|
||||
pattern = type.pattern
|
||||
assert_match(pattern, test_text)
|
||||
pattern =~ test_text # Previous assertion guarantees match
|
||||
chunk = type.new($~)
|
||||
|
||||
# Test if requested parts are correct.
|
||||
for method_sym, value in expected do
|
||||
assert_respond_to(chunk, method_sym)
|
||||
assert_equal(value, chunk.method(method_sym).call, "Checking value of '#{method_sym}'")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
358
app/models/chunks/uri.rb
Executable file → Normal file
358
app/models/chunks/uri.rb
Executable file → Normal file
|
@ -1,179 +1,179 @@
|
|||
require 'chunks/chunk'
|
||||
|
||||
# This wiki chunk matches arbitrary URIs, using patterns from the Ruby URI modules.
|
||||
# It parses out a variety of fields that could be used by renderers to format
|
||||
# the links in various ways (shortening domain names, hiding email addresses)
|
||||
# It matches email addresses and host.com.au domains without schemes (http://)
|
||||
# but adds these on as required.
|
||||
#
|
||||
# The heuristic used to match a URI is designed to err on the side of caution.
|
||||
# That is, it is more likely to not autolink a URI than it is to accidently
|
||||
# autolink something that is not a URI. The reason behind this is it is easier
|
||||
# to force a URI link by prefixing 'http://' to it than it is to escape and
|
||||
# incorrectly marked up non-URI.
|
||||
#
|
||||
# I'm using a part of the [ISO 3166-1 Standard][iso3166] for country name suffixes.
|
||||
# The generic names are from www.bnoack.com/data/countrycode2.html)
|
||||
# [iso3166]: http://geotags.com/iso3166/
|
||||
class URIChunk < Chunk::Abstract
|
||||
include URI::REGEXP::PATTERN
|
||||
|
||||
# this condition is to get rid of pesky warnings in tests
|
||||
unless defined? URIChunk::INTERNET_URI_REGEXP
|
||||
|
||||
GENERIC = '(?:aero|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org)'
|
||||
COUNTRY = '(?:au|at|be|ca|ch|de|dk|fr|hk|in|ir|it|jp|nl|no|pt|ru|se|sw|tv|tw|uk|us)'
|
||||
|
||||
# These are needed otherwise HOST will match almost anything
|
||||
TLDS = "(?:#{GENERIC}|#{COUNTRY})"
|
||||
|
||||
# Redefine USERINFO so that it must have non-zero length
|
||||
USERINFO = "(?:[#{UNRESERVED};:&=+$,]|#{ESCAPED})+"
|
||||
|
||||
# unreserved_no_ending = alphanum | mark, but URI_ENDING [)!] excluded
|
||||
UNRESERVED_NO_ENDING = "-_.~*'(#{ALNUM}"
|
||||
|
||||
# this ensures that query or fragment do not end with URI_ENDING
|
||||
# and enable us to use a much simpler self.pattern Regexp
|
||||
|
||||
# uric_no_ending = reserved | unreserved_no_ending | escaped
|
||||
URIC_NO_ENDING = "(?:[#{UNRESERVED_NO_ENDING}#{RESERVED}]|#{ESCAPED})"
|
||||
# query = *uric
|
||||
QUERY = "#{URIC_NO_ENDING}*"
|
||||
# fragment = *uric
|
||||
FRAGMENT = "#{URIC_NO_ENDING}*"
|
||||
|
||||
# DOMLABEL is defined in the ruby uri library, TLDS is defined above
|
||||
INTERNET_HOSTNAME = "(?:#{DOMLABEL}\\.)+#{TLDS}"
|
||||
|
||||
# Correct a typo bug in ruby 1.8.x lib/uri/common.rb
|
||||
PORT = '\\d*'
|
||||
|
||||
INTERNET_URI =
|
||||
"(?:(#{SCHEME}):/{0,2})?" + # Optional scheme: (\1)
|
||||
"(?:(#{USERINFO})@)?" + # Optional userinfo@ (\2)
|
||||
"(#{INTERNET_HOSTNAME})" + # Mandatory hostname (\3)
|
||||
"(?::(#{PORT}))?" + # Optional :port (\4)
|
||||
"(#{ABS_PATH})?" + # Optional absolute path (\5)
|
||||
"(?:\\?(#{QUERY}))?" + # Optional ?query (\6)
|
||||
"(?:\\#(#{FRAGMENT}))?" # Optional #fragment (\7)
|
||||
|
||||
TEXTILE_SYNTAX_PREFIX = '(!)?'
|
||||
|
||||
INTERNET_URI_REGEXP = Regexp.new(TEXTILE_SYNTAX_PREFIX + INTERNET_URI, Regexp::EXTENDED, 'N')
|
||||
|
||||
end
|
||||
|
||||
def URIChunk.pattern
|
||||
INTERNET_URI_REGEXP
|
||||
end
|
||||
|
||||
attr_reader :user, :host, :port, :path, :query, :fragment, :link_text
|
||||
|
||||
def self.apply_to(content)
|
||||
content.gsub!( self.pattern ) do |matched_text|
|
||||
chunk = self.new($~)
|
||||
if chunk.textile_url? or chunk.textile_image?
|
||||
# do not substitute
|
||||
matched_text
|
||||
else
|
||||
content.chunks << chunk
|
||||
chunk.mask(content)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@link_text = match_data[0]
|
||||
@textile_prefix, @original_scheme, @user, @host, @port, @path, @query, @fragment =
|
||||
match_data[1..-1]
|
||||
treat_trailing_character
|
||||
end
|
||||
|
||||
def textile_url?
|
||||
@textile_prefix == '":'
|
||||
end
|
||||
|
||||
def textile_image?
|
||||
@textile_prefix == '!' and @trailing_punctuation == '!'
|
||||
end
|
||||
|
||||
def treat_trailing_character
|
||||
# If the last character matched by URI pattern is in ! or ), this may be part of the markup,
|
||||
# not a URL. We should handle it as such. It is possible to do it by a regexp, but
|
||||
# much easier to do programmatically
|
||||
last_char = @link_text[-1..-1]
|
||||
if last_char == ')' or last_char == '!'
|
||||
@trailing_punctuation = last_char
|
||||
@link_text.chop!
|
||||
[@original_scheme, @user, @host, @port, @path, @query, @fragment].compact.last.chop!
|
||||
end
|
||||
end
|
||||
|
||||
# If the text should be escaped then don't keep this chunk.
|
||||
# Otherwise only keep this chunk if it was substituted back into the
|
||||
# content.
|
||||
def unmask(content)
|
||||
return nil if escaped_text
|
||||
return self if content.sub!(mask(content), "<a href=\"#{uri}\">#{link_text}</a>")
|
||||
end
|
||||
|
||||
# If there is no hostname in the URI, do not render it
|
||||
# It's probably only contains the scheme, eg 'something:'
|
||||
def escaped_text() ( host.nil? ? @uri : nil ) end
|
||||
|
||||
def scheme
|
||||
@original_scheme or (@user ? 'mailto' : 'http')
|
||||
end
|
||||
|
||||
def scheme_delimiter
|
||||
scheme == 'mailto' ? ':' : '://'
|
||||
end
|
||||
|
||||
def user_delimiter
|
||||
'@' unless @user.nil?
|
||||
end
|
||||
|
||||
def port_delimiter
|
||||
':' unless @port.nil?
|
||||
end
|
||||
|
||||
def query_delimiter
|
||||
'?' unless @query.nil?
|
||||
end
|
||||
|
||||
def uri
|
||||
[scheme, scheme_delimiter, user, user_delimiter, host, port_delimiter, port, path,
|
||||
query_delimiter, query].compact.join
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# uri with mandatory scheme but less restrictive hostname, like
|
||||
# http://localhost:2500/blah.html
|
||||
class LocalURIChunk < URIChunk
|
||||
|
||||
unless defined? LocalURIChunk::LOCAL_URI_REGEXP
|
||||
# hostname can be just a simple word like 'localhost'
|
||||
ANY_HOSTNAME = "(?:#{DOMLABEL}\\.)*#{TOPLABEL}\\.?"
|
||||
|
||||
# The basic URI expression as a string
|
||||
# Scheme and hostname are mandatory
|
||||
LOCAL_URI =
|
||||
"(?:(#{SCHEME})://)+" + # Mandatory scheme:// (\1)
|
||||
"(?:(#{USERINFO})@)?" + # Optional userinfo@ (\2)
|
||||
"(#{ANY_HOSTNAME})" + # Mandatory hostname (\3)
|
||||
"(?::(#{PORT}))?" + # Optional :port (\4)
|
||||
"(#{ABS_PATH})?" + # Optional absolute path (\5)
|
||||
"(?:\\?(#{QUERY}))?" + # Optional ?query (\6)
|
||||
"(?:\\#(#{FRAGMENT}))?" # Optional #fragment (\7)
|
||||
|
||||
LOCAL_URI_REGEXP = Regexp.new(TEXTILE_SYNTAX_PREFIX + LOCAL_URI, Regexp::EXTENDED, 'N')
|
||||
end
|
||||
|
||||
def LocalURIChunk.pattern
|
||||
LOCAL_URI_REGEXP
|
||||
end
|
||||
|
||||
end
|
||||
require 'chunks/chunk'
|
||||
|
||||
# This wiki chunk matches arbitrary URIs, using patterns from the Ruby URI modules.
|
||||
# It parses out a variety of fields that could be used by renderers to format
|
||||
# the links in various ways (shortening domain names, hiding email addresses)
|
||||
# It matches email addresses and host.com.au domains without schemes (http://)
|
||||
# but adds these on as required.
|
||||
#
|
||||
# The heuristic used to match a URI is designed to err on the side of caution.
|
||||
# That is, it is more likely to not autolink a URI than it is to accidently
|
||||
# autolink something that is not a URI. The reason behind this is it is easier
|
||||
# to force a URI link by prefixing 'http://' to it than it is to escape and
|
||||
# incorrectly marked up non-URI.
|
||||
#
|
||||
# I'm using a part of the [ISO 3166-1 Standard][iso3166] for country name suffixes.
|
||||
# The generic names are from www.bnoack.com/data/countrycode2.html)
|
||||
# [iso3166]: http://geotags.com/iso3166/
|
||||
class URIChunk < Chunk::Abstract
|
||||
include URI::REGEXP::PATTERN
|
||||
|
||||
# this condition is to get rid of pesky warnings in tests
|
||||
unless defined? URIChunk::INTERNET_URI_REGEXP
|
||||
|
||||
GENERIC = '(?:aero|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org)'
|
||||
COUNTRY = '(?:au|at|be|ca|ch|de|dk|fr|hk|in|ir|it|jp|nl|no|pt|ru|se|sw|tv|tw|uk|us)'
|
||||
|
||||
# These are needed otherwise HOST will match almost anything
|
||||
TLDS = "(?:#{GENERIC}|#{COUNTRY})"
|
||||
|
||||
# Redefine USERINFO so that it must have non-zero length
|
||||
USERINFO = "(?:[#{UNRESERVED};:&=+$,]|#{ESCAPED})+"
|
||||
|
||||
# unreserved_no_ending = alphanum | mark, but URI_ENDING [)!] excluded
|
||||
UNRESERVED_NO_ENDING = "-_.~*'(#{ALNUM}"
|
||||
|
||||
# this ensures that query or fragment do not end with URI_ENDING
|
||||
# and enable us to use a much simpler self.pattern Regexp
|
||||
|
||||
# uric_no_ending = reserved | unreserved_no_ending | escaped
|
||||
URIC_NO_ENDING = "(?:[#{UNRESERVED_NO_ENDING}#{RESERVED}]|#{ESCAPED})"
|
||||
# query = *uric
|
||||
QUERY = "#{URIC_NO_ENDING}*"
|
||||
# fragment = *uric
|
||||
FRAGMENT = "#{URIC_NO_ENDING}*"
|
||||
|
||||
# DOMLABEL is defined in the ruby uri library, TLDS is defined above
|
||||
INTERNET_HOSTNAME = "(?:#{DOMLABEL}\\.)+#{TLDS}"
|
||||
|
||||
# Correct a typo bug in ruby 1.8.x lib/uri/common.rb
|
||||
PORT = '\\d*'
|
||||
|
||||
INTERNET_URI =
|
||||
"(?:(#{SCHEME}):/{0,2})?" + # Optional scheme: (\1)
|
||||
"(?:(#{USERINFO})@)?" + # Optional userinfo@ (\2)
|
||||
"(#{INTERNET_HOSTNAME})" + # Mandatory hostname (\3)
|
||||
"(?::(#{PORT}))?" + # Optional :port (\4)
|
||||
"(#{ABS_PATH})?" + # Optional absolute path (\5)
|
||||
"(?:\\?(#{QUERY}))?" + # Optional ?query (\6)
|
||||
"(?:\\#(#{FRAGMENT}))?" # Optional #fragment (\7)
|
||||
|
||||
TEXTILE_SYNTAX_PREFIX = '(!)?'
|
||||
|
||||
INTERNET_URI_REGEXP = Regexp.new(TEXTILE_SYNTAX_PREFIX + INTERNET_URI, Regexp::EXTENDED, 'N')
|
||||
|
||||
end
|
||||
|
||||
def URIChunk.pattern
|
||||
INTERNET_URI_REGEXP
|
||||
end
|
||||
|
||||
attr_reader :user, :host, :port, :path, :query, :fragment, :link_text
|
||||
|
||||
def self.apply_to(content)
|
||||
content.gsub!( self.pattern ) do |matched_text|
|
||||
chunk = self.new($~)
|
||||
if chunk.textile_url? or chunk.textile_image?
|
||||
# do not substitute
|
||||
matched_text
|
||||
else
|
||||
content.chunks << chunk
|
||||
chunk.mask(content)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@link_text = match_data[0]
|
||||
@textile_prefix, @original_scheme, @user, @host, @port, @path, @query, @fragment =
|
||||
match_data[1..-1]
|
||||
treat_trailing_character
|
||||
end
|
||||
|
||||
def textile_url?
|
||||
@textile_prefix == '":'
|
||||
end
|
||||
|
||||
def textile_image?
|
||||
@textile_prefix == '!' and @trailing_punctuation == '!'
|
||||
end
|
||||
|
||||
def treat_trailing_character
|
||||
# If the last character matched by URI pattern is in ! or ), this may be part of the markup,
|
||||
# not a URL. We should handle it as such. It is possible to do it by a regexp, but
|
||||
# much easier to do programmatically
|
||||
last_char = @link_text[-1..-1]
|
||||
if last_char == ')' or last_char == '!'
|
||||
@trailing_punctuation = last_char
|
||||
@link_text.chop!
|
||||
[@original_scheme, @user, @host, @port, @path, @query, @fragment].compact.last.chop!
|
||||
end
|
||||
end
|
||||
|
||||
# If the text should be escaped then don't keep this chunk.
|
||||
# Otherwise only keep this chunk if it was substituted back into the
|
||||
# content.
|
||||
def unmask(content)
|
||||
return nil if escaped_text
|
||||
return self if content.sub!(mask(content), "<a href=\"#{uri}\">#{link_text}</a>")
|
||||
end
|
||||
|
||||
# If there is no hostname in the URI, do not render it
|
||||
# It's probably only contains the scheme, eg 'something:'
|
||||
def escaped_text() ( host.nil? ? @uri : nil ) end
|
||||
|
||||
def scheme
|
||||
@original_scheme or (@user ? 'mailto' : 'http')
|
||||
end
|
||||
|
||||
def scheme_delimiter
|
||||
scheme == 'mailto' ? ':' : '://'
|
||||
end
|
||||
|
||||
def user_delimiter
|
||||
'@' unless @user.nil?
|
||||
end
|
||||
|
||||
def port_delimiter
|
||||
':' unless @port.nil?
|
||||
end
|
||||
|
||||
def query_delimiter
|
||||
'?' unless @query.nil?
|
||||
end
|
||||
|
||||
def uri
|
||||
[scheme, scheme_delimiter, user, user_delimiter, host, port_delimiter, port, path,
|
||||
query_delimiter, query].compact.join
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# uri with mandatory scheme but less restrictive hostname, like
|
||||
# http://localhost:2500/blah.html
|
||||
class LocalURIChunk < URIChunk
|
||||
|
||||
unless defined? LocalURIChunk::LOCAL_URI_REGEXP
|
||||
# hostname can be just a simple word like 'localhost'
|
||||
ANY_HOSTNAME = "(?:#{DOMLABEL}\\.)*#{TOPLABEL}\\.?"
|
||||
|
||||
# The basic URI expression as a string
|
||||
# Scheme and hostname are mandatory
|
||||
LOCAL_URI =
|
||||
"(?:(#{SCHEME})://)+" + # Mandatory scheme:// (\1)
|
||||
"(?:(#{USERINFO})@)?" + # Optional userinfo@ (\2)
|
||||
"(#{ANY_HOSTNAME})" + # Mandatory hostname (\3)
|
||||
"(?::(#{PORT}))?" + # Optional :port (\4)
|
||||
"(#{ABS_PATH})?" + # Optional absolute path (\5)
|
||||
"(?:\\?(#{QUERY}))?" + # Optional ?query (\6)
|
||||
"(?:\\#(#{FRAGMENT}))?" # Optional #fragment (\7)
|
||||
|
||||
LOCAL_URI_REGEXP = Regexp.new(TEXTILE_SYNTAX_PREFIX + LOCAL_URI, Regexp::EXTENDED, 'N')
|
||||
end
|
||||
|
||||
def LocalURIChunk.pattern
|
||||
LOCAL_URI_REGEXP
|
||||
end
|
||||
|
||||
end
|
||||
|
|
284
app/models/chunks/wiki.rb
Executable file → Normal file
284
app/models/chunks/wiki.rb
Executable file → Normal file
|
@ -1,142 +1,142 @@
|
|||
require 'wiki_words'
|
||||
require 'chunks/chunk'
|
||||
require 'chunks/wiki'
|
||||
require 'cgi'
|
||||
|
||||
# Contains all the methods for finding and replacing wiki related links.
|
||||
module WikiChunk
|
||||
include Chunk
|
||||
|
||||
# A wiki link is the top-level class for anything that refers to
|
||||
# another wiki page.
|
||||
class WikiLink < Chunk::Abstract
|
||||
|
||||
attr_reader :page_name, :link_text, :link_type
|
||||
|
||||
def initialize(*args)
|
||||
super
|
||||
@link_type = 'show'
|
||||
end
|
||||
|
||||
def self.apply_to(content)
|
||||
content.gsub!( self.pattern ) do |matched_text|
|
||||
chunk = self.new($~)
|
||||
if chunk.textile_url?
|
||||
# do not substitute
|
||||
matched_text
|
||||
else
|
||||
content.chunks << chunk
|
||||
chunk.mask(content)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def textile_url?
|
||||
not @textile_link_suffix.nil?
|
||||
end
|
||||
|
||||
# By default, no escaped text
|
||||
def escaped_text() nil end
|
||||
|
||||
# Replace link with a mask, but if the word is escaped, then don't replace it
|
||||
def mask(content)
|
||||
escaped_text || super(content)
|
||||
end
|
||||
|
||||
def revert(content) content.sub!(mask(content), text) end
|
||||
|
||||
# Do not keep this chunk if it is escaped.
|
||||
# Otherwise, pass the link procedure a page_name and link_text and
|
||||
# get back a string of HTML to replace the mask with.
|
||||
def unmask(content)
|
||||
if escaped_text
|
||||
return self
|
||||
else
|
||||
chunk_found = content.sub!(mask(content)) do |match|
|
||||
content.page_link(page_name, link_text, link_type)
|
||||
end
|
||||
if chunk_found
|
||||
return self
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# This chunk matches a WikiWord. WikiWords can be escaped
|
||||
# by prepending a '\'. When this is the case, the +escaped_text+
|
||||
# method will return the WikiWord instead of the usual +nil+.
|
||||
# The +page_name+ method returns the matched WikiWord.
|
||||
class Word < WikiLink
|
||||
unless defined? WIKI_LINK
|
||||
WIKI_WORD = Regexp.new('(":)?(\\\\)?(' + WikiWords::WIKI_WORD_PATTERN + ')\b', 0, "utf-8")
|
||||
end
|
||||
|
||||
def self.pattern
|
||||
WIKI_WORD
|
||||
end
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@textile_link_suffix, @escape, @page_name = match_data[1..3]
|
||||
end
|
||||
|
||||
def escaped_text
|
||||
page_name unless @escape.nil?
|
||||
end
|
||||
def link_text() WikiWords.separate(page_name) end
|
||||
end
|
||||
|
||||
# This chunk handles [[bracketted wiki words]] and
|
||||
# [[AliasedWords|aliased wiki words]]. The first part of an
|
||||
# aliased wiki word must be a WikiWord. If the WikiWord
|
||||
# is aliased, the +link_text+ field will contain the
|
||||
# alias, otherwise +link_text+ will contain the entire
|
||||
# contents within the double brackets.
|
||||
#
|
||||
# NOTE: This chunk must be tested before WikiWord since
|
||||
# a WikiWords can be a substring of a WikiLink.
|
||||
class Link < WikiLink
|
||||
|
||||
unless defined? WIKI_LINK
|
||||
WIKI_LINK = /(":)?\[\[([^\]]+)\]\]/
|
||||
LINK_TYPE_SEPARATION = Regexp.new('^(.+):((file)|(pic))$', 0, 'utf-8')
|
||||
ALIAS_SEPARATION = Regexp.new('^(.+)\|(.+)$', 0, 'utf-8')
|
||||
end
|
||||
|
||||
def self.pattern() WIKI_LINK end
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@textile_link_suffix, @page_name = match_data[1..2]
|
||||
@link_text = @page_name
|
||||
separate_link_type
|
||||
separate_alias
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# if link wihin the brackets has a form of [[filename:file]] or [[filename:pic]],
|
||||
# this means a link to a picture or a file
|
||||
def separate_link_type
|
||||
link_type_match = LINK_TYPE_SEPARATION.match(@page_name)
|
||||
if link_type_match
|
||||
@link_text = @page_name = link_type_match[1]
|
||||
@link_type = link_type_match[2..3].compact[0]
|
||||
end
|
||||
end
|
||||
|
||||
# link text may be different from page name. this will look like [[actual page|link text]]
|
||||
def separate_alias
|
||||
alias_match = ALIAS_SEPARATION.match(@page_name)
|
||||
if alias_match
|
||||
@page_name, @link_text = alias_match[1..2]
|
||||
end
|
||||
# note that [[filename|link text:file]] is also supported
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
require 'wiki_words'
|
||||
require 'chunks/chunk'
|
||||
require 'chunks/wiki'
|
||||
require 'cgi'
|
||||
|
||||
# Contains all the methods for finding and replacing wiki related links.
|
||||
module WikiChunk
|
||||
include Chunk
|
||||
|
||||
# A wiki link is the top-level class for anything that refers to
|
||||
# another wiki page.
|
||||
class WikiLink < Chunk::Abstract
|
||||
|
||||
attr_reader :page_name, :link_text, :link_type
|
||||
|
||||
def initialize(*args)
|
||||
super
|
||||
@link_type = 'show'
|
||||
end
|
||||
|
||||
def self.apply_to(content)
|
||||
content.gsub!( self.pattern ) do |matched_text|
|
||||
chunk = self.new($~)
|
||||
if chunk.textile_url?
|
||||
# do not substitute
|
||||
matched_text
|
||||
else
|
||||
content.chunks << chunk
|
||||
chunk.mask(content)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def textile_url?
|
||||
not @textile_link_suffix.nil?
|
||||
end
|
||||
|
||||
# By default, no escaped text
|
||||
def escaped_text() nil end
|
||||
|
||||
# Replace link with a mask, but if the word is escaped, then don't replace it
|
||||
def mask(content)
|
||||
escaped_text || super(content)
|
||||
end
|
||||
|
||||
def revert(content) content.sub!(mask(content), text) end
|
||||
|
||||
# Do not keep this chunk if it is escaped.
|
||||
# Otherwise, pass the link procedure a page_name and link_text and
|
||||
# get back a string of HTML to replace the mask with.
|
||||
def unmask(content)
|
||||
if escaped_text
|
||||
return self
|
||||
else
|
||||
chunk_found = content.sub!(mask(content)) do |match|
|
||||
content.page_link(page_name, link_text, link_type)
|
||||
end
|
||||
if chunk_found
|
||||
return self
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# This chunk matches a WikiWord. WikiWords can be escaped
|
||||
# by prepending a '\'. When this is the case, the +escaped_text+
|
||||
# method will return the WikiWord instead of the usual +nil+.
|
||||
# The +page_name+ method returns the matched WikiWord.
|
||||
class Word < WikiLink
|
||||
unless defined? WIKI_LINK
|
||||
WIKI_WORD = Regexp.new('(":)?(\\\\)?(' + WikiWords::WIKI_WORD_PATTERN + ')\b', 0, "utf-8")
|
||||
end
|
||||
|
||||
def self.pattern
|
||||
WIKI_WORD
|
||||
end
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@textile_link_suffix, @escape, @page_name = match_data[1..3]
|
||||
end
|
||||
|
||||
def escaped_text
|
||||
page_name unless @escape.nil?
|
||||
end
|
||||
def link_text() WikiWords.separate(page_name) end
|
||||
end
|
||||
|
||||
# This chunk handles [[bracketted wiki words]] and
|
||||
# [[AliasedWords|aliased wiki words]]. The first part of an
|
||||
# aliased wiki word must be a WikiWord. If the WikiWord
|
||||
# is aliased, the +link_text+ field will contain the
|
||||
# alias, otherwise +link_text+ will contain the entire
|
||||
# contents within the double brackets.
|
||||
#
|
||||
# NOTE: This chunk must be tested before WikiWord since
|
||||
# a WikiWords can be a substring of a WikiLink.
|
||||
class Link < WikiLink
|
||||
|
||||
unless defined? WIKI_LINK
|
||||
WIKI_LINK = /(":)?\[\[([^\]]+)\]\]/
|
||||
LINK_TYPE_SEPARATION = Regexp.new('^(.+):((file)|(pic))$', 0, 'utf-8')
|
||||
ALIAS_SEPARATION = Regexp.new('^(.+)\|(.+)$', 0, 'utf-8')
|
||||
end
|
||||
|
||||
def self.pattern() WIKI_LINK end
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@textile_link_suffix, @page_name = match_data[1..2]
|
||||
@link_text = @page_name
|
||||
separate_link_type
|
||||
separate_alias
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# if link wihin the brackets has a form of [[filename:file]] or [[filename:pic]],
|
||||
# this means a link to a picture or a file
|
||||
def separate_link_type
|
||||
link_type_match = LINK_TYPE_SEPARATION.match(@page_name)
|
||||
if link_type_match
|
||||
@link_text = @page_name = link_type_match[1]
|
||||
@link_type = link_type_match[2..3].compact[0]
|
||||
end
|
||||
end
|
||||
|
||||
# link text may be different from page name. this will look like [[actual page|link text]]
|
||||
def separate_alias
|
||||
alias_match = ALIAS_SEPARATION.match(@page_name)
|
||||
if alias_match
|
||||
@page_name, @link_text = alias_match[1..2]
|
||||
end
|
||||
# note that [[filename|link text:file]] is also supported
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,45 +1,45 @@
|
|||
require 'instiki_errors'
|
||||
|
||||
class FileYard
|
||||
|
||||
attr_reader :files_path
|
||||
|
||||
def initialize(files_path)
|
||||
@files_path = files_path
|
||||
@files = Dir["#{files_path}/*"].collect{|path| File.basename(path) if File.file?(path) }.compact
|
||||
end
|
||||
|
||||
def upload_file(name, io)
|
||||
sanitize_file_name(name)
|
||||
if io.kind_of?(Tempfile)
|
||||
io.close
|
||||
FileUtils.mv(io.path, file_path(name))
|
||||
else
|
||||
File.open(file_path(name), 'wb') { |f| f.write(io.read) }
|
||||
end
|
||||
# just in case, restrict read access and prohibit write access to the uploaded file
|
||||
FileUtils.chmod(0440, file_path(name))
|
||||
end
|
||||
|
||||
def files
|
||||
Dir["#{files_path}/*"].collect{|path| File.basename(path) if File.file?(path)}.compact
|
||||
end
|
||||
|
||||
def has_file?(name)
|
||||
files.include?(name)
|
||||
end
|
||||
|
||||
def file_path(name)
|
||||
"#{files_path}/#{name}"
|
||||
end
|
||||
|
||||
SANE_FILE_NAME = /[-_\.A-Za-z0-9]{1,255}/
|
||||
|
||||
def sanitize_file_name(name)
|
||||
unless name =~ SANE_FILE_NAME
|
||||
raise Instiki::ValidationError.new("Invalid file name: '#{name}'.\n" +
|
||||
"Only latin characters, digits, dots, underscores and dashes are accepted.")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
require 'instiki_errors'
|
||||
|
||||
class FileYard
|
||||
|
||||
attr_reader :files_path
|
||||
|
||||
def initialize(files_path)
|
||||
@files_path = files_path
|
||||
@files = Dir["#{files_path}/*"].collect{|path| File.basename(path) if File.file?(path) }.compact
|
||||
end
|
||||
|
||||
def upload_file(name, io)
|
||||
sanitize_file_name(name)
|
||||
if io.kind_of?(Tempfile)
|
||||
io.close
|
||||
FileUtils.mv(io.path, file_path(name))
|
||||
else
|
||||
File.open(file_path(name), 'wb') { |f| f.write(io.read) }
|
||||
end
|
||||
# just in case, restrict read access and prohibit write access to the uploaded file
|
||||
FileUtils.chmod(0440, file_path(name))
|
||||
end
|
||||
|
||||
def files
|
||||
Dir["#{files_path}/*"].collect{|path| File.basename(path) if File.file?(path)}.compact
|
||||
end
|
||||
|
||||
def has_file?(name)
|
||||
files.include?(name)
|
||||
end
|
||||
|
||||
def file_path(name)
|
||||
"#{files_path}/#{name}"
|
||||
end
|
||||
|
||||
SANE_FILE_NAME = /[-_\.A-Za-z0-9]{1,255}/
|
||||
|
||||
def sanitize_file_name(name)
|
||||
unless name =~ SANE_FILE_NAME
|
||||
raise Instiki::ValidationError.new("Invalid file name: '#{name}'.\n" +
|
||||
"Only latin characters, digits, dots, underscores and dashes are accepted.")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
184
app/models/page.rb
Executable file → Normal file
184
app/models/page.rb
Executable file → Normal file
|
@ -1,92 +1,92 @@
|
|||
require 'date'
|
||||
require 'page_lock'
|
||||
require 'revision'
|
||||
require 'wiki_words'
|
||||
require 'chunks/wiki'
|
||||
|
||||
class Page
|
||||
include PageLock
|
||||
|
||||
attr_reader :name, :web
|
||||
attr_accessor :revisions
|
||||
|
||||
def initialize(web, name, content, created_at, author)
|
||||
@web, @name, @revisions = web, name, []
|
||||
revise(content, created_at, author)
|
||||
end
|
||||
|
||||
def revise(content, created_at, author)
|
||||
|
||||
if not @revisions.empty? and content == @revisions.last.content
|
||||
raise Instiki::ValidationError.new(
|
||||
"You have tried to save page '#{name}' without changing its content")
|
||||
end
|
||||
|
||||
# A user may change a page, look at it and make some more changes - several times.
|
||||
# Not to record every such iteration as a new revision, if the previous revision was done
|
||||
# by the same author, not more than 30 minutes ago, then update the last revision instead of
|
||||
# creating a new one
|
||||
if !@revisions.empty? && continous_revision?(created_at, author)
|
||||
@revisions.last.created_at = created_at
|
||||
@revisions.last.content = content
|
||||
@revisions.last.clear_display_cache
|
||||
else
|
||||
@revisions << Revision.new(self, @revisions.length, content, created_at, author)
|
||||
end
|
||||
|
||||
web.refresh_pages_with_references(name) if @revisions.length == 1
|
||||
end
|
||||
|
||||
def rollback(revision_number, created_at, author_ip = nil)
|
||||
roll_back_revision = @revisions[revision_number].dup
|
||||
revise(roll_back_revision.content, created_at, Author.new(roll_back_revision.author, author_ip))
|
||||
end
|
||||
|
||||
def revisions?
|
||||
revisions.length > 1
|
||||
end
|
||||
|
||||
def revised_on
|
||||
created_on
|
||||
end
|
||||
|
||||
def in_category?(cat)
|
||||
cat.nil? || cat.empty? || categories.include?(cat)
|
||||
end
|
||||
|
||||
def categories
|
||||
display_content.find_chunks(Category).map { |cat| cat.list }.flatten
|
||||
end
|
||||
|
||||
def authors
|
||||
revisions.collect { |rev| rev.author }
|
||||
end
|
||||
|
||||
def references
|
||||
web.select.pages_that_reference(name)
|
||||
end
|
||||
|
||||
# Returns the original wiki-word name as separate words, so "MyPage" becomes "My Page".
|
||||
def plain_name
|
||||
web.brackets_only ? name : WikiWords.separate(name)
|
||||
end
|
||||
|
||||
def link(options = {})
|
||||
web.make_link(name, nil, options)
|
||||
end
|
||||
|
||||
def author_link(options = {})
|
||||
web.make_link(author, nil, options)
|
||||
end
|
||||
|
||||
private
|
||||
def continous_revision?(created_at, author)
|
||||
@revisions.last.author == author && @revisions.last.created_at + 30.minutes > created_at
|
||||
end
|
||||
|
||||
# Forward method calls to the current revision, so the page responds to all revision calls
|
||||
def method_missing(method_symbol)
|
||||
revisions.last.send(method_symbol)
|
||||
end
|
||||
|
||||
end
|
||||
require 'date'
|
||||
require 'page_lock'
|
||||
require 'revision'
|
||||
require 'wiki_words'
|
||||
require 'chunks/wiki'
|
||||
|
||||
class Page
|
||||
include PageLock
|
||||
|
||||
attr_reader :name, :web
|
||||
attr_accessor :revisions
|
||||
|
||||
def initialize(web, name, content, created_at, author)
|
||||
@web, @name, @revisions = web, name, []
|
||||
revise(content, created_at, author)
|
||||
end
|
||||
|
||||
def revise(content, created_at, author)
|
||||
|
||||
if not @revisions.empty? and content == @revisions.last.content
|
||||
raise Instiki::ValidationError.new(
|
||||
"You have tried to save page '#{name}' without changing its content")
|
||||
end
|
||||
|
||||
# A user may change a page, look at it and make some more changes - several times.
|
||||
# Not to record every such iteration as a new revision, if the previous revision was done
|
||||
# by the same author, not more than 30 minutes ago, then update the last revision instead of
|
||||
# creating a new one
|
||||
if !@revisions.empty? && continous_revision?(created_at, author)
|
||||
@revisions.last.created_at = created_at
|
||||
@revisions.last.content = content
|
||||
@revisions.last.clear_display_cache
|
||||
else
|
||||
@revisions << Revision.new(self, @revisions.length, content, created_at, author)
|
||||
end
|
||||
|
||||
web.refresh_pages_with_references(name) if @revisions.length == 1
|
||||
end
|
||||
|
||||
def rollback(revision_number, created_at, author_ip = nil)
|
||||
roll_back_revision = @revisions[revision_number].dup
|
||||
revise(roll_back_revision.content, created_at, Author.new(roll_back_revision.author, author_ip))
|
||||
end
|
||||
|
||||
def revisions?
|
||||
revisions.length > 1
|
||||
end
|
||||
|
||||
def revised_on
|
||||
created_on
|
||||
end
|
||||
|
||||
def in_category?(cat)
|
||||
cat.nil? || cat.empty? || categories.include?(cat)
|
||||
end
|
||||
|
||||
def categories
|
||||
display_content.find_chunks(Category).map { |cat| cat.list }.flatten
|
||||
end
|
||||
|
||||
def authors
|
||||
revisions.collect { |rev| rev.author }
|
||||
end
|
||||
|
||||
def references
|
||||
web.select.pages_that_reference(name)
|
||||
end
|
||||
|
||||
# Returns the original wiki-word name as separate words, so "MyPage" becomes "My Page".
|
||||
def plain_name
|
||||
web.brackets_only ? name : WikiWords.separate(name)
|
||||
end
|
||||
|
||||
def link(options = {})
|
||||
web.make_link(name, nil, options)
|
||||
end
|
||||
|
||||
def author_link(options = {})
|
||||
web.make_link(author, nil, options)
|
||||
end
|
||||
|
||||
private
|
||||
def continous_revision?(created_at, author)
|
||||
@revisions.last.author == author && @revisions.last.created_at + 30.minutes > created_at
|
||||
end
|
||||
|
||||
# Forward method calls to the current revision, so the page responds to all revision calls
|
||||
def method_missing(method_symbol)
|
||||
revisions.last.send(method_symbol)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
46
app/models/page_lock.rb
Executable file → Normal file
46
app/models/page_lock.rb
Executable file → Normal file
|
@ -1,24 +1,24 @@
|
|||
# Contains all the lock methods to be mixed in with the page
|
||||
module PageLock
|
||||
LOCKING_PERIOD = 30 * 60 # 30 minutes
|
||||
|
||||
def lock(time, locked_by)
|
||||
@locked_at, @locked_by = time, locked_by
|
||||
end
|
||||
|
||||
def lock_duration(time)
|
||||
((time - @locked_at) / 60).to_i unless @locked_at.nil?
|
||||
end
|
||||
|
||||
def unlock
|
||||
@locked_at = nil
|
||||
end
|
||||
|
||||
def locked?(comparison_time)
|
||||
@locked_at + LOCKING_PERIOD > comparison_time unless @locked_at.nil?
|
||||
end
|
||||
|
||||
def locked_by_link
|
||||
web.make_link(@locked_by)
|
||||
end
|
||||
# Contains all the lock methods to be mixed in with the page
|
||||
module PageLock
|
||||
LOCKING_PERIOD = 30 * 60 # 30 minutes
|
||||
|
||||
def lock(time, locked_by)
|
||||
@locked_at, @locked_by = time, locked_by
|
||||
end
|
||||
|
||||
def lock_duration(time)
|
||||
((time - @locked_at) / 60).to_i unless @locked_at.nil?
|
||||
end
|
||||
|
||||
def unlock
|
||||
@locked_at = nil
|
||||
end
|
||||
|
||||
def locked?(comparison_time)
|
||||
@locked_at + LOCKING_PERIOD > comparison_time unless @locked_at.nil?
|
||||
end
|
||||
|
||||
def locked_by_link
|
||||
web.make_link(@locked_by)
|
||||
end
|
||||
end
|
144
app/models/page_set.rb
Executable file → Normal file
144
app/models/page_set.rb
Executable file → Normal file
|
@ -1,73 +1,73 @@
|
|||
# Container for a set of pages with methods for manipulation.
|
||||
|
||||
class PageSet < Array
|
||||
attr_reader :web
|
||||
|
||||
def initialize(web, pages = nil, condition = nil)
|
||||
@web = web
|
||||
# if pages is not specified, make a list of all pages in the web
|
||||
if pages.nil?
|
||||
super(web.pages.values)
|
||||
# otherwise use specified pages and condition to produce a set of pages
|
||||
elsif condition.nil?
|
||||
super(pages)
|
||||
else
|
||||
super(pages.select { |page| condition[page] })
|
||||
end
|
||||
end
|
||||
|
||||
def most_recent_revision
|
||||
self.map { |page| page.created_at }.max || Time.at(0)
|
||||
end
|
||||
|
||||
|
||||
def by_name
|
||||
PageSet.new(@web, sort_by { |page| page.name })
|
||||
end
|
||||
|
||||
alias :sort :by_name
|
||||
|
||||
def by_revision
|
||||
PageSet.new(@web, sort_by { |page| page.created_at }).reverse
|
||||
end
|
||||
|
||||
def pages_that_reference(page_name)
|
||||
self.select { |page| page.wiki_words.include?(page_name) }
|
||||
end
|
||||
|
||||
def pages_authored_by(author)
|
||||
self.select { |page| page.authors.include?(author) }
|
||||
end
|
||||
|
||||
def characters
|
||||
self.inject(0) { |chars,page| chars += page.content.size }
|
||||
end
|
||||
|
||||
# Returns all the orphaned pages in this page set. That is,
|
||||
# pages in this set for which there is no reference in the web.
|
||||
# The HomePage and author pages are always assumed to have
|
||||
# references and so cannot be orphans
|
||||
def orphaned_pages
|
||||
references = web.select.wiki_words + ["HomePage"] + web.select.authors
|
||||
self.reject { |page| references.include?(page.name) }
|
||||
end
|
||||
|
||||
# Returns all the wiki words in this page set for which
|
||||
# there are no pages in this page set's web
|
||||
def wanted_pages
|
||||
wiki_words - web.select.names
|
||||
end
|
||||
|
||||
def names
|
||||
self.map { |page| page.name }
|
||||
end
|
||||
|
||||
def wiki_words
|
||||
self.inject([]) { |wiki_words, page| wiki_words << page.wiki_words }.flatten.uniq
|
||||
end
|
||||
|
||||
def authors
|
||||
self.inject([]) { |authors, page| authors << page.authors }.flatten.uniq.sort
|
||||
end
|
||||
|
||||
# Container for a set of pages with methods for manipulation.
|
||||
|
||||
class PageSet < Array
|
||||
attr_reader :web
|
||||
|
||||
def initialize(web, pages = nil, condition = nil)
|
||||
@web = web
|
||||
# if pages is not specified, make a list of all pages in the web
|
||||
if pages.nil?
|
||||
super(web.pages.values)
|
||||
# otherwise use specified pages and condition to produce a set of pages
|
||||
elsif condition.nil?
|
||||
super(pages)
|
||||
else
|
||||
super(pages.select { |page| condition[page] })
|
||||
end
|
||||
end
|
||||
|
||||
def most_recent_revision
|
||||
self.map { |page| page.created_at }.max || Time.at(0)
|
||||
end
|
||||
|
||||
|
||||
def by_name
|
||||
PageSet.new(@web, sort_by { |page| page.name })
|
||||
end
|
||||
|
||||
alias :sort :by_name
|
||||
|
||||
def by_revision
|
||||
PageSet.new(@web, sort_by { |page| page.created_at }).reverse
|
||||
end
|
||||
|
||||
def pages_that_reference(page_name)
|
||||
self.select { |page| page.wiki_words.include?(page_name) }
|
||||
end
|
||||
|
||||
def pages_authored_by(author)
|
||||
self.select { |page| page.authors.include?(author) }
|
||||
end
|
||||
|
||||
def characters
|
||||
self.inject(0) { |chars,page| chars += page.content.size }
|
||||
end
|
||||
|
||||
# Returns all the orphaned pages in this page set. That is,
|
||||
# pages in this set for which there is no reference in the web.
|
||||
# The HomePage and author pages are always assumed to have
|
||||
# references and so cannot be orphans
|
||||
def orphaned_pages
|
||||
references = web.select.wiki_words + ["HomePage"] + web.select.authors
|
||||
self.reject { |page| references.include?(page.name) }
|
||||
end
|
||||
|
||||
# Returns all the wiki words in this page set for which
|
||||
# there are no pages in this page set's web
|
||||
def wanted_pages
|
||||
wiki_words - web.select.names
|
||||
end
|
||||
|
||||
def names
|
||||
self.map { |page| page.name }
|
||||
end
|
||||
|
||||
def wiki_words
|
||||
self.inject([]) { |wiki_words, page| wiki_words << page.wiki_words }.flatten.uniq
|
||||
end
|
||||
|
||||
def authors
|
||||
self.inject([]) { |authors, page| authors << page.authors }.flatten.uniq.sort
|
||||
end
|
||||
|
||||
end
|
166
app/models/revision.rb
Executable file → Normal file
166
app/models/revision.rb
Executable file → Normal file
|
@ -1,83 +1,83 @@
|
|||
require 'diff'
|
||||
require 'wiki_content'
|
||||
require 'chunks/wiki'
|
||||
require 'date'
|
||||
require 'author'
|
||||
require 'page'
|
||||
|
||||
class Revision
|
||||
|
||||
attr_accessor :page, :number, :content, :created_at, :author
|
||||
|
||||
def initialize(page, number, content, created_at, author)
|
||||
@page, @number, @created_at, @author = page, number, created_at, author
|
||||
self.content = content
|
||||
end
|
||||
|
||||
def created_on
|
||||
Date.new(@created_at.year, @created_at.mon, @created_at.day)
|
||||
end
|
||||
|
||||
def pretty_created_at
|
||||
# Must use DateTime because Time doesn't support %e on at least some platforms
|
||||
DateTime.new(
|
||||
@created_at.year, @created_at.mon, @created_at.day, @created_at.hour, @created_at.min
|
||||
).strftime "%B %e, %Y %H:%M"
|
||||
end
|
||||
|
||||
def next_revision
|
||||
page.revisions[number + 1]
|
||||
end
|
||||
|
||||
def previous_revision
|
||||
number > 0 ? page.revisions[number - 1] : nil
|
||||
end
|
||||
|
||||
# Returns an array of all the WikiWords present in the content of this revision.
|
||||
def wiki_words
|
||||
unless @wiki_words_cache
|
||||
wiki_chunks = display_content.find_chunks(WikiChunk::WikiLink)
|
||||
@wiki_words_cache = wiki_chunks.map { |c| ( c.escaped_text ? nil : c.page_name ) }.compact.uniq
|
||||
end
|
||||
@wiki_words_cache
|
||||
end
|
||||
|
||||
# Returns an array of all the WikiWords present in the content of this revision.
|
||||
# that already exists as a page in the web.
|
||||
def existing_pages
|
||||
wiki_words.select { |wiki_word| page.web.pages[wiki_word] }
|
||||
end
|
||||
|
||||
# Returns an array of all the WikiWords present in the content of this revision
|
||||
# that *doesn't* already exists as a page in the web.
|
||||
def unexisting_pages
|
||||
wiki_words - existing_pages
|
||||
end
|
||||
|
||||
# Explicit check for new type of display cache with find_chunks method.
|
||||
# Ensures new version works with older snapshots.
|
||||
def display_content
|
||||
unless @display_cache && @display_cache.respond_to?(:find_chunks)
|
||||
@display_cache = WikiContent.new(self)
|
||||
end
|
||||
@display_cache
|
||||
end
|
||||
|
||||
def display_diff
|
||||
previous_revision ? HTMLDiff.diff(previous_revision.display_content, display_content) : display_content
|
||||
end
|
||||
|
||||
def clear_display_cache
|
||||
@display_cache = @published_cache = @wiki_words_cache = nil
|
||||
end
|
||||
|
||||
def display_published
|
||||
@published_cache = WikiContent.new(self, {:mode => :publish}) if @published_cache.nil?
|
||||
@published_cache
|
||||
end
|
||||
|
||||
def display_content_for_export
|
||||
WikiContent.new(self, {:mode => :export} )
|
||||
end
|
||||
|
||||
end
|
||||
require 'diff'
|
||||
require 'wiki_content'
|
||||
require 'chunks/wiki'
|
||||
require 'date'
|
||||
require 'author'
|
||||
require 'page'
|
||||
|
||||
class Revision
|
||||
|
||||
attr_accessor :page, :number, :content, :created_at, :author
|
||||
|
||||
def initialize(page, number, content, created_at, author)
|
||||
@page, @number, @created_at, @author = page, number, created_at, author
|
||||
self.content = content
|
||||
end
|
||||
|
||||
def created_on
|
||||
Date.new(@created_at.year, @created_at.mon, @created_at.day)
|
||||
end
|
||||
|
||||
def pretty_created_at
|
||||
# Must use DateTime because Time doesn't support %e on at least some platforms
|
||||
DateTime.new(
|
||||
@created_at.year, @created_at.mon, @created_at.day, @created_at.hour, @created_at.min
|
||||
).strftime "%B %e, %Y %H:%M"
|
||||
end
|
||||
|
||||
def next_revision
|
||||
page.revisions[number + 1]
|
||||
end
|
||||
|
||||
def previous_revision
|
||||
number > 0 ? page.revisions[number - 1] : nil
|
||||
end
|
||||
|
||||
# Returns an array of all the WikiWords present in the content of this revision.
|
||||
def wiki_words
|
||||
unless @wiki_words_cache
|
||||
wiki_chunks = display_content.find_chunks(WikiChunk::WikiLink)
|
||||
@wiki_words_cache = wiki_chunks.map { |c| ( c.escaped_text ? nil : c.page_name ) }.compact.uniq
|
||||
end
|
||||
@wiki_words_cache
|
||||
end
|
||||
|
||||
# Returns an array of all the WikiWords present in the content of this revision.
|
||||
# that already exists as a page in the web.
|
||||
def existing_pages
|
||||
wiki_words.select { |wiki_word| page.web.pages[wiki_word] }
|
||||
end
|
||||
|
||||
# Returns an array of all the WikiWords present in the content of this revision
|
||||
# that *doesn't* already exists as a page in the web.
|
||||
def unexisting_pages
|
||||
wiki_words - existing_pages
|
||||
end
|
||||
|
||||
# Explicit check for new type of display cache with find_chunks method.
|
||||
# Ensures new version works with older snapshots.
|
||||
def display_content
|
||||
unless @display_cache && @display_cache.respond_to?(:find_chunks)
|
||||
@display_cache = WikiContent.new(self)
|
||||
end
|
||||
@display_cache
|
||||
end
|
||||
|
||||
def display_diff
|
||||
previous_revision ? HTMLDiff.diff(previous_revision.display_content, display_content) : display_content
|
||||
end
|
||||
|
||||
def clear_display_cache
|
||||
@display_cache = @published_cache = @wiki_words_cache = nil
|
||||
end
|
||||
|
||||
def display_published
|
||||
@published_cache = WikiContent.new(self, {:mode => :publish}) if @published_cache.nil?
|
||||
@published_cache
|
||||
end
|
||||
|
||||
def display_content_for_export
|
||||
WikiContent.new(self, {:mode => :export} )
|
||||
end
|
||||
|
||||
end
|
||||
|
|
308
app/models/web.rb
Executable file → Normal file
308
app/models/web.rb
Executable file → Normal file
|
@ -1,155 +1,155 @@
|
|||
require "cgi"
|
||||
require "page"
|
||||
require "page_set"
|
||||
require "wiki_words"
|
||||
require "zip/zip"
|
||||
|
||||
class Web
|
||||
attr_accessor :name, :address, :password, :markup, :color, :safe_mode, :pages
|
||||
attr_accessor :additional_style, :published, :brackets_only, :count_pages, :allow_uploads
|
||||
|
||||
def initialize(parent_wiki, name, address, password = nil)
|
||||
@wiki, @name, @address, @password = parent_wiki, name, address, password
|
||||
|
||||
# default values
|
||||
@markup = :textile
|
||||
@color = '008B26'
|
||||
@safe_mode = false
|
||||
@pages = {}
|
||||
@allow_uploads = true
|
||||
@additional_style = nil
|
||||
@published = false
|
||||
@brackets_only = false
|
||||
@count_pages = false
|
||||
@allow_uploads = true
|
||||
end
|
||||
|
||||
def add_page(page)
|
||||
@pages[page.name] = page
|
||||
end
|
||||
|
||||
def remove_pages(pages_to_be_removed)
|
||||
pages.delete_if { |page_name, page| pages_to_be_removed.include?(page) }
|
||||
end
|
||||
|
||||
def select(&condition)
|
||||
PageSet.new(self, @pages.values, condition)
|
||||
end
|
||||
|
||||
def revised_on
|
||||
select.most_recent_revision
|
||||
end
|
||||
|
||||
def authors
|
||||
select.authors
|
||||
end
|
||||
|
||||
def categories
|
||||
select.map { |page| page.categories }.flatten.uniq.sort
|
||||
end
|
||||
|
||||
# Create a link for the given page name and link text based
|
||||
# on the render mode in options and whether the page exists
|
||||
# in the this web.
|
||||
def make_link(name, text = nil, options = {})
|
||||
text = CGI.escapeHTML(text || WikiWords.separate(name))
|
||||
mode = options[:mode]
|
||||
link_type = options[:link_type] || 'show'
|
||||
case link_type
|
||||
when 'show'
|
||||
make_page_link(mode, name, text)
|
||||
when 'file'
|
||||
make_file_link(mode, name, text)
|
||||
when 'pic'
|
||||
make_pic_link(mode, name, text)
|
||||
else
|
||||
raise "Unknown link type: #{link_type}"
|
||||
end
|
||||
end
|
||||
|
||||
def make_page_link(mode, name, text)
|
||||
link = CGI.escape(name)
|
||||
case mode
|
||||
when :export
|
||||
if has_page?(name) then "<a class=\"existingWikiWord\" href=\"#{link}.html\">#{text}</a>"
|
||||
else "<span class=\"newWikiWord\">#{text}</span>" end
|
||||
when :publish
|
||||
if has_page?(name) then "<a class=\"existingWikiWord\" href=\"../published/#{link}\">#{text}</a>"
|
||||
else "<span class=\"newWikiWord\">#{text}</span>" end
|
||||
else
|
||||
if has_page?(name)
|
||||
"<a class=\"existingWikiWord\" href=\"../show/#{link}\">#{text}</a>"
|
||||
else
|
||||
"<span class=\"newWikiWord\">#{text}<a href=\"../show/#{link}\">?</a></span>"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def make_file_link(mode, name, text)
|
||||
link = CGI.escape(name)
|
||||
case mode
|
||||
when :export
|
||||
if has_file?(name) then "<a class=\"existingWikiWord\" href=\"#{link}.html\">#{text}</a>"
|
||||
else "<span class=\"newWikiWord\">#{text}</span>" end
|
||||
when :publish
|
||||
if has_file?(name) then "<a class=\"existingWikiWord\" href=\"../published/#{link}\">#{text}</a>"
|
||||
else "<span class=\"newWikiWord\">#{text}</span>" end
|
||||
else
|
||||
if has_file?(name)
|
||||
"<a class=\"existingWikiWord\" href=\"../file/#{link}\">#{text}</a>"
|
||||
else
|
||||
"<span class=\"newWikiWord\">#{text}<a href=\"../file/#{link}\">?</a></span>"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def make_pic_link(mode, name, text)
|
||||
link = CGI.escape(name)
|
||||
case mode
|
||||
when :export
|
||||
if has_file?(name) then "<img alt=\"#{text}\" src=\"#{link}\" />"
|
||||
else "<img alt=\"#{text}\" src=\"no image\" />" end
|
||||
when :publish
|
||||
if has_file?(name) then "<img alt=\"#{text}\" src=\"#{link}\" />"
|
||||
else "<span class=\"newWikiWord\">#{text}</span>" end
|
||||
else
|
||||
if has_file?(name) then "<img alt=\"#{text}\" src=\"../pic/#{link}\" />"
|
||||
else "<span class=\"newWikiWord\">#{text}<a href=\"../pic/#{link}\">?</a></span>" end
|
||||
end
|
||||
end
|
||||
|
||||
def has_page?(name)
|
||||
pages[name]
|
||||
end
|
||||
|
||||
def has_file?(name)
|
||||
wiki.file_yard(self).has_file?(name)
|
||||
end
|
||||
|
||||
# Clears the display cache for all the pages with references to
|
||||
def refresh_pages_with_references(page_name)
|
||||
select.pages_that_reference(page_name).each { |page|
|
||||
page.revisions.each { |revision| revision.clear_display_cache }
|
||||
}
|
||||
end
|
||||
|
||||
def refresh_revisions
|
||||
select.each { |page| page.revisions.each { |revision| revision.clear_display_cache } }
|
||||
end
|
||||
|
||||
private
|
||||
# Returns an array of all the wiki words in any current revision
|
||||
def wiki_words
|
||||
pages.values.inject([]) { |wiki_words, page| wiki_words << page.wiki_words }.flatten.uniq
|
||||
end
|
||||
|
||||
# Returns an array of all the page names on this web
|
||||
def page_names
|
||||
pages.keys
|
||||
end
|
||||
|
||||
# This ensures compatibility with 0.9 storages
|
||||
def wiki
|
||||
@wiki ||= WikiService.instance
|
||||
end
|
||||
require "cgi"
|
||||
require "page"
|
||||
require "page_set"
|
||||
require "wiki_words"
|
||||
require "zip/zip"
|
||||
|
||||
class Web
|
||||
attr_accessor :name, :address, :password, :markup, :color, :safe_mode, :pages
|
||||
attr_accessor :additional_style, :published, :brackets_only, :count_pages, :allow_uploads
|
||||
|
||||
def initialize(parent_wiki, name, address, password = nil)
|
||||
@wiki, @name, @address, @password = parent_wiki, name, address, password
|
||||
|
||||
# default values
|
||||
@markup = :textile
|
||||
@color = '008B26'
|
||||
@safe_mode = false
|
||||
@pages = {}
|
||||
@allow_uploads = true
|
||||
@additional_style = nil
|
||||
@published = false
|
||||
@brackets_only = false
|
||||
@count_pages = false
|
||||
@allow_uploads = true
|
||||
end
|
||||
|
||||
def add_page(page)
|
||||
@pages[page.name] = page
|
||||
end
|
||||
|
||||
def remove_pages(pages_to_be_removed)
|
||||
pages.delete_if { |page_name, page| pages_to_be_removed.include?(page) }
|
||||
end
|
||||
|
||||
def select(&condition)
|
||||
PageSet.new(self, @pages.values, condition)
|
||||
end
|
||||
|
||||
def revised_on
|
||||
select.most_recent_revision
|
||||
end
|
||||
|
||||
def authors
|
||||
select.authors
|
||||
end
|
||||
|
||||
def categories
|
||||
select.map { |page| page.categories }.flatten.uniq.sort
|
||||
end
|
||||
|
||||
# Create a link for the given page name and link text based
|
||||
# on the render mode in options and whether the page exists
|
||||
# in the this web.
|
||||
def make_link(name, text = nil, options = {})
|
||||
text = CGI.escapeHTML(text || WikiWords.separate(name))
|
||||
mode = options[:mode]
|
||||
link_type = options[:link_type] || 'show'
|
||||
case link_type
|
||||
when 'show'
|
||||
make_page_link(mode, name, text)
|
||||
when 'file'
|
||||
make_file_link(mode, name, text)
|
||||
when 'pic'
|
||||
make_pic_link(mode, name, text)
|
||||
else
|
||||
raise "Unknown link type: #{link_type}"
|
||||
end
|
||||
end
|
||||
|
||||
def make_page_link(mode, name, text)
|
||||
link = CGI.escape(name)
|
||||
case mode
|
||||
when :export
|
||||
if has_page?(name) then "<a class=\"existingWikiWord\" href=\"#{link}.html\">#{text}</a>"
|
||||
else "<span class=\"newWikiWord\">#{text}</span>" end
|
||||
when :publish
|
||||
if has_page?(name) then "<a class=\"existingWikiWord\" href=\"../published/#{link}\">#{text}</a>"
|
||||
else "<span class=\"newWikiWord\">#{text}</span>" end
|
||||
else
|
||||
if has_page?(name)
|
||||
"<a class=\"existingWikiWord\" href=\"../show/#{link}\">#{text}</a>"
|
||||
else
|
||||
"<span class=\"newWikiWord\">#{text}<a href=\"../show/#{link}\">?</a></span>"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def make_file_link(mode, name, text)
|
||||
link = CGI.escape(name)
|
||||
case mode
|
||||
when :export
|
||||
if has_file?(name) then "<a class=\"existingWikiWord\" href=\"#{link}.html\">#{text}</a>"
|
||||
else "<span class=\"newWikiWord\">#{text}</span>" end
|
||||
when :publish
|
||||
if has_file?(name) then "<a class=\"existingWikiWord\" href=\"../published/#{link}\">#{text}</a>"
|
||||
else "<span class=\"newWikiWord\">#{text}</span>" end
|
||||
else
|
||||
if has_file?(name)
|
||||
"<a class=\"existingWikiWord\" href=\"../file/#{link}\">#{text}</a>"
|
||||
else
|
||||
"<span class=\"newWikiWord\">#{text}<a href=\"../file/#{link}\">?</a></span>"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def make_pic_link(mode, name, text)
|
||||
link = CGI.escape(name)
|
||||
case mode
|
||||
when :export
|
||||
if has_file?(name) then "<img alt=\"#{text}\" src=\"#{link}\" />"
|
||||
else "<img alt=\"#{text}\" src=\"no image\" />" end
|
||||
when :publish
|
||||
if has_file?(name) then "<img alt=\"#{text}\" src=\"#{link}\" />"
|
||||
else "<span class=\"newWikiWord\">#{text}</span>" end
|
||||
else
|
||||
if has_file?(name) then "<img alt=\"#{text}\" src=\"../pic/#{link}\" />"
|
||||
else "<span class=\"newWikiWord\">#{text}<a href=\"../pic/#{link}\">?</a></span>" end
|
||||
end
|
||||
end
|
||||
|
||||
def has_page?(name)
|
||||
pages[name]
|
||||
end
|
||||
|
||||
def has_file?(name)
|
||||
wiki.file_yard(self).has_file?(name)
|
||||
end
|
||||
|
||||
# Clears the display cache for all the pages with references to
|
||||
def refresh_pages_with_references(page_name)
|
||||
select.pages_that_reference(page_name).each { |page|
|
||||
page.revisions.each { |revision| revision.clear_display_cache }
|
||||
}
|
||||
end
|
||||
|
||||
def refresh_revisions
|
||||
select.each { |page| page.revisions.each { |revision| revision.clear_display_cache } }
|
||||
end
|
||||
|
||||
private
|
||||
# Returns an array of all the wiki words in any current revision
|
||||
def wiki_words
|
||||
pages.values.inject([]) { |wiki_words, page| wiki_words << page.wiki_words }.flatten.uniq
|
||||
end
|
||||
|
||||
# Returns an array of all the page names on this web
|
||||
def page_names
|
||||
pages.keys
|
||||
end
|
||||
|
||||
# This ensures compatibility with 0.9 storages
|
||||
def wiki
|
||||
@wiki ||= WikiService.instance
|
||||
end
|
||||
end
|
192
app/models/wiki_content.rb
Executable file → Normal file
192
app/models/wiki_content.rb
Executable file → Normal file
|
@ -1,97 +1,97 @@
|
|||
require 'cgi'
|
||||
require 'chunks/engines'
|
||||
require 'chunks/category'
|
||||
require 'chunks/include'
|
||||
require 'chunks/wiki'
|
||||
require 'chunks/literal'
|
||||
require 'chunks/uri'
|
||||
require 'chunks/nowiki'
|
||||
|
||||
# Wiki content is just a string that can process itself with a chain of
|
||||
# actions. The actions can modify wiki content so that certain parts of
|
||||
# it are protected from being rendered by later actions.
|
||||
#
|
||||
# When wiki content is rendered, it can be interrogated to find out
|
||||
# which chunks were rendered. This means things like categories, wiki
|
||||
# links, can be determined.
|
||||
#
|
||||
# Exactly how wiki content is rendered is determined by a number of
|
||||
# settings that are optionally passed in to a constructor. The current
|
||||
# options are:
|
||||
# * :engine
|
||||
# => The structural markup engine to use (Textile, Markdown, RDoc)
|
||||
# * :engine_opts
|
||||
# => A list of options to pass to the markup engines (safe modes, etc)
|
||||
# * :pre_engine_actions
|
||||
# => A list of render actions or chunks to be processed before the
|
||||
# markup engine is applied. By default this is:
|
||||
# Category, Include, URIChunk, WikiChunk::Link, WikiChunk::Word
|
||||
# * :post_engine_actions
|
||||
# => A list of render actions or chunks to apply after the markup
|
||||
# engine. By default these are:
|
||||
# Literal::Pre, Literal::Tags
|
||||
# * :mode
|
||||
# => How should the content be rendered? For normal display (:display),
|
||||
# publishing (:publish) or export (:export)?
|
||||
#
|
||||
# AUTHOR: Mark Reid <mark @ threewordslong . com>
|
||||
# CREATED: 15th May 2004
|
||||
# UPDATED: 22nd May 2004
|
||||
class WikiContent < String
|
||||
|
||||
PRE_ENGINE_ACTIONS = [ NoWiki, Category, Include, WikiChunk::Link, URIChunk, LocalURIChunk,
|
||||
WikiChunk::Word ]
|
||||
POST_ENGINE_ACTIONS = [ Literal::Pre, Literal::Tags ]
|
||||
DEFAULT_OPTS = {
|
||||
:pre_engine_actions => PRE_ENGINE_ACTIONS,
|
||||
:post_engine_actions => POST_ENGINE_ACTIONS,
|
||||
:engine => Engines::Textile,
|
||||
:engine_opts => [],
|
||||
:mode => [:display]
|
||||
}
|
||||
|
||||
attr_reader :web, :options, :rendered, :chunks
|
||||
|
||||
# Create a new wiki content string from the given one.
|
||||
# The options are explained at the top of this file.
|
||||
def initialize(revision, options = {})
|
||||
@revision = revision
|
||||
@web = @revision.page.web
|
||||
|
||||
# Deep copy of DEFAULT_OPTS to ensure that changes to PRE/POST_ENGINE_ACTIONS stay local
|
||||
@options = Marshal.load(Marshal.dump(DEFAULT_OPTS)).update(options)
|
||||
@options[:engine] = Engines::MAP[@web.markup] || Engines::Textile
|
||||
@options[:engine_opts] = (@web.safe_mode ? [:filter_html, :filter_styles] : [])
|
||||
|
||||
@options[:pre_engine_actions].delete(WikiChunk::Word) if @web.brackets_only
|
||||
|
||||
super(@revision.content)
|
||||
|
||||
begin
|
||||
render!(@options[:pre_engine_actions] + [@options[:engine]] + @options[:post_engine_actions])
|
||||
# FIXME this is where all the parsing problems were shoved under the carpet
|
||||
# rescue => e
|
||||
# @rendered = e.message
|
||||
end
|
||||
end
|
||||
|
||||
# Call @web.page_link using current options.
|
||||
def page_link(name, text, link_type)
|
||||
@options[:link_type] = link_type || :show
|
||||
@web.make_link(name, text, @options)
|
||||
end
|
||||
|
||||
# Find all the chunks of the given types
|
||||
def find_chunks(chunk_type)
|
||||
rendered.select { |chunk| chunk.kind_of?(chunk_type) }
|
||||
end
|
||||
|
||||
# Render this content using the specified actions.
|
||||
def render!(chunk_types)
|
||||
@chunks = []
|
||||
chunk_types.each { |chunk_type| chunk_type.apply_to(self) }
|
||||
@rendered = @chunks.map { |chunk| chunk.unmask(self) }.compact
|
||||
(@chunks - @rendered).each { |chunk| chunk.revert(self) }
|
||||
end
|
||||
|
||||
require 'cgi'
|
||||
require 'chunks/engines'
|
||||
require 'chunks/category'
|
||||
require 'chunks/include'
|
||||
require 'chunks/wiki'
|
||||
require 'chunks/literal'
|
||||
require 'chunks/uri'
|
||||
require 'chunks/nowiki'
|
||||
|
||||
# Wiki content is just a string that can process itself with a chain of
|
||||
# actions. The actions can modify wiki content so that certain parts of
|
||||
# it are protected from being rendered by later actions.
|
||||
#
|
||||
# When wiki content is rendered, it can be interrogated to find out
|
||||
# which chunks were rendered. This means things like categories, wiki
|
||||
# links, can be determined.
|
||||
#
|
||||
# Exactly how wiki content is rendered is determined by a number of
|
||||
# settings that are optionally passed in to a constructor. The current
|
||||
# options are:
|
||||
# * :engine
|
||||
# => The structural markup engine to use (Textile, Markdown, RDoc)
|
||||
# * :engine_opts
|
||||
# => A list of options to pass to the markup engines (safe modes, etc)
|
||||
# * :pre_engine_actions
|
||||
# => A list of render actions or chunks to be processed before the
|
||||
# markup engine is applied. By default this is:
|
||||
# Category, Include, URIChunk, WikiChunk::Link, WikiChunk::Word
|
||||
# * :post_engine_actions
|
||||
# => A list of render actions or chunks to apply after the markup
|
||||
# engine. By default these are:
|
||||
# Literal::Pre, Literal::Tags
|
||||
# * :mode
|
||||
# => How should the content be rendered? For normal display (:display),
|
||||
# publishing (:publish) or export (:export)?
|
||||
#
|
||||
# AUTHOR: Mark Reid <mark @ threewordslong . com>
|
||||
# CREATED: 15th May 2004
|
||||
# UPDATED: 22nd May 2004
|
||||
class WikiContent < String
|
||||
|
||||
PRE_ENGINE_ACTIONS = [ NoWiki, Category, Include, WikiChunk::Link, URIChunk, LocalURIChunk,
|
||||
WikiChunk::Word ]
|
||||
POST_ENGINE_ACTIONS = [ Literal::Pre, Literal::Tags ]
|
||||
DEFAULT_OPTS = {
|
||||
:pre_engine_actions => PRE_ENGINE_ACTIONS,
|
||||
:post_engine_actions => POST_ENGINE_ACTIONS,
|
||||
:engine => Engines::Textile,
|
||||
:engine_opts => [],
|
||||
:mode => [:display]
|
||||
}
|
||||
|
||||
attr_reader :web, :options, :rendered, :chunks
|
||||
|
||||
# Create a new wiki content string from the given one.
|
||||
# The options are explained at the top of this file.
|
||||
def initialize(revision, options = {})
|
||||
@revision = revision
|
||||
@web = @revision.page.web
|
||||
|
||||
# Deep copy of DEFAULT_OPTS to ensure that changes to PRE/POST_ENGINE_ACTIONS stay local
|
||||
@options = Marshal.load(Marshal.dump(DEFAULT_OPTS)).update(options)
|
||||
@options[:engine] = Engines::MAP[@web.markup] || Engines::Textile
|
||||
@options[:engine_opts] = (@web.safe_mode ? [:filter_html, :filter_styles] : [])
|
||||
|
||||
@options[:pre_engine_actions].delete(WikiChunk::Word) if @web.brackets_only
|
||||
|
||||
super(@revision.content)
|
||||
|
||||
begin
|
||||
render!(@options[:pre_engine_actions] + [@options[:engine]] + @options[:post_engine_actions])
|
||||
# FIXME this is where all the parsing problems were shoved under the carpet
|
||||
# rescue => e
|
||||
# @rendered = e.message
|
||||
end
|
||||
end
|
||||
|
||||
# Call @web.page_link using current options.
|
||||
def page_link(name, text, link_type)
|
||||
@options[:link_type] = link_type || :show
|
||||
@web.make_link(name, text, @options)
|
||||
end
|
||||
|
||||
# Find all the chunks of the given types
|
||||
def find_chunks(chunk_type)
|
||||
rendered.select { |chunk| chunk.kind_of?(chunk_type) }
|
||||
end
|
||||
|
||||
# Render this content using the specified actions.
|
||||
def render!(chunk_types)
|
||||
@chunks = []
|
||||
chunk_types.each { |chunk_type| chunk_type.apply_to(self) }
|
||||
@rendered = @chunks.map { |chunk| chunk.unmask(self) }.compact
|
||||
(@chunks - @rendered).each { |chunk| chunk.revert(self) }
|
||||
end
|
||||
|
||||
end
|
444
app/models/wiki_service.rb
Executable file → Normal file
444
app/models/wiki_service.rb
Executable file → Normal file
|
@ -1,222 +1,222 @@
|
|||
require 'open-uri'
|
||||
require 'yaml'
|
||||
require 'madeleine'
|
||||
require 'madeleine/automatic'
|
||||
require 'madeleine/zmarshal'
|
||||
|
||||
require 'web'
|
||||
require 'page'
|
||||
require 'author'
|
||||
require 'file_yard'
|
||||
|
||||
module AbstractWikiService
|
||||
|
||||
attr_reader :webs, :system
|
||||
|
||||
def authenticate(password)
|
||||
password == (@system[:password] || 'instiki')
|
||||
end
|
||||
|
||||
def create_web(name, address, password = nil)
|
||||
@webs[address] = Web.new(self, name, address, password) unless @webs[address]
|
||||
end
|
||||
|
||||
def delete_web(address)
|
||||
@webs[address] = nil
|
||||
end
|
||||
|
||||
def file_yard(web)
|
||||
raise "Web #{@web.name} does not belong to this wiki service" unless @webs.values.include?(web)
|
||||
# TODO cache FileYards
|
||||
FileYard.new("#{self.storage_path}/#{web.address}")
|
||||
end
|
||||
|
||||
def init_wiki_service
|
||||
@webs = {}
|
||||
@system = {}
|
||||
end
|
||||
|
||||
def read_page(web_address, page_name)
|
||||
ApplicationController.logger.debug "Reading page '#{page_name}' from web '#{web_address}'"
|
||||
web = @webs[web_address]
|
||||
if web.nil?
|
||||
ApplicationController.logger.debug "Web '#{web_address}' not found"
|
||||
return nil
|
||||
else
|
||||
page = web.pages[page_name]
|
||||
ApplicationController.logger.debug "Page '#{page_name}' #{page.nil? ? 'not' : ''} found"
|
||||
return page
|
||||
end
|
||||
end
|
||||
|
||||
def remove_orphaned_pages(web_address)
|
||||
@webs[web_address].remove_pages(@webs[web_address].select.orphaned_pages)
|
||||
end
|
||||
|
||||
def revise_page(web_address, page_name, content, revised_on, author)
|
||||
page = read_page(web_address, page_name)
|
||||
page.revise(content, revised_on, author)
|
||||
page
|
||||
end
|
||||
|
||||
def rollback_page(web_address, page_name, revision_number, created_at, author_id = nil)
|
||||
page = read_page(web_address, page_name)
|
||||
page.rollback(revision_number, created_at, author_id)
|
||||
page
|
||||
end
|
||||
|
||||
def setup(password, web_name, web_address)
|
||||
@system[:password] = password
|
||||
create_web(web_name, web_address)
|
||||
end
|
||||
|
||||
def setup?
|
||||
not (@webs.empty?)
|
||||
end
|
||||
|
||||
def update_web(old_address, new_address, name, markup, color, additional_style, safe_mode = false,
|
||||
password = nil, published = false, brackets_only = false, count_pages = false,
|
||||
allow_uploads = true)
|
||||
if old_address != new_address
|
||||
@webs[new_address] = @webs[old_address]
|
||||
@webs.delete(old_address)
|
||||
@webs[new_address].address = new_address
|
||||
end
|
||||
|
||||
web = @webs[new_address]
|
||||
web.refresh_revisions if settings_changed?(web, markup, safe_mode, brackets_only)
|
||||
|
||||
web.name, web.markup, web.color, web.additional_style, web.safe_mode =
|
||||
name, markup, color, additional_style, safe_mode
|
||||
|
||||
web.password, web.published, web.brackets_only, web.count_pages, web.allow_uploads =
|
||||
password, published, brackets_only, count_pages, allow_uploads
|
||||
end
|
||||
|
||||
def write_page(web_address, page_name, content, written_on, author)
|
||||
page = Page.new(@webs[web_address], page_name, content, written_on, author)
|
||||
@webs[web_address].add_page(page)
|
||||
page
|
||||
end
|
||||
|
||||
def storage_path
|
||||
self.class.storage_path
|
||||
end
|
||||
|
||||
private
|
||||
def settings_changed?(web, markup, safe_mode, brackets_only)
|
||||
web.markup != markup ||
|
||||
web.safe_mode != safe_mode ||
|
||||
web.brackets_only != brackets_only
|
||||
end
|
||||
end
|
||||
|
||||
class WikiService
|
||||
|
||||
include AbstractWikiService
|
||||
include Madeleine::Automatic::Interceptor
|
||||
|
||||
# These methods do not change the state of persistent objects, and
|
||||
# should not be ogged by Madeleine
|
||||
automatic_read_only :authenticate, :read_page, :setup?, :webs, :storage_path, :file_yard
|
||||
|
||||
@@storage_path = './storage/'
|
||||
|
||||
class << self
|
||||
|
||||
def storage_path=(storage_path)
|
||||
@@storage_path = storage_path
|
||||
end
|
||||
|
||||
def storage_path
|
||||
@@storage_path
|
||||
end
|
||||
|
||||
def clean_storage
|
||||
MadeleineServer.clean_storage(self)
|
||||
end
|
||||
|
||||
def instance
|
||||
@madeleine ||= MadeleineServer.new(self)
|
||||
@system = @madeleine.system
|
||||
return @system
|
||||
end
|
||||
|
||||
def snapshot
|
||||
@madeleine.snapshot
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def initialize
|
||||
init_wiki_service
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class MadeleineServer
|
||||
|
||||
attr_reader :storage_path
|
||||
|
||||
# Clears all the command_log and snapshot files located in the storage directory, so the
|
||||
# database is essentially dropped and recreated as blank
|
||||
def self.clean_storage(service)
|
||||
begin
|
||||
Dir.foreach(service.storage_path) do |file|
|
||||
if file =~ /(command_log|snapshot)$/
|
||||
File.delete(File.join(service.storage_path, file))
|
||||
end
|
||||
end
|
||||
rescue
|
||||
Dir.mkdir(service.storage_path)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(service)
|
||||
@storage_path = service.storage_path
|
||||
@server = Madeleine::Automatic::AutomaticSnapshotMadeleine.new(service.storage_path,
|
||||
Madeleine::ZMarshal.new) {
|
||||
service.new
|
||||
}
|
||||
start_snapshot_thread
|
||||
end
|
||||
|
||||
def command_log_present?
|
||||
not Dir[storage_path + '/*.command_log'].empty?
|
||||
end
|
||||
|
||||
def snapshot
|
||||
@server.take_snapshot
|
||||
end
|
||||
|
||||
def start_snapshot_thread
|
||||
Thread.new(@server) {
|
||||
hours_since_last_snapshot = 0
|
||||
while true
|
||||
begin
|
||||
hours_since_last_snapshot += 1
|
||||
# Take a snapshot if there is a command log, or 24 hours
|
||||
# have passed since the last snapshot
|
||||
if command_log_present? or hours_since_last_snapshot >= 24
|
||||
ActionController::Base.logger.info "[#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}] " +
|
||||
'Taking a Madeleine snapshot'
|
||||
snapshot
|
||||
hours_since_last_snapshot = 0
|
||||
end
|
||||
sleep(1.hour)
|
||||
rescue => e
|
||||
ActionController::Base.logger.error(e)
|
||||
# wait for a minute (not to spoof the log with the same error)
|
||||
# and go back into the loop, to keep trying
|
||||
sleep(1.minute)
|
||||
ActionController::Base.logger.info("Retrying to save a snapshot")
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def system
|
||||
@server.system
|
||||
end
|
||||
|
||||
end
|
||||
require 'open-uri'
|
||||
require 'yaml'
|
||||
require 'madeleine'
|
||||
require 'madeleine/automatic'
|
||||
require 'madeleine/zmarshal'
|
||||
|
||||
require 'web'
|
||||
require 'page'
|
||||
require 'author'
|
||||
require 'file_yard'
|
||||
|
||||
module AbstractWikiService
|
||||
|
||||
attr_reader :webs, :system
|
||||
|
||||
def authenticate(password)
|
||||
password == (@system[:password] || 'instiki')
|
||||
end
|
||||
|
||||
def create_web(name, address, password = nil)
|
||||
@webs[address] = Web.new(self, name, address, password) unless @webs[address]
|
||||
end
|
||||
|
||||
def delete_web(address)
|
||||
@webs[address] = nil
|
||||
end
|
||||
|
||||
def file_yard(web)
|
||||
raise "Web #{@web.name} does not belong to this wiki service" unless @webs.values.include?(web)
|
||||
# TODO cache FileYards
|
||||
FileYard.new("#{self.storage_path}/#{web.address}")
|
||||
end
|
||||
|
||||
def init_wiki_service
|
||||
@webs = {}
|
||||
@system = {}
|
||||
end
|
||||
|
||||
def read_page(web_address, page_name)
|
||||
ApplicationController.logger.debug "Reading page '#{page_name}' from web '#{web_address}'"
|
||||
web = @webs[web_address]
|
||||
if web.nil?
|
||||
ApplicationController.logger.debug "Web '#{web_address}' not found"
|
||||
return nil
|
||||
else
|
||||
page = web.pages[page_name]
|
||||
ApplicationController.logger.debug "Page '#{page_name}' #{page.nil? ? 'not' : ''} found"
|
||||
return page
|
||||
end
|
||||
end
|
||||
|
||||
def remove_orphaned_pages(web_address)
|
||||
@webs[web_address].remove_pages(@webs[web_address].select.orphaned_pages)
|
||||
end
|
||||
|
||||
def revise_page(web_address, page_name, content, revised_on, author)
|
||||
page = read_page(web_address, page_name)
|
||||
page.revise(content, revised_on, author)
|
||||
page
|
||||
end
|
||||
|
||||
def rollback_page(web_address, page_name, revision_number, created_at, author_id = nil)
|
||||
page = read_page(web_address, page_name)
|
||||
page.rollback(revision_number, created_at, author_id)
|
||||
page
|
||||
end
|
||||
|
||||
def setup(password, web_name, web_address)
|
||||
@system[:password] = password
|
||||
create_web(web_name, web_address)
|
||||
end
|
||||
|
||||
def setup?
|
||||
not (@webs.empty?)
|
||||
end
|
||||
|
||||
def update_web(old_address, new_address, name, markup, color, additional_style, safe_mode = false,
|
||||
password = nil, published = false, brackets_only = false, count_pages = false,
|
||||
allow_uploads = true)
|
||||
if old_address != new_address
|
||||
@webs[new_address] = @webs[old_address]
|
||||
@webs.delete(old_address)
|
||||
@webs[new_address].address = new_address
|
||||
end
|
||||
|
||||
web = @webs[new_address]
|
||||
web.refresh_revisions if settings_changed?(web, markup, safe_mode, brackets_only)
|
||||
|
||||
web.name, web.markup, web.color, web.additional_style, web.safe_mode =
|
||||
name, markup, color, additional_style, safe_mode
|
||||
|
||||
web.password, web.published, web.brackets_only, web.count_pages, web.allow_uploads =
|
||||
password, published, brackets_only, count_pages, allow_uploads
|
||||
end
|
||||
|
||||
def write_page(web_address, page_name, content, written_on, author)
|
||||
page = Page.new(@webs[web_address], page_name, content, written_on, author)
|
||||
@webs[web_address].add_page(page)
|
||||
page
|
||||
end
|
||||
|
||||
def storage_path
|
||||
self.class.storage_path
|
||||
end
|
||||
|
||||
private
|
||||
def settings_changed?(web, markup, safe_mode, brackets_only)
|
||||
web.markup != markup ||
|
||||
web.safe_mode != safe_mode ||
|
||||
web.brackets_only != brackets_only
|
||||
end
|
||||
end
|
||||
|
||||
class WikiService
|
||||
|
||||
include AbstractWikiService
|
||||
include Madeleine::Automatic::Interceptor
|
||||
|
||||
# These methods do not change the state of persistent objects, and
|
||||
# should not be ogged by Madeleine
|
||||
automatic_read_only :authenticate, :read_page, :setup?, :webs, :storage_path, :file_yard
|
||||
|
||||
@@storage_path = './storage/'
|
||||
|
||||
class << self
|
||||
|
||||
def storage_path=(storage_path)
|
||||
@@storage_path = storage_path
|
||||
end
|
||||
|
||||
def storage_path
|
||||
@@storage_path
|
||||
end
|
||||
|
||||
def clean_storage
|
||||
MadeleineServer.clean_storage(self)
|
||||
end
|
||||
|
||||
def instance
|
||||
@madeleine ||= MadeleineServer.new(self)
|
||||
@system = @madeleine.system
|
||||
return @system
|
||||
end
|
||||
|
||||
def snapshot
|
||||
@madeleine.snapshot
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def initialize
|
||||
init_wiki_service
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class MadeleineServer
|
||||
|
||||
attr_reader :storage_path
|
||||
|
||||
# Clears all the command_log and snapshot files located in the storage directory, so the
|
||||
# database is essentially dropped and recreated as blank
|
||||
def self.clean_storage(service)
|
||||
begin
|
||||
Dir.foreach(service.storage_path) do |file|
|
||||
if file =~ /(command_log|snapshot)$/
|
||||
File.delete(File.join(service.storage_path, file))
|
||||
end
|
||||
end
|
||||
rescue
|
||||
Dir.mkdir(service.storage_path)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(service)
|
||||
@storage_path = service.storage_path
|
||||
@server = Madeleine::Automatic::AutomaticSnapshotMadeleine.new(service.storage_path,
|
||||
Madeleine::ZMarshal.new) {
|
||||
service.new
|
||||
}
|
||||
start_snapshot_thread
|
||||
end
|
||||
|
||||
def command_log_present?
|
||||
not Dir[storage_path + '/*.command_log'].empty?
|
||||
end
|
||||
|
||||
def snapshot
|
||||
@server.take_snapshot
|
||||
end
|
||||
|
||||
def start_snapshot_thread
|
||||
Thread.new(@server) {
|
||||
hours_since_last_snapshot = 0
|
||||
while true
|
||||
begin
|
||||
hours_since_last_snapshot += 1
|
||||
# Take a snapshot if there is a command log, or 24 hours
|
||||
# have passed since the last snapshot
|
||||
if command_log_present? or hours_since_last_snapshot >= 24
|
||||
ActionController::Base.logger.info "[#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}] " +
|
||||
'Taking a Madeleine snapshot'
|
||||
snapshot
|
||||
hours_since_last_snapshot = 0
|
||||
end
|
||||
sleep(1.hour)
|
||||
rescue => e
|
||||
ActionController::Base.logger.error(e)
|
||||
# wait for a minute (not to spoof the log with the same error)
|
||||
# and go back into the loop, to keep trying
|
||||
sleep(1.minute)
|
||||
ActionController::Base.logger.info("Retrying to save a snapshot")
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def system
|
||||
@server.system
|
||||
end
|
||||
|
||||
end
|
||||
|
|
46
app/models/wiki_words.rb
Executable file → Normal file
46
app/models/wiki_words.rb
Executable file → Normal file
|
@ -1,23 +1,23 @@
|
|||
# Contains all the methods for finding and replacing wiki words
|
||||
module WikiWords
|
||||
# In order of appearance: Latin, greek, cyrillian, armenian
|
||||
I18N_HIGHER_CASE_LETTERS =
|
||||
"À<EFBFBD>?ÂÃÄÅĀĄĂÆÇĆČĈĊĎ<C48A>?ÈÉÊËĒĘĚĔĖĜĞĠĢĤĦÌ<C4A6>?Î<>?ĪĨĬĮİIJĴĶ<C4B4>?ĽĹĻĿÑŃŇŅŊÒÓÔÕÖØŌ<C398>?ŎŒŔŘŖŚŠŞŜȘŤŢŦȚÙÚÛÜŪŮŰŬŨŲŴ<C5B2>?ŶŸŹŽŻ" +
|
||||
"ΑΒΓΔΕΖΗΘΙΚΛΜ<EFBFBD>?ΞΟΠΡΣΤΥΦΧΨΩ" +
|
||||
"ΆΈΉΊΌΎ<EFBFBD>?ѠѢѤѦѨѪѬѮѰѲѴѶѸѺѼѾҀҊҌҎ<D28C>?ҒҔҖҘҚҜҞҠҢҤҦҨҪҬҮҰҲҴҶҸҺҼҾ<D2BC>?ӃӅӇӉӋ<D389>?<3F>?ӒӔӖӘӚӜӞӠӢӤӦӨӪӬӮӰӲӴӸЖ" +
|
||||
"ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀ<EFBFBD>?ՂՃՄՅՆՇՈՉՊՋՌ<D58B>?<3F>?<3F>?ՑՒՓՔՕՖ"
|
||||
|
||||
I18N_LOWER_CASE_LETTERS =
|
||||
"àáâãäå<EFBFBD>?ąăæçć<C3A7>?ĉċ<C489>?đèéêëēęěĕėƒ<C497>?ğġģĥħìíîïīĩĭįıijĵķĸłľĺļŀñńňņʼnŋòóôõöø<C3B6>?ő<>?œŕřŗśšş<C5A1>?șťţŧțùúûüūůűŭũųŵýÿŷžżźÞþßſ<C39F>?ð" +
|
||||
"άέήίΰαβγδεζηθικλμνξοπ<EFBFBD>?ςστυφχψωϊϋό<CF8B>?ώ<>?" +
|
||||
"абвгдежзийклмнопр<EFBFBD>?туфхцчшщъыь<D18B>?ю<>?<3F>?ёђѓєѕіїјљћќ<D19B>?ўџѡѣѥѧѩѫѭѯѱѳѵѷѹѻѽѿ<D1BD>?ҋ<>?<3F>?ґғҕҗҙқ<D299>?ҟҡңҥҧҩҫҭүұҳҵҷҹһҽҿӀӂӄӆӈӊӌӎӑӓӕӗәӛ<D399>?ӟӡӣӥӧөӫӭӯӱӳӵӹ" +
|
||||
"աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտր<EFBFBD>?ւփքօֆև"
|
||||
|
||||
WIKI_WORD_PATTERN = '[A-Z' + I18N_HIGHER_CASE_LETTERS + '][a-z' + I18N_LOWER_CASE_LETTERS + ']+[A-Z' + I18N_HIGHER_CASE_LETTERS + ']\w+'
|
||||
CAMEL_CASED_WORD_BORDER = /([a-z#{I18N_LOWER_CASE_LETTERS}])([A-Z#{I18N_HIGHER_CASE_LETTERS}])/u
|
||||
|
||||
def self.separate(wiki_word)
|
||||
wiki_word.gsub(CAMEL_CASED_WORD_BORDER, '\1 \2')
|
||||
end
|
||||
|
||||
end
|
||||
# Contains all the methods for finding and replacing wiki words
|
||||
module WikiWords
|
||||
# In order of appearance: Latin, greek, cyrillian, armenian
|
||||
I18N_HIGHER_CASE_LETTERS =
|
||||
"À<EFBFBD>?ÂÃÄÅĀĄĂÆÇĆČĈĊĎ<C48A>?ÈÉÊËĒĘĚĔĖĜĞĠĢĤĦÌ<C4A6>?Î<>?ĪĨĬĮİIJĴĶ<C4B4>?ĽĹĻĿÑŃŇŅŊÒÓÔÕÖØŌ<C398>?ŎŒŔŘŖŚŠŞŜȘŤŢŦȚÙÚÛÜŪŮŰŬŨŲŴ<C5B2>?ŶŸŹŽŻ" +
|
||||
"ΑΒΓΔΕΖΗΘΙΚΛΜ<EFBFBD>?ΞΟΠΡΣΤΥΦΧΨΩ" +
|
||||
"ΆΈΉΊΌΎ<EFBFBD>?ѠѢѤѦѨѪѬѮѰѲѴѶѸѺѼѾҀҊҌҎ<D28C>?ҒҔҖҘҚҜҞҠҢҤҦҨҪҬҮҰҲҴҶҸҺҼҾ<D2BC>?ӃӅӇӉӋ<D389>?<3F>?ӒӔӖӘӚӜӞӠӢӤӦӨӪӬӮӰӲӴӸЖ" +
|
||||
"ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀ<EFBFBD>?ՂՃՄՅՆՇՈՉՊՋՌ<D58B>?<3F>?<3F>?ՑՒՓՔՕՖ"
|
||||
|
||||
I18N_LOWER_CASE_LETTERS =
|
||||
"àáâãäå<EFBFBD>?ąăæçć<C3A7>?ĉċ<C489>?đèéêëēęěĕėƒ<C497>?ğġģĥħìíîïīĩĭįıijĵķĸłľĺļŀñńňņʼnŋòóôõöø<C3B6>?ő<>?œŕřŗśšş<C5A1>?șťţŧțùúûüūůűŭũųŵýÿŷžżźÞþßſ<C39F>?ð" +
|
||||
"άέήίΰαβγδεζηθικλμνξοπ<EFBFBD>?ςστυφχψωϊϋό<CF8B>?ώ<>?" +
|
||||
"абвгдежзийклмнопр<EFBFBD>?туфхцчшщъыь<D18B>?ю<>?<3F>?ёђѓєѕіїјљћќ<D19B>?ўџѡѣѥѧѩѫѭѯѱѳѵѷѹѻѽѿ<D1BD>?ҋ<>?<3F>?ґғҕҗҙқ<D299>?ҟҡңҥҧҩҫҭүұҳҵҷҹһҽҿӀӂӄӆӈӊӌӎӑӓӕӗәӛ<D399>?ӟӡӣӥӧөӫӭӯӱӳӵӹ" +
|
||||
"աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտր<EFBFBD>?ւփքօֆև"
|
||||
|
||||
WIKI_WORD_PATTERN = '[A-Z' + I18N_HIGHER_CASE_LETTERS + '][a-z' + I18N_LOWER_CASE_LETTERS + ']+[A-Z' + I18N_HIGHER_CASE_LETTERS + ']\w+'
|
||||
CAMEL_CASED_WORD_BORDER = /([a-z#{I18N_LOWER_CASE_LETTERS}])([A-Z#{I18N_HIGHER_CASE_LETTERS}])/u
|
||||
|
||||
def self.separate(wiki_word)
|
||||
wiki_word.gsub(CAMEL_CASED_WORD_BORDER, '\1 \2')
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
<%
|
||||
@title = "Upload #{@file_name}"
|
||||
@hide_navigatio = false
|
||||
%>
|
||||
|
||||
<p>
|
||||
<%= form_tag({}, {:multipart => true}) %>
|
||||
<p>
|
||||
File to upload:
|
||||
<br/>
|
||||
<input type="file" name="file" size="40" />
|
||||
</p>
|
||||
<p>
|
||||
<input type="submit" value="Update" /> as
|
||||
<input type="text" name="author" id="authorName" value="<%= @author %>"
|
||||
onClick="this.value == 'AnonymousCoward' ? this.value = '' : true" />
|
||||
<% if @page %>
|
||||
| <a href="../file/">Cancel</a> <small>(unlocks page)</small>
|
||||
<% end %>
|
||||
</p>
|
||||
<%= end_form_tag %>
|
||||
<%
|
||||
@title = "Upload #{@file_name}"
|
||||
@hide_navigatio = false
|
||||
%>
|
||||
|
||||
<p>
|
||||
<%= form_tag({}, {:multipart => true}) %>
|
||||
<p>
|
||||
File to upload:
|
||||
<br/>
|
||||
<input type="file" name="file" size="40" />
|
||||
</p>
|
||||
<p>
|
||||
<input type="submit" value="Update" /> as
|
||||
<input type="text" name="author" id="authorName" value="<%= @author %>"
|
||||
onClick="this.value == 'AnonymousCoward' ? this.value = '' : true" />
|
||||
<% if @page %>
|
||||
| <a href="../file/">Cancel</a> <small>(unlocks page)</small>
|
||||
<% end %>
|
||||
</p>
|
||||
<%= end_form_tag %>
|
||||
</p>
|
|
@ -1,72 +1,72 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>
|
||||
<% if @page and (@page.name == 'HomePage') and (%w( show published print ).include?(@action_name)) %>
|
||||
<%= @web.name %>
|
||||
<% elsif @web %>
|
||||
<%= @title %> in <%= @web.name %>
|
||||
<% else %>
|
||||
<%= @title %>
|
||||
<% end %>
|
||||
</title>
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
|
||||
<style type="text/css">
|
||||
h1#pageName, .newWikiWord a, a.existingWikiWord, .newWikiWord a:hover, #TextileHelp h3 {
|
||||
color: #<%= @web ? @web.color : "393" %>;
|
||||
}
|
||||
|
||||
#Container, #Content {
|
||||
width: <%= @content_width || "600" %>px;
|
||||
}
|
||||
<%= File.read(RAILS_ROOT + '/public/stylesheets/instiki.css') if @inline_style %>
|
||||
</style>
|
||||
|
||||
<link rel="Stylesheet" href="/stylesheets/instiki.css" type="text/css" media="screen" />
|
||||
|
||||
<style type="text/css">
|
||||
<%= @style_additions %>
|
||||
<%= @web ? @web.additional_style : '' %>
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="Container">
|
||||
<div id="Content">
|
||||
|
||||
<h1 id="pageName">
|
||||
<% if @page and (@page.name == 'HomePage') and %w( show published print ).include?(@action_name) %>
|
||||
<%= @web.name %>
|
||||
<% elsif @web %>
|
||||
<small><%= @web.name %></small><br />
|
||||
<%= @title %>
|
||||
<% else %>
|
||||
<%= @title %>
|
||||
<% end %>
|
||||
</h1>
|
||||
|
||||
<% if @flash[:error] %> <div id="error">
|
||||
<hr/><p><%= @flash[:error].to_s %></p><hr/></div>
|
||||
<% end %>
|
||||
|
||||
<% if @flash[:info] %> <div id="info">
|
||||
<hr/><p><%= @flash[:info].to_s %></p><hr/></div>
|
||||
<% end %>
|
||||
|
||||
<%= render 'navigation' unless @web.nil? || @hide_navigation %>
|
||||
<%= @content_for_layout %>
|
||||
|
||||
<div id="footer">
|
||||
<hr/>
|
||||
<p>This site is running on <a href="http://instiki.org/">Instiki</a></p>
|
||||
<br/>
|
||||
<p>Powered by <a href="http://rubyonrails.com/">Ruby on Rails</a></p>
|
||||
</div>
|
||||
|
||||
</div> <!-- Content -->
|
||||
</div> <!-- Container -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>
|
||||
<% if @page and (@page.name == 'HomePage') and (%w( show published print ).include?(@action_name)) %>
|
||||
<%= @web.name %>
|
||||
<% elsif @web %>
|
||||
<%= @title %> in <%= @web.name %>
|
||||
<% else %>
|
||||
<%= @title %>
|
||||
<% end %>
|
||||
</title>
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
|
||||
<style type="text/css">
|
||||
h1#pageName, .newWikiWord a, a.existingWikiWord, .newWikiWord a:hover, #TextileHelp h3 {
|
||||
color: #<%= @web ? @web.color : "393" %>;
|
||||
}
|
||||
|
||||
#Container, #Content {
|
||||
width: <%= @content_width || "600" %>px;
|
||||
}
|
||||
<%= File.read(RAILS_ROOT + '/public/stylesheets/instiki.css') if @inline_style %>
|
||||
</style>
|
||||
|
||||
<link rel="Stylesheet" href="/stylesheets/instiki.css" type="text/css" media="screen" />
|
||||
|
||||
<style type="text/css">
|
||||
<%= @style_additions %>
|
||||
<%= @web ? @web.additional_style : '' %>
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="Container">
|
||||
<div id="Content">
|
||||
|
||||
<h1 id="pageName">
|
||||
<% if @page and (@page.name == 'HomePage') and %w( show published print ).include?(@action_name) %>
|
||||
<%= @web.name %>
|
||||
<% elsif @web %>
|
||||
<small><%= @web.name %></small><br />
|
||||
<%= @title %>
|
||||
<% else %>
|
||||
<%= @title %>
|
||||
<% end %>
|
||||
</h1>
|
||||
|
||||
<% if @flash[:error] %> <div id="error">
|
||||
<hr/><p><%= @flash[:error].to_s %></p><hr/></div>
|
||||
<% end %>
|
||||
|
||||
<% if @flash[:info] %> <div id="info">
|
||||
<hr/><p><%= @flash[:info].to_s %></p><hr/></div>
|
||||
<% end %>
|
||||
|
||||
<%= render 'navigation' unless @web.nil? || @hide_navigation %>
|
||||
<%= @content_for_layout %>
|
||||
|
||||
<div id="footer">
|
||||
<hr/>
|
||||
<p>This site is running on <a href="http://instiki.org/">Instiki</a></p>
|
||||
<br/>
|
||||
<p>Powered by <a href="http://rubyonrails.com/">Ruby on Rails</a></p>
|
||||
</div>
|
||||
|
||||
</div> <!-- Content -->
|
||||
</div> <!-- Container -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
30
app/views/markdown_help.rhtml
Executable file → Normal file
30
app/views/markdown_help.rhtml
Executable file → Normal file
|
@ -1,16 +1,16 @@
|
|||
<div id="TextileHelp" style="float: right; width: 250px; margin-top: 5px">
|
||||
<h3>Markdown formatting tips (<a target="_new" href="http://daringfireball.net/projects/markdown/syntax">advanced</a>)</h3>
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tr><td>_your text_</td><td class="arrow">→</td><td><em>your text</em></td></tr>
|
||||
<tr><td>**your text**</td><td class="arrow">→</td><td><strong>your text</strong></td></tr>
|
||||
<tr><td>`my code`</td><td class="arrow">→</td><td><code>my code</code></td></tr>
|
||||
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">→</td><td>• Bulleted list<br />• Second item</td></tr>
|
||||
<tr><td>1. Numbered list<br />1. Second item</td><td class="arrow">→</td><td>1. Numbered list<br />2. Second item</td></tr>
|
||||
<tr><td>[link name](URL)</td><td class="arrow">→</td><td><a href="URL">link name</a></td></tr>
|
||||
<tr><td>***</td><td class="arrow">→</td><td>Horizontal ruler</td></tr>
|
||||
<tr><td><http://url><br /><email@add.com></td><td class="arrow">→</td><td>Auto-linked</td></tr>
|
||||
<tr><td></td><td class="arrow">→</td><td>Image</td></tr>
|
||||
</table>
|
||||
|
||||
<%= render 'wiki_words_help' %>
|
||||
<div id="TextileHelp" style="float: right; width: 250px; margin-top: 5px">
|
||||
<h3>Markdown formatting tips (<a target="_new" href="http://daringfireball.net/projects/markdown/syntax">advanced</a>)</h3>
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tr><td>_your text_</td><td class="arrow">→</td><td><em>your text</em></td></tr>
|
||||
<tr><td>**your text**</td><td class="arrow">→</td><td><strong>your text</strong></td></tr>
|
||||
<tr><td>`my code`</td><td class="arrow">→</td><td><code>my code</code></td></tr>
|
||||
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">→</td><td>• Bulleted list<br />• Second item</td></tr>
|
||||
<tr><td>1. Numbered list<br />1. Second item</td><td class="arrow">→</td><td>1. Numbered list<br />2. Second item</td></tr>
|
||||
<tr><td>[link name](URL)</td><td class="arrow">→</td><td><a href="URL">link name</a></td></tr>
|
||||
<tr><td>***</td><td class="arrow">→</td><td>Horizontal ruler</td></tr>
|
||||
<tr><td><http://url><br /><email@add.com></td><td class="arrow">→</td><td>Auto-linked</td></tr>
|
||||
<tr><td></td><td class="arrow">→</td><td>Image</td></tr>
|
||||
</table>
|
||||
|
||||
<%= render 'wiki_words_help' %>
|
||||
</div>
|
50
app/views/navigation.rhtml
Executable file → Normal file
50
app/views/navigation.rhtml
Executable file → Normal file
|
@ -1,25 +1,25 @@
|
|||
<%
|
||||
def list_item(title, url, description, accesskey = nil)
|
||||
if @title == title
|
||||
"<b class=\"navOn\" title=\"#{description}\" accesskey=\"#{accesskey}\">#{title}</b>"
|
||||
else
|
||||
"<a href=\"#{url}\" title=\"#{description}\" accesskey=\"#{accesskey}\">#{title}</a>"
|
||||
end
|
||||
end
|
||||
%>
|
||||
|
||||
<form id="navigationForm" class="navigation" action="../search/" method="get" style="font-size: 10px">
|
||||
|
||||
<% if @action_name != "published" then %>
|
||||
<%= list_item "Home Page", "../show/HomePage", "Home, Sweet Home", "H" %> |
|
||||
<%= list_item "All Pages", "../list/", "Alphabetically sorted list of pages", "A" %> |
|
||||
<%= list_item "Recently Revised", "../recently_revised/", "Pages sorted by when they were last changed", "U" %> |
|
||||
<%= list_item "Authors", "../authors/", "Who wrote what" %> |
|
||||
<%= list_item "Feeds", "../feeds/", "Subscribe to changes by RSS" %> |
|
||||
<%= list_item "Export", "../export/", "Download a zip with all the pages in this wiki", "X" %> |
|
||||
<input type="text" id="searchField" name="query" style="font-size: 10px" value="Search" onClick="this.value == 'Search' ? this.value = '' : true" />
|
||||
<% else %>
|
||||
<%= list_item "Home Page", "../published/HomePage", "Home, Sweet Home", "H" %> |
|
||||
<% end%>
|
||||
|
||||
</form>
|
||||
<%
|
||||
def list_item(title, url, description, accesskey = nil)
|
||||
if @title == title
|
||||
"<b class=\"navOn\" title=\"#{description}\" accesskey=\"#{accesskey}\">#{title}</b>"
|
||||
else
|
||||
"<a href=\"#{url}\" title=\"#{description}\" accesskey=\"#{accesskey}\">#{title}</a>"
|
||||
end
|
||||
end
|
||||
%>
|
||||
|
||||
<form id="navigationForm" class="navigation" action="../search/" method="get" style="font-size: 10px">
|
||||
|
||||
<% if @action_name != "published" then %>
|
||||
<%= list_item "Home Page", "../show/HomePage", "Home, Sweet Home", "H" %> |
|
||||
<%= list_item "All Pages", "../list/", "Alphabetically sorted list of pages", "A" %> |
|
||||
<%= list_item "Recently Revised", "../recently_revised/", "Pages sorted by when they were last changed", "U" %> |
|
||||
<%= list_item "Authors", "../authors/", "Who wrote what" %> |
|
||||
<%= list_item "Feeds", "../feeds/", "Subscribe to changes by RSS" %> |
|
||||
<%= list_item "Export", "../export/", "Download a zip with all the pages in this wiki", "X" %> |
|
||||
<input type="text" id="searchField" name="query" style="font-size: 10px" value="Search" onClick="this.value == 'Search' ? this.value = '' : true" />
|
||||
<% else %>
|
||||
<%= list_item "Home Page", "../published/HomePage", "Home, Sweet Home", "H" %> |
|
||||
<% end%>
|
||||
|
||||
</form>
|
||||
|
|
30
app/views/rdoc_help.rhtml
Executable file → Normal file
30
app/views/rdoc_help.rhtml
Executable file → Normal file
|
@ -1,16 +1,16 @@
|
|||
<div id="TextileHelp" style="float: right; width: 250px; margin-top: 5px">
|
||||
<h3>RDoc formatting tips (<a target="_new" href="http://rdoc.sourceforge.net/doc/files/markup/simple_markup_rb.html">advanced</a>)</h3>
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tr><td>_your text_</td><td class="arrow">→</td><td><em>your text</em></td></tr>
|
||||
<tr><td>*your text*</td><td class="arrow">→</td><td><strong>your text</strong></td></tr>
|
||||
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">→</td><td>• Bulleted list<br />• Second item</td></tr>
|
||||
<tr><td>1. Numbered list<br />2. Second item</td><td class="arrow">→</td><td>1. Numbered list<br />2. Second item</td></tr>
|
||||
<tr><td>+my_code+</td><td class="arrow">→</td><td><code>my_code</code></td></tr>
|
||||
<tr><td>---</td><td class="arrow">→</td><td>Horizontal ruler</td></tr>
|
||||
<tr><td>[[URL linkname]]</td><td class="arrow">→</td><td><a href="URL">linkname</a></td></tr>
|
||||
<tr><td>http://url<br />mailto:e@add.com</td><td class="arrow">→</td><td>Auto-linked</td></tr>
|
||||
<tr><td>imageURL</td><td class="arrow">→</td><td>Image</td></tr>
|
||||
</table>
|
||||
|
||||
<%= render 'wiki_words_help' %>
|
||||
<div id="TextileHelp" style="float: right; width: 250px; margin-top: 5px">
|
||||
<h3>RDoc formatting tips (<a target="_new" href="http://rdoc.sourceforge.net/doc/files/markup/simple_markup_rb.html">advanced</a>)</h3>
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tr><td>_your text_</td><td class="arrow">→</td><td><em>your text</em></td></tr>
|
||||
<tr><td>*your text*</td><td class="arrow">→</td><td><strong>your text</strong></td></tr>
|
||||
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">→</td><td>• Bulleted list<br />• Second item</td></tr>
|
||||
<tr><td>1. Numbered list<br />2. Second item</td><td class="arrow">→</td><td>1. Numbered list<br />2. Second item</td></tr>
|
||||
<tr><td>+my_code+</td><td class="arrow">→</td><td><code>my_code</code></td></tr>
|
||||
<tr><td>---</td><td class="arrow">→</td><td>Horizontal ruler</td></tr>
|
||||
<tr><td>[[URL linkname]]</td><td class="arrow">→</td><td><a href="URL">linkname</a></td></tr>
|
||||
<tr><td>http://url<br />mailto:e@add.com</td><td class="arrow">→</td><td>Auto-linked</td></tr>
|
||||
<tr><td>imageURL</td><td class="arrow">→</td><td>Image</td></tr>
|
||||
</table>
|
||||
|
||||
<%= render 'wiki_words_help' %>
|
||||
</div>
|
54
app/views/textile_help.rhtml
Executable file → Normal file
54
app/views/textile_help.rhtml
Executable file → Normal file
|
@ -1,28 +1,28 @@
|
|||
<div id="TextileHelp" style="float: right; width: 250px; margin-top: 5px">
|
||||
<h3>Textile formatting tips (<a href="#" onClick="quickRedReference(); return false;">advanced</a>)</h3>
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tr><td>_your text_</td><td class="arrow">→</td><td><em>your text</em></td></tr>
|
||||
<tr><td>*your text*</td><td class="arrow">→</td><td><strong>your text</strong></td></tr>
|
||||
<tr><td>%{color:red}hello%</td><td class="arrow">→</td><td><span style="color: red;">hello</span></td></tr>
|
||||
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">→</td><td>• Bulleted list<br />• Second item</td></tr>
|
||||
<tr><td># Numbered list<br /># Second item</td><td class="arrow">→</td><td>1. Numbered list<br />2. Second item</td></tr>
|
||||
<tr><td>"linkname":URL</td><td class="arrow">→</td><td><a href="URL">linkname</a></td></tr>
|
||||
<tr><td>|a|table|row|<br />|b|table|row|</td><td class="arrow">→</td><td>Table</td></tr>
|
||||
<tr><td>http://url<br />email@address.com</td><td class="arrow">→</td><td>Auto-linked</td></tr>
|
||||
<tr><td>!imageURL!</td><td class="arrow">→</td><td>Image</td></tr>
|
||||
</table>
|
||||
|
||||
<%= render 'wiki_words_help' %>
|
||||
</div>
|
||||
|
||||
<script language="JavaScript">
|
||||
function quickRedReference() {
|
||||
window.open(
|
||||
"http://hobix.com/textile/quick.html",
|
||||
"redRef",
|
||||
"height=600,width=550,channelmode=0,dependent=0," +
|
||||
"directories=0,fullscreen=0,location=0,menubar=0," +
|
||||
"resizable=0,scrollbars=1,status=1,toolbar=0"
|
||||
);
|
||||
}
|
||||
<div id="TextileHelp" style="float: right; width: 250px; margin-top: 5px">
|
||||
<h3>Textile formatting tips (<a href="#" onClick="quickRedReference(); return false;">advanced</a>)</h3>
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tr><td>_your text_</td><td class="arrow">→</td><td><em>your text</em></td></tr>
|
||||
<tr><td>*your text*</td><td class="arrow">→</td><td><strong>your text</strong></td></tr>
|
||||
<tr><td>%{color:red}hello%</td><td class="arrow">→</td><td><span style="color: red;">hello</span></td></tr>
|
||||
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">→</td><td>• Bulleted list<br />• Second item</td></tr>
|
||||
<tr><td># Numbered list<br /># Second item</td><td class="arrow">→</td><td>1. Numbered list<br />2. Second item</td></tr>
|
||||
<tr><td>"linkname":URL</td><td class="arrow">→</td><td><a href="URL">linkname</a></td></tr>
|
||||
<tr><td>|a|table|row|<br />|b|table|row|</td><td class="arrow">→</td><td>Table</td></tr>
|
||||
<tr><td>http://url<br />email@address.com</td><td class="arrow">→</td><td>Auto-linked</td></tr>
|
||||
<tr><td>!imageURL!</td><td class="arrow">→</td><td>Image</td></tr>
|
||||
</table>
|
||||
|
||||
<%= render 'wiki_words_help' %>
|
||||
</div>
|
||||
|
||||
<script language="JavaScript">
|
||||
function quickRedReference() {
|
||||
window.open(
|
||||
"http://hobix.com/textile/quick.html",
|
||||
"redRef",
|
||||
"height=600,width=550,channelmode=0,dependent=0," +
|
||||
"directories=0,fullscreen=0,location=0,menubar=0," +
|
||||
"resizable=0,scrollbars=1,status=1,toolbar=0"
|
||||
);
|
||||
}
|
||||
</script>
|
22
app/views/wiki/authors.rhtml
Executable file → Normal file
22
app/views/wiki/authors.rhtml
Executable file → Normal file
|
@ -1,11 +1,11 @@
|
|||
<% @title = 'Authors' %>
|
||||
|
||||
<ul id="authorList">
|
||||
<% for author in @authors %>
|
||||
<li>
|
||||
<%= @web.make_link(author) %>
|
||||
co- or authored:
|
||||
<%= @web.select.pages_authored_by(author).collect { |page| page.link }.join ', ' %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% @title = 'Authors' %>
|
||||
|
||||
<ul id="authorList">
|
||||
<% for author in @authors %>
|
||||
<li>
|
||||
<%= @web.make_link(author) %>
|
||||
co- or authored:
|
||||
<%= @web.select.pages_authored_by(author).collect { |page| page.link }.join ', ' %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
|
58
app/views/wiki/edit.rhtml
Executable file → Normal file
58
app/views/wiki/edit.rhtml
Executable file → Normal file
|
@ -1,29 +1,29 @@
|
|||
<%
|
||||
@title = "Editing #{@page.name}"
|
||||
@content_width = 720
|
||||
@hide_navigation = true
|
||||
%>
|
||||
|
||||
<%= "<p style='color:red'>Please correct the error that caused this error in rendering:<br/><small>#{@params["msg"]}</small></p>" if @params["msg"] %>
|
||||
|
||||
<%= render("#{@web.markup}_help") if @web %>
|
||||
|
||||
<form id="editForm" action="../save/<%= @page.name %>" method="post" onSubmit="cleanAuthorName();">
|
||||
<p>
|
||||
<textarea name="content" style="width: 450px; height: 500px"><%= @page.content %></textarea>
|
||||
</p>
|
||||
<p>
|
||||
<input type="submit" value="Update" /> as
|
||||
<input type="text" name="author" id="authorName" value="<%= @author %>"
|
||||
onClick="this.value == 'AnonymousCoward' ? this.value = '' : true" />
|
||||
| <a href="../cancel_edit/<%= @page.name %>">Cancel</a> <small>(unlocks page)</small>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<script language="JavaScript1.2">
|
||||
function cleanAuthorName() {
|
||||
if (document.getElementById('authorName').value == "") {
|
||||
document.getElementById('authorName').value = 'AnonymousCoward';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<%
|
||||
@title = "Editing #{@page.name}"
|
||||
@content_width = 720
|
||||
@hide_navigation = true
|
||||
%>
|
||||
|
||||
<%= "<p style='color:red'>Please correct the error that caused this error in rendering:<br/><small>#{@params["msg"]}</small></p>" if @params["msg"] %>
|
||||
|
||||
<%= render("#{@web.markup}_help") if @web %>
|
||||
|
||||
<form id="editForm" action="../save/<%= @page.name %>" method="post" onSubmit="cleanAuthorName();">
|
||||
<p>
|
||||
<textarea name="content" style="width: 450px; height: 500px"><%= @page.content %></textarea>
|
||||
</p>
|
||||
<p>
|
||||
<input type="submit" value="Update" /> as
|
||||
<input type="text" name="author" id="authorName" value="<%= @author %>"
|
||||
onClick="this.value == 'AnonymousCoward' ? this.value = '' : true" />
|
||||
| <a href="../cancel_edit/<%= @page.name %>">Cancel</a> <small>(unlocks page)</small>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<script language="JavaScript1.2">
|
||||
function cleanAuthorName() {
|
||||
if (document.getElementById('authorName').value == "") {
|
||||
document.getElementById('authorName').value = 'AnonymousCoward';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
234
app/views/wiki/edit_web.rhtml
Executable file → Normal file
234
app/views/wiki/edit_web.rhtml
Executable file → Normal file
|
@ -1,117 +1,117 @@
|
|||
<% @title = "Edit Web" %>
|
||||
|
||||
<form action="../update_web" id="setup" method="post" onSubmit="cleanAddress(); return validateSetup()">
|
||||
<h2 style="margin-bottom: 3px">Name and address</h2>
|
||||
<div class="help">
|
||||
The name of the web is included in the title on all pages.
|
||||
The address is the base path that all pages within the web live beneath.
|
||||
Ex: the address "rails" gives URLs like <i>/rails/show/HomePage</i>.
|
||||
</div>
|
||||
|
||||
<div class="inputBox, disableAutoComplete">
|
||||
Name: <input type="text" id="name" name="name" value="<%= @web.name %>"
|
||||
onChange="proposeAddress();" />
|
||||
Address: <input type="text" id="address" name="address" value="<%= @web.address %>"
|
||||
onChange="cleanAddress();" />
|
||||
<i>(Letters and digits only)</i>
|
||||
</div>
|
||||
|
||||
<h2 style="margin-bottom: 3px">Specialize</h2>
|
||||
<div class="help">
|
||||
Turning safe mode on will strip HTML tags and stylesheet options from the content of all pages.
|
||||
Turning on "brackets only" will require all wiki words to be as [[wiki word]] and WikiWord
|
||||
won't work.
|
||||
Turning "allow uploads" on will let wiki users to upload pictures and other files to the wiki
|
||||
and include them on wiki pages.
|
||||
Additions to the stylesheet take precedence over the existing styles.
|
||||
<i>Hint:</i> View source on a page you want to style to find ID names on individual tags.
|
||||
<a href="#" onClick="document.getElementById('additionalStyle').style.display='block';return false;">
|
||||
See styles >>
|
||||
</a>
|
||||
</div>
|
||||
<div class="inputBox, disableAutoComplete">
|
||||
Markup:
|
||||
<select name="markup">
|
||||
<%= html_options({"Textile" => :textile, "Markdown" => :markdown, "RDoc" => :rdoc },
|
||||
@web.markup) %>
|
||||
</select>
|
||||
|
||||
|
||||
|
||||
Color:
|
||||
<select name="color">
|
||||
<%= html_options({ "Green" => "008B26", "Purple" => "504685", "Red" => "DA0006",
|
||||
"Orange" => "FA6F00", "Grey" => "8BA2B0" }, @web.color) %>
|
||||
</select>
|
||||
|
||||
|
||||
|
||||
<small>
|
||||
<input type="checkbox" name="safe_mode" <%= 'checked="on"' if @web.safe_mode %> /> Safe mode
|
||||
|
||||
<input type="checkbox" name="brackets_only" <%= 'checked="on"' if @web.brackets_only %> />
|
||||
Brackets only
|
||||
|
||||
<input type="checkbox" name="count_pages" <%= 'checked="on"' if @web.count_pages %> /> Count pages
|
||||
|
||||
<input type="checkbox" name="allow_uploads" <%= 'checked="on"' if @web.allow_uploads %> /> Allow uploads
|
||||
</small>
|
||||
|
||||
<textarea id="additionalStyle"
|
||||
style="display: none; margin-top: 10px; margin-bottom: 5px; width: 560px; height: 200px"
|
||||
name="additional_style"><%= @web.additional_style %>
|
||||
</textarea>
|
||||
</div>
|
||||
|
||||
<h2 style="margin-bottom: 3px">Password protection for this web (<%= @web.name %>)</h2>
|
||||
<div class="help">
|
||||
This is the password that visitors need to view and edit this web.
|
||||
Setting the password to nothing will remove the password protection.
|
||||
</div>
|
||||
<div class="inputBox">
|
||||
Password: <input class="disableAutoComplete" type="password" id="password"
|
||||
name="password" value="<%= @web.password %>" />
|
||||
|
||||
Verify: <input class="disableAutoComplete" type="password" id="password_check"
|
||||
value="<%= @web.password %>" name="password_check" />
|
||||
</div>
|
||||
|
||||
<h2 style="margin-bottom: 3px">Publish read-only version of this web (<%= @web.name %>)</h2>
|
||||
<div class="help">
|
||||
You can turn on a read-only version of this web that's accessible even when the regular web
|
||||
is password protected.
|
||||
The published version is accessible through URLs like /wiki/published/HomePage.
|
||||
</div>
|
||||
<div class="inputBox">
|
||||
<input type="checkbox" name="published" <%= 'checked="on"' if @web.published %> /> Publish this web
|
||||
</div>
|
||||
|
||||
<p align="right">
|
||||
<small>
|
||||
Enter system password
|
||||
<input type="password" class="disableAutoComplete" id="system_password" name="system_password" />
|
||||
and
|
||||
<input type="submit" value="Update Web" />
|
||||
<br/><br/>
|
||||
...or forget changes and <a href="/new_web/">create a new web</a>
|
||||
</small>
|
||||
</p>
|
||||
|
||||
</form>
|
||||
|
||||
<br/>
|
||||
<h1>Other administrative tasks</h1>
|
||||
|
||||
<form action="../remove_orphaned_pages" id="remove_orphaned_pages" method="post">
|
||||
<p align="right">
|
||||
<small>
|
||||
Clean up by entering system password
|
||||
<input type="password" id="system_password" name="system_password" />
|
||||
and
|
||||
<input type="submit" value="Delete Orphan Pages" />
|
||||
</small>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript" src="/javascripts/edit_web.js" />
|
||||
<script type="text/javascript">overrideAutocomplete()</script>
|
||||
<% @title = "Edit Web" %>
|
||||
|
||||
<form action="../update_web" id="setup" method="post" onSubmit="cleanAddress(); return validateSetup()">
|
||||
<h2 style="margin-bottom: 3px">Name and address</h2>
|
||||
<div class="help">
|
||||
The name of the web is included in the title on all pages.
|
||||
The address is the base path that all pages within the web live beneath.
|
||||
Ex: the address "rails" gives URLs like <i>/rails/show/HomePage</i>.
|
||||
</div>
|
||||
|
||||
<div class="inputBox, disableAutoComplete">
|
||||
Name: <input type="text" id="name" name="name" value="<%= @web.name %>"
|
||||
onChange="proposeAddress();" />
|
||||
Address: <input type="text" id="address" name="address" value="<%= @web.address %>"
|
||||
onChange="cleanAddress();" />
|
||||
<i>(Letters and digits only)</i>
|
||||
</div>
|
||||
|
||||
<h2 style="margin-bottom: 3px">Specialize</h2>
|
||||
<div class="help">
|
||||
Turning safe mode on will strip HTML tags and stylesheet options from the content of all pages.
|
||||
Turning on "brackets only" will require all wiki words to be as [[wiki word]] and WikiWord
|
||||
won't work.
|
||||
Turning "allow uploads" on will let wiki users to upload pictures and other files to the wiki
|
||||
and include them on wiki pages.
|
||||
Additions to the stylesheet take precedence over the existing styles.
|
||||
<i>Hint:</i> View source on a page you want to style to find ID names on individual tags.
|
||||
<a href="#" onClick="document.getElementById('additionalStyle').style.display='block';return false;">
|
||||
See styles >>
|
||||
</a>
|
||||
</div>
|
||||
<div class="inputBox, disableAutoComplete">
|
||||
Markup:
|
||||
<select name="markup">
|
||||
<%= html_options({"Textile" => :textile, "Markdown" => :markdown, "RDoc" => :rdoc },
|
||||
@web.markup) %>
|
||||
</select>
|
||||
|
||||
|
||||
|
||||
Color:
|
||||
<select name="color">
|
||||
<%= html_options({ "Green" => "008B26", "Purple" => "504685", "Red" => "DA0006",
|
||||
"Orange" => "FA6F00", "Grey" => "8BA2B0" }, @web.color) %>
|
||||
</select>
|
||||
|
||||
|
||||
|
||||
<small>
|
||||
<input type="checkbox" name="safe_mode" <%= 'checked="on"' if @web.safe_mode %> /> Safe mode
|
||||
|
||||
<input type="checkbox" name="brackets_only" <%= 'checked="on"' if @web.brackets_only %> />
|
||||
Brackets only
|
||||
|
||||
<input type="checkbox" name="count_pages" <%= 'checked="on"' if @web.count_pages %> /> Count pages
|
||||
|
||||
<input type="checkbox" name="allow_uploads" <%= 'checked="on"' if @web.allow_uploads %> /> Allow uploads
|
||||
</small>
|
||||
|
||||
<textarea id="additionalStyle"
|
||||
style="display: none; margin-top: 10px; margin-bottom: 5px; width: 560px; height: 200px"
|
||||
name="additional_style"><%= @web.additional_style %>
|
||||
</textarea>
|
||||
</div>
|
||||
|
||||
<h2 style="margin-bottom: 3px">Password protection for this web (<%= @web.name %>)</h2>
|
||||
<div class="help">
|
||||
This is the password that visitors need to view and edit this web.
|
||||
Setting the password to nothing will remove the password protection.
|
||||
</div>
|
||||
<div class="inputBox">
|
||||
Password: <input class="disableAutoComplete" type="password" id="password"
|
||||
name="password" value="<%= @web.password %>" />
|
||||
|
||||
Verify: <input class="disableAutoComplete" type="password" id="password_check"
|
||||
value="<%= @web.password %>" name="password_check" />
|
||||
</div>
|
||||
|
||||
<h2 style="margin-bottom: 3px">Publish read-only version of this web (<%= @web.name %>)</h2>
|
||||
<div class="help">
|
||||
You can turn on a read-only version of this web that's accessible even when the regular web
|
||||
is password protected.
|
||||
The published version is accessible through URLs like /wiki/published/HomePage.
|
||||
</div>
|
||||
<div class="inputBox">
|
||||
<input type="checkbox" name="published" <%= 'checked="on"' if @web.published %> /> Publish this web
|
||||
</div>
|
||||
|
||||
<p align="right">
|
||||
<small>
|
||||
Enter system password
|
||||
<input type="password" class="disableAutoComplete" id="system_password" name="system_password" />
|
||||
and
|
||||
<input type="submit" value="Update Web" />
|
||||
<br/><br/>
|
||||
...or forget changes and <a href="/new_web/">create a new web</a>
|
||||
</small>
|
||||
</p>
|
||||
|
||||
</form>
|
||||
|
||||
<br/>
|
||||
<h1>Other administrative tasks</h1>
|
||||
|
||||
<form action="../remove_orphaned_pages" id="remove_orphaned_pages" method="post">
|
||||
<p align="right">
|
||||
<small>
|
||||
Clean up by entering system password
|
||||
<input type="password" id="system_password" name="system_password" />
|
||||
and
|
||||
<input type="submit" value="Delete Orphan Pages" />
|
||||
</small>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript" src="/javascripts/edit_web.js" />
|
||||
<script type="text/javascript">overrideAutocomplete()</script>
|
||||
|
|
24
app/views/wiki/export.rhtml
Executable file → Normal file
24
app/views/wiki/export.rhtml
Executable file → Normal file
|
@ -1,12 +1,12 @@
|
|||
<% @title = "Export" %>
|
||||
|
||||
<p>You can export all the pages in this web as a zip file in either HTML (with working links and all) or the pure markup (to import in another wiki).</p>
|
||||
|
||||
<ul id="feedsList">
|
||||
<li><a href="../export_html">HTML</a>
|
||||
<li><a href="../export_markup">Markup (<%= @web.markup %>)</a>
|
||||
<% if OPTIONS[:pdflatex] && @web.markup == :textile %>
|
||||
<li><a href="../export_tex">TeX</a>
|
||||
<li><a href="../export_pdf">PDF</a>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% @title = "Export" %>
|
||||
|
||||
<p>You can export all the pages in this web as a zip file in either HTML (with working links and all) or the pure markup (to import in another wiki).</p>
|
||||
|
||||
<ul id="feedsList">
|
||||
<li><a href="../export_html">HTML</a>
|
||||
<li><a href="../export_markup">Markup (<%= @web.markup %>)</a>
|
||||
<% if OPTIONS[:pdflatex] && @web.markup == :textile %>
|
||||
<li><a href="../export_tex">TeX</a>
|
||||
<li><a href="../export_pdf">PDF</a>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
|
16
app/views/wiki/feeds.rhtml
Executable file → Normal file
16
app/views/wiki/feeds.rhtml
Executable file → Normal file
|
@ -1,8 +1,8 @@
|
|||
<% @title = "Feeds" %>
|
||||
|
||||
<p>You can subscribe to this wiki by RSS and get either just the headlines of the pages that change or the entire page.</p>
|
||||
|
||||
<ul id="feedsList">
|
||||
<li><a href="../rss_with_content<%= "?password=#{web.password}" if @web.password %>">Full content (RSS 2.0)</a>
|
||||
<li><a href="../rss_with_headlines<%= "?password=#{web.password}" if @web.password %>">Headlines (RSS 2.0)</a>
|
||||
</ul>
|
||||
<% @title = "Feeds" %>
|
||||
|
||||
<p>You can subscribe to this wiki by RSS and get either just the headlines of the pages that change or the entire page.</p>
|
||||
|
||||
<ul id="feedsList">
|
||||
<li><a href="../rss_with_content<%= "?password=#{web.password}" if @web.password %>">Full content (RSS 2.0)</a>
|
||||
<li><a href="../rss_with_headlines<%= "?password=#{web.password}" if @web.password %>">Headlines (RSS 2.0)</a>
|
||||
</ul>
|
||||
|
|
114
app/views/wiki/list.rhtml
Executable file → Normal file
114
app/views/wiki/list.rhtml
Executable file → Normal file
|
@ -1,57 +1,57 @@
|
|||
<% @title = "All Pages" %>
|
||||
|
||||
<% unless @categories.empty? %>
|
||||
<div id="categories">
|
||||
<strong>Categories</strong>:
|
||||
[<a href=".">Any</a>]
|
||||
<%= @category_links.join(', ') %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div id="allPages" style="float: left; width: 280px; margin-right: 30px">
|
||||
<% unless @pages_that_are_orphaned.empty? && @page_names_that_are_wanted.empty? %>
|
||||
<h2>
|
||||
All Pages
|
||||
<br/><small style="font-size: 12px"><i>All pages in <%= @set_name %> listed alphabetically</i></small>
|
||||
</h2>
|
||||
<% end %>
|
||||
|
||||
<ul><% for page in @pages_by_name %>
|
||||
<li><a href="../show/<%= page.name %>"><%= truncate(page.plain_name, 35) %></a></li>
|
||||
<% end %></ul>
|
||||
|
||||
<% if @web.count_pages %>
|
||||
<% total_chars = @pages_in_category.characters %>
|
||||
<p><small>All content: <%= total_chars %> chars / <%= sprintf("%-.1f", (total_chars / 2275 )) %> pages</small></p>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div style="float: left; width: 280px">
|
||||
<% unless @page_names_that_are_wanted.empty? %>
|
||||
<h2>
|
||||
Wanted Pages
|
||||
<br/><small style="font-size: 12px"><i>Unexisting pages that other pages in <%= @set_name %> reference</i></small>
|
||||
</h2>
|
||||
|
||||
<ul style="margin-bottom: 10px">
|
||||
<% for page_name in @page_names_that_are_wanted %>
|
||||
<li>
|
||||
<a href="../show/<%= page_name %>"><%= truncate(WikiWords.separate(page_name), 35) %></a>
|
||||
wanted by
|
||||
<%= @web.select.pages_that_reference(page_name).collect { |page| page.link }.join(", ") %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% end %>
|
||||
|
||||
<% unless @pages_that_are_orphaned.empty? %>
|
||||
<h2>
|
||||
Orphaned Pages
|
||||
<br/><small style="font-size: 12px"><i>Pages in <%= @set_name %> that no other page reference</i></small>
|
||||
</h2>
|
||||
|
||||
<ul style="margin-bottom: 35px">
|
||||
<% for page in @pages_that_are_orphaned %><li><a href="../show/<%= page.name %>"><%= truncate(page.plain_name, 35) %></a></li><% end %>
|
||||
</ul>
|
||||
<% end %>
|
||||
</div>
|
||||
<% @title = "All Pages" %>
|
||||
|
||||
<% unless @categories.empty? %>
|
||||
<div id="categories">
|
||||
<strong>Categories</strong>:
|
||||
[<a href=".">Any</a>]
|
||||
<%= @category_links.join(', ') %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div id="allPages" style="float: left; width: 280px; margin-right: 30px">
|
||||
<% unless @pages_that_are_orphaned.empty? && @page_names_that_are_wanted.empty? %>
|
||||
<h2>
|
||||
All Pages
|
||||
<br/><small style="font-size: 12px"><i>All pages in <%= @set_name %> listed alphabetically</i></small>
|
||||
</h2>
|
||||
<% end %>
|
||||
|
||||
<ul><% for page in @pages_by_name %>
|
||||
<li><a href="../show/<%= page.name %>"><%= truncate(page.plain_name, 35) %></a></li>
|
||||
<% end %></ul>
|
||||
|
||||
<% if @web.count_pages %>
|
||||
<% total_chars = @pages_in_category.characters %>
|
||||
<p><small>All content: <%= total_chars %> chars / <%= sprintf("%-.1f", (total_chars / 2275 )) %> pages</small></p>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div style="float: left; width: 280px">
|
||||
<% unless @page_names_that_are_wanted.empty? %>
|
||||
<h2>
|
||||
Wanted Pages
|
||||
<br/><small style="font-size: 12px"><i>Unexisting pages that other pages in <%= @set_name %> reference</i></small>
|
||||
</h2>
|
||||
|
||||
<ul style="margin-bottom: 10px">
|
||||
<% for page_name in @page_names_that_are_wanted %>
|
||||
<li>
|
||||
<a href="../show/<%= page_name %>"><%= truncate(WikiWords.separate(page_name), 35) %></a>
|
||||
wanted by
|
||||
<%= @web.select.pages_that_reference(page_name).collect { |page| page.link }.join(", ") %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% end %>
|
||||
|
||||
<% unless @pages_that_are_orphaned.empty? %>
|
||||
<h2>
|
||||
Orphaned Pages
|
||||
<br/><small style="font-size: 12px"><i>Pages in <%= @set_name %> that no other page reference</i></small>
|
||||
</h2>
|
||||
|
||||
<ul style="margin-bottom: 35px">
|
||||
<% for page in @pages_that_are_orphaned %><li><a href="../show/<%= page.name %>"><%= truncate(page.plain_name, 35) %></a></li><% end %>
|
||||
</ul>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
40
app/views/wiki/locked.rhtml
Executable file → Normal file
40
app/views/wiki/locked.rhtml
Executable file → Normal file
|
@ -1,20 +1,20 @@
|
|||
<% @title = "#{@page.plain_name} is locked" %>
|
||||
|
||||
<% if @page.lock_duration(Time.now) == 0 %>
|
||||
<p><%= @page.locked_by_link %> just started editing this page.</p>
|
||||
<% else %>
|
||||
<p><%= @page.locked_by_link %> has been editing this page for <%= @page.lock_duration(Time.now) %> minutes.</p>
|
||||
<% end %>
|
||||
|
||||
<p>
|
||||
<%= link_to 'Edit the page anyway',
|
||||
{:web => @web_name, :action => 'edit', :id => @page.name, :params => {'break_lock' => '1'} },
|
||||
{:accesskey => 'E'}
|
||||
%>
|
||||
|
||||
<%= link_to 'Cancel',
|
||||
{:web => @web_name, :action => 'show', :id => @page.name},
|
||||
{:accesskey => 'C'}
|
||||
%>
|
||||
|
||||
</p>
|
||||
<% @title = "#{@page.plain_name} is locked" %>
|
||||
|
||||
<% if @page.lock_duration(Time.now) == 0 %>
|
||||
<p><%= @page.locked_by_link %> just started editing this page.</p>
|
||||
<% else %>
|
||||
<p><%= @page.locked_by_link %> has been editing this page for <%= @page.lock_duration(Time.now) %> minutes.</p>
|
||||
<% end %>
|
||||
|
||||
<p>
|
||||
<%= link_to 'Edit the page anyway',
|
||||
{:web => @web_name, :action => 'edit', :id => @page.name, :params => {'break_lock' => '1'} },
|
||||
{:accesskey => 'E'}
|
||||
%>
|
||||
|
||||
<%= link_to 'Cancel',
|
||||
{:web => @web_name, :action => 'show', :id => @page.name},
|
||||
{:accesskey => 'C'}
|
||||
%>
|
||||
|
||||
</p>
|
||||
|
|
16
app/views/wiki/login.rhtml
Executable file → Normal file
16
app/views/wiki/login.rhtml
Executable file → Normal file
|
@ -1,8 +1,8 @@
|
|||
<% @title = "#{@web_name} Login" %><% @hide_navigation = true %>
|
||||
|
||||
<form action="authenticate" method="post">
|
||||
<p>
|
||||
<b>Password</b><br />
|
||||
<input type="password" name="password" />
|
||||
</p>
|
||||
</form>
|
||||
<% @title = "#{@web_name} Login" %><% @hide_navigation = true %>
|
||||
|
||||
<form action="authenticate" method="post">
|
||||
<p>
|
||||
<b>Password</b><br />
|
||||
<input type="password" name="password" />
|
||||
</p>
|
||||
</form>
|
||||
|
|
50
app/views/wiki/new.rhtml
Executable file → Normal file
50
app/views/wiki/new.rhtml
Executable file → Normal file
|
@ -1,25 +1,25 @@
|
|||
<%
|
||||
@title = "Creating #{WikiWords.separate(CGI.unescape(@page_name))}"
|
||||
@content_width = 720
|
||||
@hide_navigation = true
|
||||
%>
|
||||
|
||||
<%= render("#{@web.markup}_help") if @web %>
|
||||
|
||||
<form action="../save/<%= @page_name %>" method="post" onSubmit="cleanAuthorName();">
|
||||
<p>
|
||||
<textarea name="content" style="width: 450px; height: 500px"></textarea>
|
||||
</p>
|
||||
<p>
|
||||
<input type="submit" value="Create" /> as
|
||||
<input type="text" name="author" id="authorName" value="<%= @author %>" onClick="this.value == 'AnonymousCoward' ? this.value = '' : true" />
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<script language="JavaScript1.2">
|
||||
function cleanAuthorName() {
|
||||
if (document.getElementById('authorName').value == "") {
|
||||
document.getElementById('authorName').value = 'AnonymousCoward';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<%
|
||||
@title = "Creating #{WikiWords.separate(CGI.unescape(@page_name))}"
|
||||
@content_width = 720
|
||||
@hide_navigation = true
|
||||
%>
|
||||
|
||||
<%= render("#{@web.markup}_help") if @web %>
|
||||
|
||||
<form action="../save/<%= @page_name %>" method="post" onSubmit="cleanAuthorName();">
|
||||
<p>
|
||||
<textarea name="content" style="width: 450px; height: 500px"></textarea>
|
||||
</p>
|
||||
<p>
|
||||
<input type="submit" value="Create" /> as
|
||||
<input type="text" name="author" id="authorName" value="<%= @author %>" onClick="this.value == 'AnonymousCoward' ? this.value = '' : true" />
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<script language="JavaScript1.2">
|
||||
function cleanAuthorName() {
|
||||
if (document.getElementById('authorName').value == "") {
|
||||
document.getElementById('authorName').value = 'AnonymousCoward';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
166
app/views/wiki/new_system.rhtml
Executable file → Normal file
166
app/views/wiki/new_system.rhtml
Executable file → Normal file
|
@ -1,83 +1,83 @@
|
|||
<% @title = "Instiki Setup"; @content_width = 500 %>
|
||||
|
||||
<p>
|
||||
Congratulations on succesfully installing and starting Instiki.
|
||||
Since this is the first time Instiki has been run on this port,
|
||||
you'll need to do a brief one-time setup.
|
||||
</p>
|
||||
|
||||
<form action="../create_system" id="setup" method="post" onSubmit="return validateSetup()">
|
||||
<ol class="setup">
|
||||
<li>
|
||||
|
||||
<h2 style="margin-bottom: 3px">Name and address for your first web</h2>
|
||||
<div class="help">
|
||||
The name of the web is included in the title on all pages.
|
||||
The address is the base path that all pages within the web live beneath.
|
||||
Ex: the address "rails" gives URLs like <i>/rails/show/HomePage</i>.
|
||||
The address can only consist of letters and digits.
|
||||
</div>
|
||||
<div class="inputBox">
|
||||
Name: <input type="text" id="web_name" name="web_name" value="Wiki"
|
||||
onChange="proposeAddress();" onClick="this.value == 'Wiki' ? this.value = '' : true" />
|
||||
|
||||
Address: <input type="text" id="web_address" name="web_address" onChange="cleanAddress();"
|
||||
value="wiki" />
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<h2 style="margin-bottom: 3px">Password for creating and changing webs</h2>
|
||||
<div class="help">
|
||||
Administrative access allows you to make new webs and change existing ones.<br/>
|
||||
Everyone with this password will be able to do this, so pick it carefully.
|
||||
</div>
|
||||
<div class="inputBox">
|
||||
Password: <input type="password" id="password" name="password" />
|
||||
|
||||
Verify: <input type="password" id="password_check" name="password_check" />
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<p align="right">
|
||||
<input type="submit" value="Setup" style="margin-left: 40px" />
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function proposeAddress() {
|
||||
document.getElementById('web_address').value =
|
||||
document.getElementById('web_name').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
||||
}
|
||||
|
||||
function cleanAddress() {
|
||||
document.getElementById('web_address').value =
|
||||
document.getElementById('web_address').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
||||
}
|
||||
|
||||
function validateSetup() {
|
||||
if (document.getElementById('web_name').value == "") {
|
||||
alert("You must pick a name for the first web");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('web_address').value == "") {
|
||||
alert("You must pick an address for the first web");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('password').value == "") {
|
||||
alert("You must pick a system password");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('password_check').value == "" ||
|
||||
document.getElementById('password').value != document.getElementById('password_check').value) {
|
||||
alert("The password and its verification doesn't match");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
</script>
|
||||
<% @title = "Instiki Setup"; @content_width = 500 %>
|
||||
|
||||
<p>
|
||||
Congratulations on succesfully installing and starting Instiki.
|
||||
Since this is the first time Instiki has been run on this port,
|
||||
you'll need to do a brief one-time setup.
|
||||
</p>
|
||||
|
||||
<form action="../create_system" id="setup" method="post" onSubmit="return validateSetup()">
|
||||
<ol class="setup">
|
||||
<li>
|
||||
|
||||
<h2 style="margin-bottom: 3px">Name and address for your first web</h2>
|
||||
<div class="help">
|
||||
The name of the web is included in the title on all pages.
|
||||
The address is the base path that all pages within the web live beneath.
|
||||
Ex: the address "rails" gives URLs like <i>/rails/show/HomePage</i>.
|
||||
The address can only consist of letters and digits.
|
||||
</div>
|
||||
<div class="inputBox">
|
||||
Name: <input type="text" id="web_name" name="web_name" value="Wiki"
|
||||
onChange="proposeAddress();" onClick="this.value == 'Wiki' ? this.value = '' : true" />
|
||||
|
||||
Address: <input type="text" id="web_address" name="web_address" onChange="cleanAddress();"
|
||||
value="wiki" />
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<h2 style="margin-bottom: 3px">Password for creating and changing webs</h2>
|
||||
<div class="help">
|
||||
Administrative access allows you to make new webs and change existing ones.<br/>
|
||||
Everyone with this password will be able to do this, so pick it carefully.
|
||||
</div>
|
||||
<div class="inputBox">
|
||||
Password: <input type="password" id="password" name="password" />
|
||||
|
||||
Verify: <input type="password" id="password_check" name="password_check" />
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<p align="right">
|
||||
<input type="submit" value="Setup" style="margin-left: 40px" />
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function proposeAddress() {
|
||||
document.getElementById('web_address').value =
|
||||
document.getElementById('web_name').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
||||
}
|
||||
|
||||
function cleanAddress() {
|
||||
document.getElementById('web_address').value =
|
||||
document.getElementById('web_address').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
||||
}
|
||||
|
||||
function validateSetup() {
|
||||
if (document.getElementById('web_name').value == "") {
|
||||
alert("You must pick a name for the first web");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('web_address').value == "") {
|
||||
alert("You must pick an address for the first web");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('password').value == "") {
|
||||
alert("You must pick a system password");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('password_check').value == "" ||
|
||||
document.getElementById('password').value != document.getElementById('password_check').value) {
|
||||
alert("The password and its verification doesn't match");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
</script>
|
||||
|
|
138
app/views/wiki/new_web.rhtml
Executable file → Normal file
138
app/views/wiki/new_web.rhtml
Executable file → Normal file
|
@ -1,69 +1,69 @@
|
|||
<% @title = "New Wiki Web"; @content_width = 500 %>
|
||||
|
||||
<p>
|
||||
Each web serves as an isolated name space for wiki pages,
|
||||
so different subjects or projects can write about different <i>MuppetShows</i>.
|
||||
</p>
|
||||
|
||||
<form action="../create_web" id="setup" method="post" onSubmit="cleanAddress();
|
||||
return validateSetup()">
|
||||
|
||||
<ol class="setup">
|
||||
<li>
|
||||
<h2 style="margin-bottom: 3px">Name and address for your new web</h2>
|
||||
<div class="help">
|
||||
The name of the web is included in the title on all pages.
|
||||
The address is the base path that all pages within the web live beneath.
|
||||
Ex: the address "rails" gives URLs like <i>/rails/show/HomePage</i>.
|
||||
The address can only consist of letters and digits.
|
||||
</div>
|
||||
<div class="inputBox">
|
||||
Name: <input type="text" id="web_name" name="name" onChange="proposeAddress();" />
|
||||
|
||||
Address: <input type="text" id="web_address" name="address" onChange="cleanAddress();" />
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
|
||||
<p align="right">
|
||||
<small>
|
||||
Enter system password
|
||||
<input type="password" id="system_password" name="system_password" />
|
||||
and
|
||||
<input type="submit" value="Create Web" />
|
||||
</small>
|
||||
</p>
|
||||
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function proposeAddress() {
|
||||
document.getElementById('web_address').value =
|
||||
document.getElementById('web_name').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
||||
}
|
||||
|
||||
function cleanAddress() {
|
||||
document.getElementById('web_address').value =
|
||||
document.getElementById('web_address').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
||||
}
|
||||
|
||||
function validateSetup() {
|
||||
if (document.getElementById('web_name').value == "") {
|
||||
alert("You must pick a name for the new web");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('web_address').value == "") {
|
||||
alert("You must pick an address for the new web");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('system_password').value == "") {
|
||||
alert("You must enter the system password");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
</script>
|
||||
<% @title = "New Wiki Web"; @content_width = 500 %>
|
||||
|
||||
<p>
|
||||
Each web serves as an isolated name space for wiki pages,
|
||||
so different subjects or projects can write about different <i>MuppetShows</i>.
|
||||
</p>
|
||||
|
||||
<form action="../create_web" id="setup" method="post" onSubmit="cleanAddress();
|
||||
return validateSetup()">
|
||||
|
||||
<ol class="setup">
|
||||
<li>
|
||||
<h2 style="margin-bottom: 3px">Name and address for your new web</h2>
|
||||
<div class="help">
|
||||
The name of the web is included in the title on all pages.
|
||||
The address is the base path that all pages within the web live beneath.
|
||||
Ex: the address "rails" gives URLs like <i>/rails/show/HomePage</i>.
|
||||
The address can only consist of letters and digits.
|
||||
</div>
|
||||
<div class="inputBox">
|
||||
Name: <input type="text" id="web_name" name="name" onChange="proposeAddress();" />
|
||||
|
||||
Address: <input type="text" id="web_address" name="address" onChange="cleanAddress();" />
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
|
||||
<p align="right">
|
||||
<small>
|
||||
Enter system password
|
||||
<input type="password" id="system_password" name="system_password" />
|
||||
and
|
||||
<input type="submit" value="Create Web" />
|
||||
</small>
|
||||
</p>
|
||||
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function proposeAddress() {
|
||||
document.getElementById('web_address').value =
|
||||
document.getElementById('web_name').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
||||
}
|
||||
|
||||
function cleanAddress() {
|
||||
document.getElementById('web_address').value =
|
||||
document.getElementById('web_address').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
||||
}
|
||||
|
||||
function validateSetup() {
|
||||
if (document.getElementById('web_name').value == "") {
|
||||
alert("You must pick a name for the new web");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('web_address').value == "") {
|
||||
alert("You must pick an address for the new web");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('system_password').value == "") {
|
||||
alert("You must enter the system password");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
</script>
|
||||
|
|
156
app/views/wiki/page.rhtml
Executable file → Normal file
156
app/views/wiki/page.rhtml
Executable file → Normal file
|
@ -1,78 +1,78 @@
|
|||
<% @title = @page.plain_name %>
|
||||
|
||||
<div id="revision">
|
||||
<%= @page.display_content %>
|
||||
</div>
|
||||
|
||||
<div id="changes" style="display: none">
|
||||
<p style="background: #eee; padding: 3px; border: 1px solid silver">
|
||||
<small>
|
||||
Showing changes from revision #<%= @page.number - 1 %> to #<%= @page.number %>:
|
||||
<ins class="diffins">Added</ins> | <del class="diffdel">Removed</del>
|
||||
</small>
|
||||
</p>
|
||||
|
||||
<%= @page.display_diff %>
|
||||
</div>
|
||||
|
||||
<div class="byline">
|
||||
<%= @page.revisions? ? "Revised" : "Created" %> on <%= @page.pretty_created_at %>
|
||||
by <%= @page.author_link %>
|
||||
<%= "(#{@page.author.ip})" if @page.author.respond_to?(:ip) %>
|
||||
<% if @web.count_pages %>
|
||||
<% total_chars = @page.content.length %>
|
||||
(<%= total_chars %> characters / <%= sprintf("%-.1f", (total_chars / 2275 rescue 0)) %> pages)
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="navigation">
|
||||
<% if @page.name == "HomePage" %>
|
||||
<a href="../edit/<%= @page.name %>" class="navlink" accesskey="E">Edit Page</a>
|
||||
| <a href="../edit_web/" class="navlink">Edit Web</a>
|
||||
<% else %>
|
||||
<a href="../edit/<%= @page.name %>" class="navlink" accesskey="E">Edit</a>
|
||||
<% end %>
|
||||
|
||||
<% if @page.revisions.length > 1 %>
|
||||
| <a href="../revision/<%= @page.name %>?rev=<%= @page.revisions.length - 2 %>" class="navlink" accesskey="R">Back in time</a>
|
||||
<small>(<%= @page.revisions.length - 1 %> revisions)</small>
|
||||
<% end %>
|
||||
|
||||
<% if @page.revisions.length > 1 %>
|
||||
<span id="show_changes">
|
||||
| <a href="#" onClick="toggleChanges(); return false;">See changes</a>
|
||||
</span>
|
||||
<span id="hide_changes" style="display: none">
|
||||
| <a href="#" onClick="toggleChanges(); return false;">Hide changes</a>
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<small>
|
||||
| Views: <a href="../print/<%= @page.name %>">Print</a>
|
||||
<% if defined? RedClothForTex and RedClothForTex.available? and @web.markup == :textile %>
|
||||
| <a href="../tex/<%= @page.name %>">TeX</a> | <a href="../pdf/<%= @page.name %>">PDF</a>
|
||||
<% end %>
|
||||
</small>
|
||||
|
||||
<% if @page.references.length > 0 %>
|
||||
<small>
|
||||
| Linked from: <%= @page.references.collect { |ref| ref.link }.join(", ") %>
|
||||
</small>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<script language="Javascript">
|
||||
function toggleChanges() {
|
||||
if (document.getElementById("changes").style.display == "none") {
|
||||
document.getElementById("changes").style.display = "block";
|
||||
document.getElementById("revision").style.display = "none";
|
||||
document.getElementById("show_changes").style.display = "none";
|
||||
document.getElementById("hide_changes").style.display = "inline";
|
||||
} else {
|
||||
document.getElementById("changes").style.display = "none";
|
||||
document.getElementById("revision").style.display = "block";
|
||||
document.getElementById("show_changes").style.display = "inline";
|
||||
document.getElementById("hide_changes").style.display = "none";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<% @title = @page.plain_name %>
|
||||
|
||||
<div id="revision">
|
||||
<%= @page.display_content %>
|
||||
</div>
|
||||
|
||||
<div id="changes" style="display: none">
|
||||
<p style="background: #eee; padding: 3px; border: 1px solid silver">
|
||||
<small>
|
||||
Showing changes from revision #<%= @page.number - 1 %> to #<%= @page.number %>:
|
||||
<ins class="diffins">Added</ins> | <del class="diffdel">Removed</del>
|
||||
</small>
|
||||
</p>
|
||||
|
||||
<%= @page.display_diff %>
|
||||
</div>
|
||||
|
||||
<div class="byline">
|
||||
<%= @page.revisions? ? "Revised" : "Created" %> on <%= @page.pretty_created_at %>
|
||||
by <%= @page.author_link %>
|
||||
<%= "(#{@page.author.ip})" if @page.author.respond_to?(:ip) %>
|
||||
<% if @web.count_pages %>
|
||||
<% total_chars = @page.content.length %>
|
||||
(<%= total_chars %> characters / <%= sprintf("%-.1f", (total_chars / 2275 rescue 0)) %> pages)
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="navigation">
|
||||
<% if @page.name == "HomePage" %>
|
||||
<a href="../edit/<%= @page.name %>" class="navlink" accesskey="E">Edit Page</a>
|
||||
| <a href="../edit_web/" class="navlink">Edit Web</a>
|
||||
<% else %>
|
||||
<a href="../edit/<%= @page.name %>" class="navlink" accesskey="E">Edit</a>
|
||||
<% end %>
|
||||
|
||||
<% if @page.revisions.length > 1 %>
|
||||
| <a href="../revision/<%= @page.name %>?rev=<%= @page.revisions.length - 2 %>" class="navlink" accesskey="R">Back in time</a>
|
||||
<small>(<%= @page.revisions.length - 1 %> revisions)</small>
|
||||
<% end %>
|
||||
|
||||
<% if @page.revisions.length > 1 %>
|
||||
<span id="show_changes">
|
||||
| <a href="#" onClick="toggleChanges(); return false;">See changes</a>
|
||||
</span>
|
||||
<span id="hide_changes" style="display: none">
|
||||
| <a href="#" onClick="toggleChanges(); return false;">Hide changes</a>
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<small>
|
||||
| Views: <a href="../print/<%= @page.name %>">Print</a>
|
||||
<% if defined? RedClothForTex and RedClothForTex.available? and @web.markup == :textile %>
|
||||
| <a href="../tex/<%= @page.name %>">TeX</a> | <a href="../pdf/<%= @page.name %>">PDF</a>
|
||||
<% end %>
|
||||
</small>
|
||||
|
||||
<% if @page.references.length > 0 %>
|
||||
<small>
|
||||
| Linked from: <%= @page.references.collect { |ref| ref.link }.join(", ") %>
|
||||
</small>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<script language="Javascript">
|
||||
function toggleChanges() {
|
||||
if (document.getElementById("changes").style.display == "none") {
|
||||
document.getElementById("changes").style.display = "block";
|
||||
document.getElementById("revision").style.display = "none";
|
||||
document.getElementById("show_changes").style.display = "none";
|
||||
document.getElementById("hide_changes").style.display = "inline";
|
||||
} else {
|
||||
document.getElementById("changes").style.display = "none";
|
||||
document.getElementById("revision").style.display = "block";
|
||||
document.getElementById("show_changes").style.display = "inline";
|
||||
document.getElementById("hide_changes").style.display = "none";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
28
app/views/wiki/print.rhtml
Executable file → Normal file
28
app/views/wiki/print.rhtml
Executable file → Normal file
|
@ -1,14 +1,14 @@
|
|||
<%
|
||||
@title = @page.plain_name
|
||||
@hide_navigation = true
|
||||
@style_additions = ".newWikiWord { background-color: white; font-style: italic; }"
|
||||
@inline_style = true
|
||||
%>
|
||||
|
||||
<%= @page.display_content_for_export %>
|
||||
|
||||
<div class="byline">
|
||||
<%= @page.revisions? ? "Revised" : "Created" %> on <%= @page.pretty_created_at %>
|
||||
by
|
||||
<%= @page.author_link({ :mode => :export }) %>
|
||||
</div>
|
||||
<%
|
||||
@title = @page.plain_name
|
||||
@hide_navigation = true
|
||||
@style_additions = ".newWikiWord { background-color: white; font-style: italic; }"
|
||||
@inline_style = true
|
||||
%>
|
||||
|
||||
<%= @page.display_content_for_export %>
|
||||
|
||||
<div class="byline">
|
||||
<%= @page.revisions? ? "Revised" : "Created" %> on <%= @page.pretty_created_at %>
|
||||
by
|
||||
<%= @page.author_link({ :mode => :export }) %>
|
||||
</div>
|
||||
|
|
16
app/views/wiki/published.rhtml
Executable file → Normal file
16
app/views/wiki/published.rhtml
Executable file → Normal file
|
@ -1,8 +1,8 @@
|
|||
<%
|
||||
@title = @page.plain_name
|
||||
@hide_navigation = false
|
||||
@style_additions = ".newWikiWord { background-color: white; font-style: italic; }"
|
||||
@inline_style = true
|
||||
%>
|
||||
|
||||
<%= @page.display_published %>
|
||||
<%
|
||||
@title = @page.plain_name
|
||||
@hide_navigation = false
|
||||
@style_additions = ".newWikiWord { background-color: white; font-style: italic; }"
|
||||
@inline_style = true
|
||||
%>
|
||||
|
||||
<%= @page.display_published %>
|
||||
|
|
64
app/views/wiki/recently_revised.rhtml
Executable file → Normal file
64
app/views/wiki/recently_revised.rhtml
Executable file → Normal file
|
@ -1,32 +1,32 @@
|
|||
<% @title = "Recently Revised" %>
|
||||
|
||||
<% unless @categories.empty? %>
|
||||
<div id="categories">
|
||||
<strong>Categories</strong>:
|
||||
[<a href=".">Any</a>]
|
||||
<%= @category_links.join(', ') %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% unless @pages_by_revision.empty? %>
|
||||
<% revision_date = @pages_by_revision.first.revised_on %>
|
||||
<h3><%= revision_date.strftime('%B %e, %Y') %></h3>
|
||||
<ul>
|
||||
<% for page in @pages_by_revision %>
|
||||
<% if page.revised_on < revision_date %>
|
||||
<% revision_date = page.revised_on %>
|
||||
</ul>
|
||||
<h3><%= revision_date.strftime('%B %e, %Y') %></h3>
|
||||
<ul>
|
||||
<% end %>
|
||||
<li>
|
||||
<a href="../show/<%= page.name %>"><%= page.plain_name %></a>
|
||||
<div class="byline" style="margin-bottom: 0px">
|
||||
by <%= page.author_link %>
|
||||
at <%= page.created_at.strftime "%H:%M" %>
|
||||
<%= "from #{page.author.ip}" if page.author.respond_to?(:ip) %>
|
||||
</div>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% end %>
|
||||
<% @title = "Recently Revised" %>
|
||||
|
||||
<% unless @categories.empty? %>
|
||||
<div id="categories">
|
||||
<strong>Categories</strong>:
|
||||
[<a href=".">Any</a>]
|
||||
<%= @category_links.join(', ') %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% unless @pages_by_revision.empty? %>
|
||||
<% revision_date = @pages_by_revision.first.revised_on %>
|
||||
<h3><%= revision_date.strftime('%B %e, %Y') %></h3>
|
||||
<ul>
|
||||
<% for page in @pages_by_revision %>
|
||||
<% if page.revised_on < revision_date %>
|
||||
<% revision_date = page.revised_on %>
|
||||
</ul>
|
||||
<h3><%= revision_date.strftime('%B %e, %Y') %></h3>
|
||||
<ul>
|
||||
<% end %>
|
||||
<li>
|
||||
<a href="../show/<%= page.name %>"><%= page.plain_name %></a>
|
||||
<div class="byline" style="margin-bottom: 0px">
|
||||
by <%= page.author_link %>
|
||||
at <%= page.created_at.strftime "%H:%M" %>
|
||||
<%= "from #{page.author.ip}" if page.author.respond_to?(:ip) %>
|
||||
</div>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% end %>
|
||||
|
|
158
app/views/wiki/revision.rhtml
Executable file → Normal file
158
app/views/wiki/revision.rhtml
Executable file → Normal file
|
@ -1,79 +1,79 @@
|
|||
<% @title = "#{@page.plain_name} (Rev ##{@revision.number})" %>
|
||||
|
||||
<div id="revision">
|
||||
<%= @revision.display_content %>
|
||||
</div>
|
||||
|
||||
<div id="changes" style="display: none">
|
||||
<p style="background: #eee; padding: 3px; border: 1px solid silver">
|
||||
<small>
|
||||
Showing changes from revision #<%= @revision.number - 1 %> to #<%= @revision.number %>:
|
||||
<ins class="diffins">Added</ins> | <del class="diffdel">Removed</del>
|
||||
</small>
|
||||
</p>
|
||||
|
||||
<%= @revision.display_diff %>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="byline">
|
||||
<%= "Revision from #{@revision.pretty_created_at} by" %>
|
||||
<%= @page.web.make_link(@revision.author) %>
|
||||
</div>
|
||||
|
||||
<div class="navigation">
|
||||
|
||||
<% if @revision.next_revision %>
|
||||
<% if @revision.next_revision.number < (@page.revisions.length - 1) %>
|
||||
<a href="../revision/<%= @page.name %>?rev=<%= @revision.next_revision.number %>" class="navlink">
|
||||
<% else %>
|
||||
<a href="../show/<%= @page.name %>" class="navlink">
|
||||
<% end %>
|
||||
Forward in time</a>
|
||||
(<%= @revision.page.revisions.length - @revision.next_revision.number %> more)
|
||||
<% end %>
|
||||
|
||||
<% if @revision.next_revision && @revision.previous_revision %>
|
||||
|
|
||||
<% end %>
|
||||
|
||||
<% if @revision.previous_revision %>
|
||||
<a href="../revision/<%= @page.name %>?rev=<%= @revision.previous_revision.number %>" class="navlink">Back in time</a>
|
||||
(<%= @revision.previous_revision.number + 1 %> more)
|
||||
<% end %>
|
||||
|
||||
| <a href="../show/<%= @page.name %>" class="navlink">See current</a>
|
||||
|
||||
<% if @revision.previous_revision %>
|
||||
<span id="show_changes">
|
||||
| <a href="#" onClick="toggleChanges(); return false;">See changes</a>
|
||||
</span>
|
||||
<span id="hide_changes" style="display: none">
|
||||
| <a href="#" onClick="toggleChanges(); return false;">Hide changes</a>
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
| <a href="../rollback/<%= @page.name %>?rev=<%= @revision.number %>" class="navlink">Rollback</a>
|
||||
|
||||
<% if @page.references.length > 0 %>
|
||||
<small>
|
||||
| Linked from: <%= @page.references.collect { |ref| "<a href='#{ref.name}'>#{ref.name}</a>" }.join(", ") %>
|
||||
</small>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<script language="Javascript">
|
||||
function toggleChanges() {
|
||||
if (document.getElementById("changes").style.display == "none") {
|
||||
document.getElementById("changes").style.display = "block";
|
||||
document.getElementById("revision").style.display = "none";
|
||||
document.getElementById("show_changes").style.display = "none";
|
||||
document.getElementById("hide_changes").style.display = "inline";
|
||||
} else {
|
||||
document.getElementById("changes").style.display = "none";
|
||||
document.getElementById("revision").style.display = "block";
|
||||
document.getElementById("show_changes").style.display = "inline";
|
||||
document.getElementById("hide_changes").style.display = "none";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<% @title = "#{@page.plain_name} (Rev ##{@revision.number})" %>
|
||||
|
||||
<div id="revision">
|
||||
<%= @revision.display_content %>
|
||||
</div>
|
||||
|
||||
<div id="changes" style="display: none">
|
||||
<p style="background: #eee; padding: 3px; border: 1px solid silver">
|
||||
<small>
|
||||
Showing changes from revision #<%= @revision.number - 1 %> to #<%= @revision.number %>:
|
||||
<ins class="diffins">Added</ins> | <del class="diffdel">Removed</del>
|
||||
</small>
|
||||
</p>
|
||||
|
||||
<%= @revision.display_diff %>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="byline">
|
||||
<%= "Revision from #{@revision.pretty_created_at} by" %>
|
||||
<%= @page.web.make_link(@revision.author) %>
|
||||
</div>
|
||||
|
||||
<div class="navigation">
|
||||
|
||||
<% if @revision.next_revision %>
|
||||
<% if @revision.next_revision.number < (@page.revisions.length - 1) %>
|
||||
<a href="../revision/<%= @page.name %>?rev=<%= @revision.next_revision.number %>" class="navlink">
|
||||
<% else %>
|
||||
<a href="../show/<%= @page.name %>" class="navlink">
|
||||
<% end %>
|
||||
Forward in time</a>
|
||||
(<%= @revision.page.revisions.length - @revision.next_revision.number %> more)
|
||||
<% end %>
|
||||
|
||||
<% if @revision.next_revision && @revision.previous_revision %>
|
||||
|
|
||||
<% end %>
|
||||
|
||||
<% if @revision.previous_revision %>
|
||||
<a href="../revision/<%= @page.name %>?rev=<%= @revision.previous_revision.number %>" class="navlink">Back in time</a>
|
||||
(<%= @revision.previous_revision.number + 1 %> more)
|
||||
<% end %>
|
||||
|
||||
| <a href="../show/<%= @page.name %>" class="navlink">See current</a>
|
||||
|
||||
<% if @revision.previous_revision %>
|
||||
<span id="show_changes">
|
||||
| <a href="#" onClick="toggleChanges(); return false;">See changes</a>
|
||||
</span>
|
||||
<span id="hide_changes" style="display: none">
|
||||
| <a href="#" onClick="toggleChanges(); return false;">Hide changes</a>
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
| <a href="../rollback/<%= @page.name %>?rev=<%= @revision.number %>" class="navlink">Rollback</a>
|
||||
|
||||
<% if @page.references.length > 0 %>
|
||||
<small>
|
||||
| Linked from: <%= @page.references.collect { |ref| "<a href='#{ref.name}'>#{ref.name}</a>" }.join(", ") %>
|
||||
</small>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<script language="Javascript">
|
||||
function toggleChanges() {
|
||||
if (document.getElementById("changes").style.display == "none") {
|
||||
document.getElementById("changes").style.display = "block";
|
||||
document.getElementById("revision").style.display = "none";
|
||||
document.getElementById("show_changes").style.display = "none";
|
||||
document.getElementById("hide_changes").style.display = "inline";
|
||||
} else {
|
||||
document.getElementById("changes").style.display = "none";
|
||||
document.getElementById("revision").style.display = "block";
|
||||
document.getElementById("show_changes").style.display = "inline";
|
||||
document.getElementById("hide_changes").style.display = "none";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
0
app/views/wiki/rollback.rhtml
Executable file → Normal file
0
app/views/wiki/rollback.rhtml
Executable file → Normal file
44
app/views/wiki/rss_feed.rhtml
Executable file → Normal file
44
app/views/wiki/rss_feed.rhtml
Executable file → Normal file
|
@ -1,22 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<channel>
|
||||
<title><%= @web.name %></title>
|
||||
<link><%= url_for :only_path => false, :web => @web_name, :action => 'show', :id => 'HomePage' %></link>
|
||||
<description>An Instiki wiki</description>
|
||||
<language>en-us</language>
|
||||
<ttl>40</ttl>
|
||||
<% for page in @pages_by_revision %>
|
||||
<item>
|
||||
<title><%= page.plain_name %></title>
|
||||
<% unless @hide_description %>
|
||||
<description><%= CGI.escapeHTML(page.display_content) %></description>
|
||||
<% end %>
|
||||
<pubDate><%= page.created_at.strftime "%a, %e %b %Y %H:%M:%S %Z" %></pubDate>
|
||||
<guid><%= url_for :only_path => false, :web => @web_name, :action => 'show', :id => page.name %></guid>
|
||||
<link><%= url_for :only_path => false, :web => @web_name, :action => 'show', :id => page.name %></link>
|
||||
<dc:creator><%= WikiWords.separate(page.author) %></dc:creator>
|
||||
</item>
|
||||
<% end %>
|
||||
</channel>
|
||||
</rss>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<channel>
|
||||
<title><%= @web.name %></title>
|
||||
<link><%= url_for :only_path => false, :web => @web_name, :action => 'show', :id => 'HomePage' %></link>
|
||||
<description>An Instiki wiki</description>
|
||||
<language>en-us</language>
|
||||
<ttl>40</ttl>
|
||||
<% for page in @pages_by_revision %>
|
||||
<item>
|
||||
<title><%= page.plain_name %></title>
|
||||
<% unless @hide_description %>
|
||||
<description><%= CGI.escapeHTML(page.display_content) %></description>
|
||||
<% end %>
|
||||
<pubDate><%= page.created_at.strftime "%a, %e %b %Y %H:%M:%S %Z" %></pubDate>
|
||||
<guid><%= url_for :only_path => false, :web => @web_name, :action => 'show', :id => page.name %></guid>
|
||||
<link><%= url_for :only_path => false, :web => @web_name, :action => 'show', :id => page.name %></link>
|
||||
<dc:creator><%= WikiWords.separate(page.author) %></dc:creator>
|
||||
</item>
|
||||
<% end %>
|
||||
</channel>
|
||||
</rss>
|
||||
|
|
26
app/views/wiki/search.rhtml
Executable file → Normal file
26
app/views/wiki/search.rhtml
Executable file → Normal file
|
@ -1,13 +1,13 @@
|
|||
<% @title = @results.length > 0 ? "#{@results.length} pages contains \"#{@params["query"]}\"" : "No pages contains \"#{@query}\"" %>
|
||||
|
||||
<% if @results.length > 0 %>
|
||||
<ul>
|
||||
<% for page in @results %>
|
||||
<li><a href="../show/<%= page.name %>"><%= page.plain_name %></a></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% else %>
|
||||
<p>Perhaps you should try expanding your query. Remember that Instiki searches for entire phrases, so if you search for "all that jazz" it will not match pages that contain these words in separation—only as a sentence fragment.</p>
|
||||
|
||||
<p>If you're a high-tech computer wizard, you might even want try constructing a regular expression. That's actually what Instiki uses, so go right ahead and flex your "[a-z]*Leet?RegExpSkill(s|z)"</p>
|
||||
<% end %>
|
||||
<% @title = @results.length > 0 ? "#{@results.length} pages contains \"#{@params["query"]}\"" : "No pages contains \"#{@query}\"" %>
|
||||
|
||||
<% if @results.length > 0 %>
|
||||
<ul>
|
||||
<% for page in @results %>
|
||||
<li><a href="../show/<%= page.name %>"><%= page.plain_name %></a></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% else %>
|
||||
<p>Perhaps you should try expanding your query. Remember that Instiki searches for entire phrases, so if you search for "all that jazz" it will not match pages that contain these words in separation—only as a sentence fragment.</p>
|
||||
|
||||
<p>If you're a high-tech computer wizard, you might even want try constructing a regular expression. That's actually what Instiki uses, so go right ahead and flex your "[a-z]*Leet?RegExpSkill(s|z)"</p>
|
||||
<% end %>
|
||||
|
|
44
app/views/wiki/tex.rhtml
Executable file → Normal file
44
app/views/wiki/tex.rhtml
Executable file → Normal file
|
@ -1,23 +1,23 @@
|
|||
\documentclass[12pt,titlepage]{article}
|
||||
|
||||
\usepackage[danish]{babel} %danske tekster
|
||||
\usepackage[OT1]{fontenc} %rigtige danske bogstaver...
|
||||
\usepackage{a4}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{ucs}
|
||||
\usepackage[utf8x]{inputenc}
|
||||
\input epsf
|
||||
|
||||
%-------------------------------------------------------------------
|
||||
|
||||
\begin{document}
|
||||
|
||||
\sloppy
|
||||
|
||||
%-------------------------------------------------------------------
|
||||
|
||||
\section*{<%= @page.name %>}
|
||||
|
||||
<%= @tex_content %>
|
||||
|
||||
\documentclass[12pt,titlepage]{article}
|
||||
|
||||
\usepackage[danish]{babel} %danske tekster
|
||||
\usepackage[OT1]{fontenc} %rigtige danske bogstaver...
|
||||
\usepackage{a4}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{ucs}
|
||||
\usepackage[utf8x]{inputenc}
|
||||
\input epsf
|
||||
|
||||
%-------------------------------------------------------------------
|
||||
|
||||
\begin{document}
|
||||
|
||||
\sloppy
|
||||
|
||||
%-------------------------------------------------------------------
|
||||
|
||||
\section*{<%= @page.name %>}
|
||||
|
||||
<%= @tex_content %>
|
||||
|
||||
\end{document}
|
68
app/views/wiki/tex_web.rhtml
Executable file → Normal file
68
app/views/wiki/tex_web.rhtml
Executable file → Normal file
|
@ -1,35 +1,35 @@
|
|||
\documentclass[12pt,titlepage]{article}
|
||||
|
||||
\usepackage{fancyhdr}
|
||||
\pagestyle{fancy}
|
||||
|
||||
\fancyhead[LE,RO]{}
|
||||
\fancyhead[LO,RE]{\nouppercase{\bfseries \leftmark}}
|
||||
\fancyfoot[C]{\thepage}
|
||||
|
||||
\usepackage[danish]{babel} %danske tekster
|
||||
\usepackage{a4}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{ucs}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\input epsf
|
||||
|
||||
|
||||
%-------------------------------------------------------------------
|
||||
|
||||
\title{<%= @web_name %>}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\maketitle
|
||||
|
||||
\tableofcontents
|
||||
\pagebreak
|
||||
|
||||
\sloppy
|
||||
|
||||
%-------------------------------------------------------------------
|
||||
|
||||
<%= @tex_content %>
|
||||
|
||||
\documentclass[12pt,titlepage]{article}
|
||||
|
||||
\usepackage{fancyhdr}
|
||||
\pagestyle{fancy}
|
||||
|
||||
\fancyhead[LE,RO]{}
|
||||
\fancyhead[LO,RE]{\nouppercase{\bfseries \leftmark}}
|
||||
\fancyfoot[C]{\thepage}
|
||||
|
||||
\usepackage[danish]{babel} %danske tekster
|
||||
\usepackage{a4}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{ucs}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\input epsf
|
||||
|
||||
|
||||
%-------------------------------------------------------------------
|
||||
|
||||
\title{<%= @web_name %>}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\maketitle
|
||||
|
||||
\tableofcontents
|
||||
\pagebreak
|
||||
|
||||
\sloppy
|
||||
|
||||
%-------------------------------------------------------------------
|
||||
|
||||
<%= @tex_content %>
|
||||
|
||||
\end{document}
|
36
app/views/wiki/web_list.rhtml
Executable file → Normal file
36
app/views/wiki/web_list.rhtml
Executable file → Normal file
|
@ -1,18 +1,18 @@
|
|||
<% @title = "Wiki webs" %>
|
||||
|
||||
<ul>
|
||||
<% for web in @webs %>
|
||||
<li>
|
||||
<% if web.published %>
|
||||
<%= web.make_link 'HomePage', web.name, :mode => :publish %> (read-only) /
|
||||
<%= web.make_link 'HomePage', 'editable version', :mode => :edit %> (requires login)
|
||||
<% else %>
|
||||
<%= web.make_link 'HomePage', nil, :mode => :edit %>
|
||||
<% end %>
|
||||
<div class="byline" style="margin-bottom: 0px">
|
||||
<%= web.pages.length %> pages by <%= web.authors.length %> authors
|
||||
</div>
|
||||
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% @title = "Wiki webs" %>
|
||||
|
||||
<ul>
|
||||
<% for web in @webs %>
|
||||
<li>
|
||||
<% if web.published %>
|
||||
<%= web.make_link 'HomePage', web.name, :mode => :publish %> (read-only) /
|
||||
<%= web.make_link 'HomePage', 'editable version', :mode => :edit %> (requires login)
|
||||
<% else %>
|
||||
<%= web.make_link 'HomePage', nil, :mode => :edit %>
|
||||
<% end %>
|
||||
<div class="byline" style="margin-bottom: 0px">
|
||||
<%= web.pages.length %> pages by <%= web.authors.length %> authors
|
||||
</div>
|
||||
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
|
18
app/views/wiki_words_help.rhtml
Executable file → Normal file
18
app/views/wiki_words_help.rhtml
Executable file → Normal file
|
@ -1,9 +1,9 @@
|
|||
<h3>Wiki words</h3>
|
||||
<p style="border-top: 1px dotted #ccc; margin-top: 0px">
|
||||
Two or more uppercase words stuck together (camel case) or any phrase surrounded by double
|
||||
brackets is a wiki word. A camel-case wiki word can be escaped by putting \ in front of it.
|
||||
</p>
|
||||
<p>
|
||||
Wiki words: <i>HomePage, ThreeWordsTogether, [[C++]], [[Let's play again!]]</i><br/>
|
||||
Not wiki words: <i>IBM, School</i>
|
||||
</p>
|
||||
<h3>Wiki words</h3>
|
||||
<p style="border-top: 1px dotted #ccc; margin-top: 0px">
|
||||
Two or more uppercase words stuck together (camel case) or any phrase surrounded by double
|
||||
brackets is a wiki word. A camel-case wiki word can be escaped by putting \ in front of it.
|
||||
</p>
|
||||
<p>
|
||||
Wiki words: <i>HomePage, ThreeWordsTogether, [[C++]], [[Let's play again!]]</i><br/>
|
||||
Not wiki words: <i>IBM, School</i>
|
||||
</p>
|
||||
|
|
8
config/environments/development.rb
Executable file → Normal file
8
config/environments/development.rb
Executable file → Normal file
|
@ -1,4 +1,4 @@
|
|||
Dependencies.mechanism = :require
|
||||
ActionController::Base.consider_all_requests_local = true
|
||||
BREAKPOINT_SERVER_PORT = 42531
|
||||
ActionController::Base.logger.level = Logger::DEBUG
|
||||
Dependencies.mechanism = :require
|
||||
ActionController::Base.consider_all_requests_local = true
|
||||
BREAKPOINT_SERVER_PORT = 42531
|
||||
ActionController::Base.logger.level = Logger::DEBUG
|
||||
|
|
44
libraries/active_record_stub.rb
Executable file → Normal file
44
libraries/active_record_stub.rb
Executable file → Normal file
|
@ -1,23 +1,23 @@
|
|||
# This project uses Railties, which has an external dependency on ActiveRecord
|
||||
# Since ActiveRecord may not be present in Instiki runtime environment, this
|
||||
# file provides a stub replacement for it
|
||||
|
||||
unless defined? ActiveRecord::Base
|
||||
|
||||
module ActiveRecord
|
||||
class Base
|
||||
|
||||
# dependency in railties/lib/dispatcher.rb
|
||||
def self.reset_column_information_and_inheritable_attributes_for_all_subclasses
|
||||
# noop
|
||||
end
|
||||
|
||||
# dependency in actionpack/lib/action_controller/benchmarking.rb
|
||||
def self.connected?
|
||||
false
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
# This project uses Railties, which has an external dependency on ActiveRecord
|
||||
# Since ActiveRecord may not be present in Instiki runtime environment, this
|
||||
# file provides a stub replacement for it
|
||||
|
||||
unless defined? ActiveRecord::Base
|
||||
|
||||
module ActiveRecord
|
||||
class Base
|
||||
|
||||
# dependency in railties/lib/dispatcher.rb
|
||||
def self.reset_column_information_and_inheritable_attributes_for_all_subclasses
|
||||
# noop
|
||||
end
|
||||
|
||||
# dependency in actionpack/lib/action_controller/benchmarking.rb
|
||||
def self.connected?
|
||||
false
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
948
libraries/diff.rb
Executable file → Normal file
948
libraries/diff.rb
Executable file → Normal file
|
@ -1,475 +1,475 @@
|
|||
# heavily based off difflib.py - see that file for documentation
|
||||
# ported from Python by Bill Atkins
|
||||
|
||||
# This does not support all features offered by difflib; it
|
||||
# implements only the subset of features necessary
|
||||
# to support a Ruby version of HTML Differ. You're welcome to finish this off.
|
||||
|
||||
# By default, String#each iterates by line. This isn't really appropriate
|
||||
# for diff, so often a string will be split by // to get an array of one-
|
||||
# character strings.
|
||||
|
||||
# Some methods in Diff are untested and are not guaranteed to work. The
|
||||
# methods in HTMLDiff and any methods it calls should work quite well.
|
||||
|
||||
# changes by DenisMertz
|
||||
# * main change:
|
||||
# ** get the tag soup away
|
||||
# the tag soup problem was first reported with <p> tags, but it appeared also with
|
||||
# <li>, <ul> etc... tags
|
||||
# this version should mostly fix these problems
|
||||
# ** added a Builder class to manage the creation of the final htmldiff
|
||||
# * minor changes:
|
||||
# ** use symbols instead of string to represent opcodes
|
||||
# ** small fix to html2list
|
||||
#
|
||||
|
||||
module Enumerable
|
||||
def reduce(init)
|
||||
result = init
|
||||
each { |item| result = yield(result, item) }
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
module Diff
|
||||
|
||||
class SequenceMatcher
|
||||
def initialize(a=[''], b=[''], isjunk=nil, byline=false)
|
||||
a = (!byline and a.kind_of? String) ? a.split(//) : a
|
||||
b = (!byline and b.kind_of? String) ? b.split(//) : b
|
||||
@isjunk = isjunk || proc {}
|
||||
set_seqs a, b
|
||||
end
|
||||
|
||||
def set_seqs(a, b)
|
||||
set_seq_a a
|
||||
set_seq_b b
|
||||
end
|
||||
|
||||
def set_seq_a(a)
|
||||
@a = a
|
||||
@matching_blocks = @opcodes = nil
|
||||
end
|
||||
|
||||
def set_seq_b(b)
|
||||
@b = b
|
||||
@matching_blocks = @opcodes = nil
|
||||
chain_b
|
||||
end
|
||||
|
||||
def chain_b
|
||||
@fullbcount = nil
|
||||
@b2j = {}
|
||||
pophash = {}
|
||||
junkdict = {}
|
||||
|
||||
@b.each_with_index do |elt, i|
|
||||
if @b2j.has_key? elt
|
||||
indices = @b2j[elt]
|
||||
if @b.length >= 200 and indices.length * 100 > @b.length
|
||||
pophash[elt] = 1
|
||||
indices.clear
|
||||
else
|
||||
indices.push i
|
||||
end
|
||||
else
|
||||
@b2j[elt] = [i]
|
||||
end
|
||||
end
|
||||
|
||||
pophash.each_key { |elt| @b2j.delete elt }
|
||||
|
||||
junkdict = {}
|
||||
|
||||
unless @isjunk.nil?
|
||||
[pophash, @b2j].each do |d|
|
||||
d.each_key do |elt|
|
||||
if @isjunk.call(elt)
|
||||
junkdict[elt] = 1
|
||||
d.delete elt
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@isbjunk = junkdict.method(:has_key?)
|
||||
@isbpopular = junkdict.method(:has_key?)
|
||||
end
|
||||
|
||||
def find_longest_match(alo, ahi, blo, bhi)
|
||||
besti, bestj, bestsize = alo, blo, 0
|
||||
|
||||
j2len = {}
|
||||
|
||||
(alo..ahi).step do |i|
|
||||
newj2len = {}
|
||||
(@b2j[@a[i]] || []).each do |j|
|
||||
if j < blo
|
||||
next
|
||||
end
|
||||
if j >= bhi
|
||||
break
|
||||
end
|
||||
|
||||
k = newj2len[j] = (j2len[j - 1] || 0) + 1
|
||||
if k > bestsize
|
||||
besti, bestj, bestsize = i - k + 1, j - k + 1, k
|
||||
end
|
||||
end
|
||||
j2len = newj2len
|
||||
end
|
||||
|
||||
while besti > alo and bestj > blo and
|
||||
not @isbjunk.call(@b[bestj-1]) and
|
||||
@a[besti-1] == @b[bestj-1]
|
||||
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
|
||||
end
|
||||
|
||||
while besti+bestsize < ahi and bestj+bestsize < bhi and
|
||||
not @isbjunk.call(@b[bestj+bestsize]) and
|
||||
@a[besti+bestsize] == @b[bestj+bestsize]
|
||||
bestsize += 1
|
||||
end
|
||||
|
||||
while besti > alo and bestj > blo and
|
||||
@isbjunk.call(@b[bestj-1]) and
|
||||
@a[besti-1] == @b[bestj-1]
|
||||
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
|
||||
end
|
||||
|
||||
while besti+bestsize < ahi and bestj+bestsize < bhi and
|
||||
@isbjunk.call(@b[bestj+bestsize]) and
|
||||
@a[besti+bestsize] == @b[bestj+bestsize]
|
||||
bestsize += 1
|
||||
end
|
||||
|
||||
[besti, bestj, bestsize]
|
||||
end
|
||||
|
||||
def get_matching_blocks
|
||||
return @matching_blocks unless @matching_blocks.nil? or
|
||||
@matching_blocks.empty?
|
||||
|
||||
@matching_blocks = []
|
||||
la, lb = @a.length, @b.length
|
||||
match_block_helper(0, la, 0, lb, @matching_blocks)
|
||||
@matching_blocks.push [la, lb, 0]
|
||||
end
|
||||
|
||||
def match_block_helper(alo, ahi, blo, bhi, answer)
|
||||
i, j, k = x = find_longest_match(alo, ahi, blo, bhi)
|
||||
if not k.zero?
|
||||
if alo < i and blo < j
|
||||
match_block_helper(alo, i, blo, j, answer)
|
||||
end
|
||||
answer.push x
|
||||
if i + k < ahi and j + k < bhi
|
||||
match_block_helper(i + k, ahi, j + k, bhi, answer)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def get_opcodes
|
||||
unless @opcodes.nil? or @opcodes.empty?
|
||||
return @opcodes
|
||||
end
|
||||
|
||||
i = j = 0
|
||||
@opcodes = answer = []
|
||||
get_matching_blocks.each do |ai, bj, size|
|
||||
tag = if i < ai and j < bj
|
||||
:replace
|
||||
elsif i < ai
|
||||
:delete
|
||||
elsif j < bj
|
||||
:insert
|
||||
end
|
||||
|
||||
answer.push [tag, i, ai, j, bj] if tag
|
||||
|
||||
i, j = ai + size, bj + size
|
||||
|
||||
answer.push [:equal, ai, i, bj, j] unless size.zero?
|
||||
|
||||
end
|
||||
return answer
|
||||
end
|
||||
|
||||
# XXX: untested
|
||||
def get_grouped_opcodes(n=3)
|
||||
codes = get_opcodes
|
||||
if codes[0][0] == :equal
|
||||
tag, i1, i2, j1, j2 = codes[0]
|
||||
codes[0] = tag, [i1, i2 - n].max, i2, [j1, j2-n].max, j2
|
||||
end
|
||||
|
||||
if codes[-1][0] == :equal
|
||||
tag, i1, i2, j1, j2 = codes[-1]
|
||||
codes[-1] = tag, i1, min(i2, i1+n), j1, min(j2, j1+n)
|
||||
end
|
||||
nn = n + n
|
||||
group = []
|
||||
codes.each do |tag, i1, i2, j1, j2|
|
||||
if tag == :equal and i2-i1 > nn
|
||||
group.push [tag, i1, [i2, i1 + n].min, j1, [j2, j1 + n].min]
|
||||
yield group
|
||||
group = []
|
||||
i1, j1 = [i1, i2-n].max, [j1, j2-n].max
|
||||
group.push [tag, i1, i2, j1 ,j2]
|
||||
end
|
||||
end
|
||||
if group and group.length != 1 and group[0][0] == :equal
|
||||
yield group
|
||||
end
|
||||
end
|
||||
|
||||
def ratio
|
||||
matches = get_matching_blocks.reduce(0) do |sum, triple|
|
||||
sum + triple[-1]
|
||||
end
|
||||
Diff.calculate_ratio(matches, @a.length + @b.length)
|
||||
end
|
||||
|
||||
def quick_ratio
|
||||
if @fullbcount.nil? or @fullbcount.empty?
|
||||
@fullbcount = {}
|
||||
@b.each do |elt|
|
||||
@fullbcount[elt] = (@fullbcount[elt] || 0) + 1
|
||||
end
|
||||
end
|
||||
|
||||
avail = {}
|
||||
matches = 0
|
||||
@a.each do |elt|
|
||||
if avail.has_key? elt
|
||||
numb = avail[elt]
|
||||
else
|
||||
numb = @fullbcount[elt] || 0
|
||||
end
|
||||
avail[elt] = numb - 1
|
||||
if numb > 0
|
||||
matches += 1
|
||||
end
|
||||
end
|
||||
Diff.calculate_ratio matches, @a.length + @b.length
|
||||
end
|
||||
|
||||
def real_quick_ratio
|
||||
la, lb = @a.length, @b.length
|
||||
Diff.calculate_ratio([la, lb].min, la + lb)
|
||||
end
|
||||
|
||||
protected :chain_b, :match_block_helper
|
||||
end # end class SequenceMatcher
|
||||
|
||||
def self.calculate_ratio(matches, length)
|
||||
return 1.0 if length.zero?
|
||||
2.0 * matches / length
|
||||
end
|
||||
|
||||
# XXX: untested
|
||||
def self.get_close_matches(word, possibilities, n=3, cutoff=0.6)
|
||||
unless n > 0
|
||||
raise "n must be > 0: #{n}"
|
||||
end
|
||||
unless 0.0 <= cutoff and cutoff <= 1.0
|
||||
raise "cutoff must be in (0.0..1.0): #{cutoff}"
|
||||
end
|
||||
|
||||
result = []
|
||||
s = SequenceMatcher.new
|
||||
s.set_seq_b word
|
||||
possibilities.each do |x|
|
||||
s.set_seq_a x
|
||||
if s.real_quick_ratio >= cutoff and
|
||||
s.quick_ratio >= cutoff and
|
||||
s.ratio >= cutoff
|
||||
result.push [s.ratio, x]
|
||||
end
|
||||
end
|
||||
|
||||
unless result.nil? or result.empty?
|
||||
result.sort
|
||||
result.reverse!
|
||||
result = result[-n..-1]
|
||||
end
|
||||
result.collect { |score, x| x }
|
||||
end
|
||||
|
||||
def self.count_leading(line, ch)
|
||||
i, n = 0, line.length
|
||||
while i < n and line[i].chr == ch
|
||||
i += 1
|
||||
end
|
||||
i
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
module HTMLDiff
|
||||
include Diff
|
||||
class Builder
|
||||
VALID_METHODS = [:replace, :insert, :delete, :equal]
|
||||
def initialize(a, b)
|
||||
@a = a
|
||||
@b = b
|
||||
@content = []
|
||||
end
|
||||
|
||||
def do_op(opcode)
|
||||
@opcode = opcode
|
||||
op = @opcode[0]
|
||||
VALID_METHODS.include?(op) or raise(NameError, "Invalid opcode #{op}")
|
||||
self.method(op).call
|
||||
end
|
||||
|
||||
def result
|
||||
@content.join('')
|
||||
end
|
||||
|
||||
#this methods have to be called via do_op(opcode) so that @opcode is set properly
|
||||
private
|
||||
|
||||
def replace
|
||||
delete("diffmod")
|
||||
insert("diffmod")
|
||||
end
|
||||
|
||||
def insert(tagclass="diffins")
|
||||
op_helper("ins", tagclass, @b[@opcode[3]...@opcode[4]])
|
||||
end
|
||||
|
||||
def delete(tagclass="diffdel")
|
||||
op_helper("del", tagclass, @a[@opcode[1]...@opcode[2]])
|
||||
end
|
||||
|
||||
def equal
|
||||
@content += @b[@opcode[3]...@opcode[4]]
|
||||
end
|
||||
|
||||
# using this as op_helper would be equivalent to the first version of diff.rb by Bill Atkins
|
||||
def op_helper_simple(tagname, tagclass, to_add)
|
||||
@content << "<#{tagname} class=\"#{tagclass}\">"
|
||||
@content += to_add
|
||||
@content << "</#{tagname}>"
|
||||
end
|
||||
|
||||
# this tries to put <p> tags or newline chars before the opening diff tags (<ins> or <del>)
|
||||
# or after the ending diff tags
|
||||
# as a result the diff tags should be the "more inside" possible.
|
||||
# this seems to work nice with html containing only paragraphs
|
||||
# but not sure it works if there are other tags (div, span ... ? ) around
|
||||
def op_helper(tagname, tagclass, to_add)
|
||||
@content << to_add.shift while ( HTMLDiff.is_newline(to_add.first) or
|
||||
HTMLDiff.is_p_close_tag(to_add.first) or
|
||||
HTMLDiff.is_p_open_tag(to_add.first) )
|
||||
@content << "<#{tagname} class=\"#{tagclass}\">"
|
||||
@content += to_add
|
||||
last_tags = []
|
||||
last_tags.unshift(@content.pop) while ( HTMLDiff.is_newline(@content.last) or
|
||||
HTMLDiff.is_p_close_tag(@content.last) or
|
||||
HTMLDiff.is_p_open_tag(@content.last) )
|
||||
last_tags.unshift "</#{tagname}>"
|
||||
@content += last_tags
|
||||
remove_empty_diff(tagname, tagclass)
|
||||
end
|
||||
|
||||
def remove_empty_diff(tagname, tagclass)
|
||||
if @content[-2] == "<#{tagname} class=\"#{tagclass}\">" and @content[-1] == "</#{tagname}>" then
|
||||
@content.pop
|
||||
@content.pop
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def self.is_newline(x)
|
||||
(x == "\n") or (x == "\r") or (x == "\t")
|
||||
end
|
||||
|
||||
def self.is_p_open_tag(x)
|
||||
x =~ /\A<(p|li|ul|ol|dir|dt|dl)/
|
||||
end
|
||||
|
||||
def self.is_p_close_tag(x)
|
||||
x =~ %r!\A</(p|li|ul|ol|dir|dt|dl)!
|
||||
end
|
||||
|
||||
def self.diff(a, b)
|
||||
a, b = a.split(//), b.split(//) if a.kind_of? String and b.kind_of? String
|
||||
a, b = html2list(a), html2list(b)
|
||||
|
||||
out = Builder.new(a, b)
|
||||
s = SequenceMatcher.new(a, b)
|
||||
|
||||
s.get_opcodes.each do |opcode|
|
||||
out.do_op(opcode)
|
||||
end
|
||||
|
||||
out.result
|
||||
end
|
||||
|
||||
def self.html2list(x, b=false)
|
||||
mode = 'char'
|
||||
cur = ''
|
||||
out = []
|
||||
|
||||
x = x.split(//) if x.kind_of? String
|
||||
|
||||
x.each do |c|
|
||||
if mode == 'tag'
|
||||
if c == '>'
|
||||
if b
|
||||
cur += ']'
|
||||
else
|
||||
cur += c
|
||||
end
|
||||
out.push(cur)
|
||||
cur = ''
|
||||
mode = 'char'
|
||||
else
|
||||
cur += c
|
||||
end
|
||||
elsif mode == 'char'
|
||||
if c == '<'
|
||||
out.push cur
|
||||
if b
|
||||
cur = '['
|
||||
else
|
||||
cur = c
|
||||
end
|
||||
mode = 'tag'
|
||||
elsif /\s/.match c
|
||||
out.push cur + c
|
||||
cur = ''
|
||||
else
|
||||
cur += c
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
out.push cur
|
||||
# TODO: make something better here
|
||||
out.each{|x| x.chomp! unless is_newline(x)}
|
||||
out.find_all { |x| x != '' }
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
if __FILE__ == $0
|
||||
|
||||
require 'pp'
|
||||
# a = "<p>this is the original string</p>" # \n<p>but around the world</p>"
|
||||
# b = "<p>this is the original </p><p>other parag</p><p>string</p>"
|
||||
a = "<ul>\n\t<li>one</li>\n\t<li>two</li>\n</ul>"
|
||||
b = "<ul>\n\t<li>one</li>\n\t<li>two\n\t<ul><li>abc</li></ul></li>\n</ul>"
|
||||
puts a
|
||||
pp HTMLDiff.html2list(a)
|
||||
puts
|
||||
puts b
|
||||
pp HTMLDiff.html2list(b)
|
||||
puts
|
||||
puts HTMLDiff.diff(a, b)
|
||||
# heavily based off difflib.py - see that file for documentation
|
||||
# ported from Python by Bill Atkins
|
||||
|
||||
# This does not support all features offered by difflib; it
|
||||
# implements only the subset of features necessary
|
||||
# to support a Ruby version of HTML Differ. You're welcome to finish this off.
|
||||
|
||||
# By default, String#each iterates by line. This isn't really appropriate
|
||||
# for diff, so often a string will be split by // to get an array of one-
|
||||
# character strings.
|
||||
|
||||
# Some methods in Diff are untested and are not guaranteed to work. The
|
||||
# methods in HTMLDiff and any methods it calls should work quite well.
|
||||
|
||||
# changes by DenisMertz
|
||||
# * main change:
|
||||
# ** get the tag soup away
|
||||
# the tag soup problem was first reported with <p> tags, but it appeared also with
|
||||
# <li>, <ul> etc... tags
|
||||
# this version should mostly fix these problems
|
||||
# ** added a Builder class to manage the creation of the final htmldiff
|
||||
# * minor changes:
|
||||
# ** use symbols instead of string to represent opcodes
|
||||
# ** small fix to html2list
|
||||
#
|
||||
|
||||
module Enumerable
|
||||
def reduce(init)
|
||||
result = init
|
||||
each { |item| result = yield(result, item) }
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
module Diff
|
||||
|
||||
class SequenceMatcher
|
||||
def initialize(a=[''], b=[''], isjunk=nil, byline=false)
|
||||
a = (!byline and a.kind_of? String) ? a.split(//) : a
|
||||
b = (!byline and b.kind_of? String) ? b.split(//) : b
|
||||
@isjunk = isjunk || proc {}
|
||||
set_seqs a, b
|
||||
end
|
||||
|
||||
def set_seqs(a, b)
|
||||
set_seq_a a
|
||||
set_seq_b b
|
||||
end
|
||||
|
||||
def set_seq_a(a)
|
||||
@a = a
|
||||
@matching_blocks = @opcodes = nil
|
||||
end
|
||||
|
||||
def set_seq_b(b)
|
||||
@b = b
|
||||
@matching_blocks = @opcodes = nil
|
||||
chain_b
|
||||
end
|
||||
|
||||
def chain_b
|
||||
@fullbcount = nil
|
||||
@b2j = {}
|
||||
pophash = {}
|
||||
junkdict = {}
|
||||
|
||||
@b.each_with_index do |elt, i|
|
||||
if @b2j.has_key? elt
|
||||
indices = @b2j[elt]
|
||||
if @b.length >= 200 and indices.length * 100 > @b.length
|
||||
pophash[elt] = 1
|
||||
indices.clear
|
||||
else
|
||||
indices.push i
|
||||
end
|
||||
else
|
||||
@b2j[elt] = [i]
|
||||
end
|
||||
end
|
||||
|
||||
pophash.each_key { |elt| @b2j.delete elt }
|
||||
|
||||
junkdict = {}
|
||||
|
||||
unless @isjunk.nil?
|
||||
[pophash, @b2j].each do |d|
|
||||
d.each_key do |elt|
|
||||
if @isjunk.call(elt)
|
||||
junkdict[elt] = 1
|
||||
d.delete elt
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@isbjunk = junkdict.method(:has_key?)
|
||||
@isbpopular = junkdict.method(:has_key?)
|
||||
end
|
||||
|
||||
def find_longest_match(alo, ahi, blo, bhi)
|
||||
besti, bestj, bestsize = alo, blo, 0
|
||||
|
||||
j2len = {}
|
||||
|
||||
(alo..ahi).step do |i|
|
||||
newj2len = {}
|
||||
(@b2j[@a[i]] || []).each do |j|
|
||||
if j < blo
|
||||
next
|
||||
end
|
||||
if j >= bhi
|
||||
break
|
||||
end
|
||||
|
||||
k = newj2len[j] = (j2len[j - 1] || 0) + 1
|
||||
if k > bestsize
|
||||
besti, bestj, bestsize = i - k + 1, j - k + 1, k
|
||||
end
|
||||
end
|
||||
j2len = newj2len
|
||||
end
|
||||
|
||||
while besti > alo and bestj > blo and
|
||||
not @isbjunk.call(@b[bestj-1]) and
|
||||
@a[besti-1] == @b[bestj-1]
|
||||
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
|
||||
end
|
||||
|
||||
while besti+bestsize < ahi and bestj+bestsize < bhi and
|
||||
not @isbjunk.call(@b[bestj+bestsize]) and
|
||||
@a[besti+bestsize] == @b[bestj+bestsize]
|
||||
bestsize += 1
|
||||
end
|
||||
|
||||
while besti > alo and bestj > blo and
|
||||
@isbjunk.call(@b[bestj-1]) and
|
||||
@a[besti-1] == @b[bestj-1]
|
||||
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
|
||||
end
|
||||
|
||||
while besti+bestsize < ahi and bestj+bestsize < bhi and
|
||||
@isbjunk.call(@b[bestj+bestsize]) and
|
||||
@a[besti+bestsize] == @b[bestj+bestsize]
|
||||
bestsize += 1
|
||||
end
|
||||
|
||||
[besti, bestj, bestsize]
|
||||
end
|
||||
|
||||
def get_matching_blocks
|
||||
return @matching_blocks unless @matching_blocks.nil? or
|
||||
@matching_blocks.empty?
|
||||
|
||||
@matching_blocks = []
|
||||
la, lb = @a.length, @b.length
|
||||
match_block_helper(0, la, 0, lb, @matching_blocks)
|
||||
@matching_blocks.push [la, lb, 0]
|
||||
end
|
||||
|
||||
def match_block_helper(alo, ahi, blo, bhi, answer)
|
||||
i, j, k = x = find_longest_match(alo, ahi, blo, bhi)
|
||||
if not k.zero?
|
||||
if alo < i and blo < j
|
||||
match_block_helper(alo, i, blo, j, answer)
|
||||
end
|
||||
answer.push x
|
||||
if i + k < ahi and j + k < bhi
|
||||
match_block_helper(i + k, ahi, j + k, bhi, answer)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def get_opcodes
|
||||
unless @opcodes.nil? or @opcodes.empty?
|
||||
return @opcodes
|
||||
end
|
||||
|
||||
i = j = 0
|
||||
@opcodes = answer = []
|
||||
get_matching_blocks.each do |ai, bj, size|
|
||||
tag = if i < ai and j < bj
|
||||
:replace
|
||||
elsif i < ai
|
||||
:delete
|
||||
elsif j < bj
|
||||
:insert
|
||||
end
|
||||
|
||||
answer.push [tag, i, ai, j, bj] if tag
|
||||
|
||||
i, j = ai + size, bj + size
|
||||
|
||||
answer.push [:equal, ai, i, bj, j] unless size.zero?
|
||||
|
||||
end
|
||||
return answer
|
||||
end
|
||||
|
||||
# XXX: untested
|
||||
def get_grouped_opcodes(n=3)
|
||||
codes = get_opcodes
|
||||
if codes[0][0] == :equal
|
||||
tag, i1, i2, j1, j2 = codes[0]
|
||||
codes[0] = tag, [i1, i2 - n].max, i2, [j1, j2-n].max, j2
|
||||
end
|
||||
|
||||
if codes[-1][0] == :equal
|
||||
tag, i1, i2, j1, j2 = codes[-1]
|
||||
codes[-1] = tag, i1, min(i2, i1+n), j1, min(j2, j1+n)
|
||||
end
|
||||
nn = n + n
|
||||
group = []
|
||||
codes.each do |tag, i1, i2, j1, j2|
|
||||
if tag == :equal and i2-i1 > nn
|
||||
group.push [tag, i1, [i2, i1 + n].min, j1, [j2, j1 + n].min]
|
||||
yield group
|
||||
group = []
|
||||
i1, j1 = [i1, i2-n].max, [j1, j2-n].max
|
||||
group.push [tag, i1, i2, j1 ,j2]
|
||||
end
|
||||
end
|
||||
if group and group.length != 1 and group[0][0] == :equal
|
||||
yield group
|
||||
end
|
||||
end
|
||||
|
||||
def ratio
|
||||
matches = get_matching_blocks.reduce(0) do |sum, triple|
|
||||
sum + triple[-1]
|
||||
end
|
||||
Diff.calculate_ratio(matches, @a.length + @b.length)
|
||||
end
|
||||
|
||||
def quick_ratio
|
||||
if @fullbcount.nil? or @fullbcount.empty?
|
||||
@fullbcount = {}
|
||||
@b.each do |elt|
|
||||
@fullbcount[elt] = (@fullbcount[elt] || 0) + 1
|
||||
end
|
||||
end
|
||||
|
||||
avail = {}
|
||||
matches = 0
|
||||
@a.each do |elt|
|
||||
if avail.has_key? elt
|
||||
numb = avail[elt]
|
||||
else
|
||||
numb = @fullbcount[elt] || 0
|
||||
end
|
||||
avail[elt] = numb - 1
|
||||
if numb > 0
|
||||
matches += 1
|
||||
end
|
||||
end
|
||||
Diff.calculate_ratio matches, @a.length + @b.length
|
||||
end
|
||||
|
||||
def real_quick_ratio
|
||||
la, lb = @a.length, @b.length
|
||||
Diff.calculate_ratio([la, lb].min, la + lb)
|
||||
end
|
||||
|
||||
protected :chain_b, :match_block_helper
|
||||
end # end class SequenceMatcher
|
||||
|
||||
def self.calculate_ratio(matches, length)
|
||||
return 1.0 if length.zero?
|
||||
2.0 * matches / length
|
||||
end
|
||||
|
||||
# XXX: untested
|
||||
def self.get_close_matches(word, possibilities, n=3, cutoff=0.6)
|
||||
unless n > 0
|
||||
raise "n must be > 0: #{n}"
|
||||
end
|
||||
unless 0.0 <= cutoff and cutoff <= 1.0
|
||||
raise "cutoff must be in (0.0..1.0): #{cutoff}"
|
||||
end
|
||||
|
||||
result = []
|
||||
s = SequenceMatcher.new
|
||||
s.set_seq_b word
|
||||
possibilities.each do |x|
|
||||
s.set_seq_a x
|
||||
if s.real_quick_ratio >= cutoff and
|
||||
s.quick_ratio >= cutoff and
|
||||
s.ratio >= cutoff
|
||||
result.push [s.ratio, x]
|
||||
end
|
||||
end
|
||||
|
||||
unless result.nil? or result.empty?
|
||||
result.sort
|
||||
result.reverse!
|
||||
result = result[-n..-1]
|
||||
end
|
||||
result.collect { |score, x| x }
|
||||
end
|
||||
|
||||
def self.count_leading(line, ch)
|
||||
i, n = 0, line.length
|
||||
while i < n and line[i].chr == ch
|
||||
i += 1
|
||||
end
|
||||
i
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
module HTMLDiff
|
||||
include Diff
|
||||
class Builder
|
||||
VALID_METHODS = [:replace, :insert, :delete, :equal]
|
||||
def initialize(a, b)
|
||||
@a = a
|
||||
@b = b
|
||||
@content = []
|
||||
end
|
||||
|
||||
def do_op(opcode)
|
||||
@opcode = opcode
|
||||
op = @opcode[0]
|
||||
VALID_METHODS.include?(op) or raise(NameError, "Invalid opcode #{op}")
|
||||
self.method(op).call
|
||||
end
|
||||
|
||||
def result
|
||||
@content.join('')
|
||||
end
|
||||
|
||||
#this methods have to be called via do_op(opcode) so that @opcode is set properly
|
||||
private
|
||||
|
||||
def replace
|
||||
delete("diffmod")
|
||||
insert("diffmod")
|
||||
end
|
||||
|
||||
def insert(tagclass="diffins")
|
||||
op_helper("ins", tagclass, @b[@opcode[3]...@opcode[4]])
|
||||
end
|
||||
|
||||
def delete(tagclass="diffdel")
|
||||
op_helper("del", tagclass, @a[@opcode[1]...@opcode[2]])
|
||||
end
|
||||
|
||||
def equal
|
||||
@content += @b[@opcode[3]...@opcode[4]]
|
||||
end
|
||||
|
||||
# using this as op_helper would be equivalent to the first version of diff.rb by Bill Atkins
|
||||
def op_helper_simple(tagname, tagclass, to_add)
|
||||
@content << "<#{tagname} class=\"#{tagclass}\">"
|
||||
@content += to_add
|
||||
@content << "</#{tagname}>"
|
||||
end
|
||||
|
||||
# this tries to put <p> tags or newline chars before the opening diff tags (<ins> or <del>)
|
||||
# or after the ending diff tags
|
||||
# as a result the diff tags should be the "more inside" possible.
|
||||
# this seems to work nice with html containing only paragraphs
|
||||
# but not sure it works if there are other tags (div, span ... ? ) around
|
||||
def op_helper(tagname, tagclass, to_add)
|
||||
@content << to_add.shift while ( HTMLDiff.is_newline(to_add.first) or
|
||||
HTMLDiff.is_p_close_tag(to_add.first) or
|
||||
HTMLDiff.is_p_open_tag(to_add.first) )
|
||||
@content << "<#{tagname} class=\"#{tagclass}\">"
|
||||
@content += to_add
|
||||
last_tags = []
|
||||
last_tags.unshift(@content.pop) while ( HTMLDiff.is_newline(@content.last) or
|
||||
HTMLDiff.is_p_close_tag(@content.last) or
|
||||
HTMLDiff.is_p_open_tag(@content.last) )
|
||||
last_tags.unshift "</#{tagname}>"
|
||||
@content += last_tags
|
||||
remove_empty_diff(tagname, tagclass)
|
||||
end
|
||||
|
||||
def remove_empty_diff(tagname, tagclass)
|
||||
if @content[-2] == "<#{tagname} class=\"#{tagclass}\">" and @content[-1] == "</#{tagname}>" then
|
||||
@content.pop
|
||||
@content.pop
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def self.is_newline(x)
|
||||
(x == "\n") or (x == "\r") or (x == "\t")
|
||||
end
|
||||
|
||||
def self.is_p_open_tag(x)
|
||||
x =~ /\A<(p|li|ul|ol|dir|dt|dl)/
|
||||
end
|
||||
|
||||
def self.is_p_close_tag(x)
|
||||
x =~ %r!\A</(p|li|ul|ol|dir|dt|dl)!
|
||||
end
|
||||
|
||||
def self.diff(a, b)
|
||||
a, b = a.split(//), b.split(//) if a.kind_of? String and b.kind_of? String
|
||||
a, b = html2list(a), html2list(b)
|
||||
|
||||
out = Builder.new(a, b)
|
||||
s = SequenceMatcher.new(a, b)
|
||||
|
||||
s.get_opcodes.each do |opcode|
|
||||
out.do_op(opcode)
|
||||
end
|
||||
|
||||
out.result
|
||||
end
|
||||
|
||||
def self.html2list(x, b=false)
|
||||
mode = 'char'
|
||||
cur = ''
|
||||
out = []
|
||||
|
||||
x = x.split(//) if x.kind_of? String
|
||||
|
||||
x.each do |c|
|
||||
if mode == 'tag'
|
||||
if c == '>'
|
||||
if b
|
||||
cur += ']'
|
||||
else
|
||||
cur += c
|
||||
end
|
||||
out.push(cur)
|
||||
cur = ''
|
||||
mode = 'char'
|
||||
else
|
||||
cur += c
|
||||
end
|
||||
elsif mode == 'char'
|
||||
if c == '<'
|
||||
out.push cur
|
||||
if b
|
||||
cur = '['
|
||||
else
|
||||
cur = c
|
||||
end
|
||||
mode = 'tag'
|
||||
elsif /\s/.match c
|
||||
out.push cur + c
|
||||
cur = ''
|
||||
else
|
||||
cur += c
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
out.push cur
|
||||
# TODO: make something better here
|
||||
out.each{|x| x.chomp! unless is_newline(x)}
|
||||
out.find_all { |x| x != '' }
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
if __FILE__ == $0
|
||||
|
||||
require 'pp'
|
||||
# a = "<p>this is the original string</p>" # \n<p>but around the world</p>"
|
||||
# b = "<p>this is the original </p><p>other parag</p><p>string</p>"
|
||||
a = "<ul>\n\t<li>one</li>\n\t<li>two</li>\n</ul>"
|
||||
b = "<ul>\n\t<li>one</li>\n\t<li>two\n\t<ul><li>abc</li></ul></li>\n</ul>"
|
||||
puts a
|
||||
pp HTMLDiff.html2list(a)
|
||||
puts
|
||||
puts b
|
||||
pp HTMLDiff.html2list(b)
|
||||
puts
|
||||
puts HTMLDiff.diff(a, b)
|
||||
end
|
|
@ -1,15 +1,15 @@
|
|||
# Model methods that want to rollback transactions gracefully
|
||||
# (i.e, returning the user back to the form from which the request was posted)
|
||||
# should raise Instiki::ValidationError.
|
||||
#
|
||||
# E.g. if a model object does
|
||||
# raise "Foo: '#{foo}' is not equal to Bar: '#{bar}'" if (foo != bar)
|
||||
#
|
||||
# then the operation is not committed; Rails returns the user to the page
|
||||
# where s/he was entering foo and bar, and the error message will be displayed
|
||||
# on the page
|
||||
|
||||
module Instiki
|
||||
class ValidationError < StandardError
|
||||
end
|
||||
# Model methods that want to rollback transactions gracefully
|
||||
# (i.e, returning the user back to the form from which the request was posted)
|
||||
# should raise Instiki::ValidationError.
|
||||
#
|
||||
# E.g. if a model object does
|
||||
# raise "Foo: '#{foo}' is not equal to Bar: '#{bar}'" if (foo != bar)
|
||||
#
|
||||
# then the operation is not committed; Rails returns the user to the page
|
||||
# where s/he was entering foo and bar, and the error message will be displayed
|
||||
# on the page
|
||||
|
||||
module Instiki
|
||||
class ValidationError < StandardError
|
||||
end
|
||||
end
|
302
libraries/rdocsupport.rb
Executable file → Normal file
302
libraries/rdocsupport.rb
Executable file → Normal file
|
@ -1,152 +1,152 @@
|
|||
begin
|
||||
require "rdoc/markup/simple_markup"
|
||||
require 'rdoc/markup/simple_markup/to_html'
|
||||
rescue LoadError
|
||||
# use old version if available
|
||||
require 'markup/simple_markup'
|
||||
require 'markup/simple_markup/to_html'
|
||||
end
|
||||
|
||||
module RDocSupport
|
||||
|
||||
# A simple +rdoc+ markup class which recognizes some additional
|
||||
# formatting commands suitable for Wiki use.
|
||||
class RDocMarkup < SM::SimpleMarkup
|
||||
def initialize
|
||||
super()
|
||||
|
||||
pre = '(?:\\s|^|\\\\)'
|
||||
|
||||
# links of the form
|
||||
# [[<url> description with spaces]]
|
||||
add_special(/((\\)?\[\[\S+?\s+.+?\]\])/,:TIDYLINK)
|
||||
|
||||
# and external references
|
||||
add_special(/((\\)?(link:|anchor:|http:|mailto:|ftp:|img:|www\.)\S+\w\/?)/,
|
||||
:HYPERLINK)
|
||||
|
||||
# <br/>
|
||||
add_special(%r{(#{pre}<br/>)}, :BR)
|
||||
|
||||
# and <center> ... </center>
|
||||
add_html("center", :CENTER)
|
||||
end
|
||||
|
||||
def convert(text, handler)
|
||||
super.sub(/^<p>\n/, '').sub(/<\/p>$/, '')
|
||||
end
|
||||
end
|
||||
|
||||
# Handle special hyperlinking requirments for RDoc formatted
|
||||
# entries. Requires RDoc
|
||||
|
||||
class HyperLinkHtml < SM::ToHtml
|
||||
|
||||
# Initialize the HyperLinkHtml object.
|
||||
# [path] location of the node
|
||||
# [site] object representing the whole site (typically of class
|
||||
# +Site+)
|
||||
def initialize
|
||||
super()
|
||||
add_tag(:CENTER, "<center>", "</center>")
|
||||
end
|
||||
|
||||
# handle <br/>
|
||||
def handle_special_BR(special)
|
||||
return "<br/>" if special.text[0,1] == '\\'
|
||||
special.text
|
||||
end
|
||||
|
||||
# We're invoked with a potential external hyperlink.
|
||||
# [mailto:] just gets inserted.
|
||||
# [http:] links are checked to see if they
|
||||
# reference an image. If so, that image gets inserted
|
||||
# using an <img> tag. Otherwise a conventional <a href>
|
||||
# is used.
|
||||
# [img:] insert a <tt><img></tt> tag
|
||||
# [link:] used to insert arbitrary <tt><a></tt> references
|
||||
# [anchor:] used to create an anchor
|
||||
def handle_special_HYPERLINK(special)
|
||||
text = special.text.strip
|
||||
return text[1..-1] if text[0,1] == '\\'
|
||||
url = special.text.strip
|
||||
if url =~ /([A-Za-z]+):(.*)/
|
||||
type = $1
|
||||
path = $2
|
||||
else
|
||||
type = "http"
|
||||
path = url
|
||||
url = "http://#{url}"
|
||||
end
|
||||
|
||||
case type
|
||||
when "http"
|
||||
if url =~ /\.(gif|png|jpg|jpeg|bmp)$/
|
||||
"<img src=\"#{url}\"/>"
|
||||
else
|
||||
"<a href=\"#{url}\">#{url.sub(%r{^\w+:/*}, '')}</a>"
|
||||
end
|
||||
when "img"
|
||||
"<img src=\"#{path}\"/>"
|
||||
when "link"
|
||||
"<a href=\"#{path}\">#{path}</a>"
|
||||
when "anchor"
|
||||
"<a name=\"#{path}\"></a>"
|
||||
else
|
||||
"<a href=\"#{url}\">#{url.sub(%r{^\w+:/*}, '')}</a>"
|
||||
end
|
||||
end
|
||||
|
||||
# Here's a hyperlink where the label is different to the URL
|
||||
# [[url label that may contain spaces]]
|
||||
#
|
||||
|
||||
def handle_special_TIDYLINK(special)
|
||||
text = special.text.strip
|
||||
return text[1..-1] if text[0,1] == '\\'
|
||||
unless text =~ /\[\[(\S+?)\s+(.+?)\]\]/
|
||||
return text
|
||||
end
|
||||
url = $1
|
||||
label = $2
|
||||
label = RDocFormatter.new(label).to_html
|
||||
label = label.split.select{|x| x =~ /\S/}.
|
||||
map{|x| x.chomp}.join(' ')
|
||||
|
||||
case url
|
||||
when /link:(\S+)/
|
||||
return %{<a href="#{$1}">#{label}</a>}
|
||||
when /img:(\S+)/
|
||||
return %{<img src="http://#{$1}" alt="#{label}" />}
|
||||
when /rubytalk:(\S+)/
|
||||
return %{<a href="http://ruby-talk.org/blade/#{$1}">#{label}</a>}
|
||||
when /rubygarden:(\S+)/
|
||||
return %{<a href="http://www.rubygarden.org/ruby?#{$1}">#{label}</a>}
|
||||
when /c2:(\S+)/
|
||||
return %{<a href="http://c2.com/cgi/wiki?#{$1}">#{label}</a>}
|
||||
when /isbn:(\S+)/
|
||||
return %{<a href="http://search.barnesandnoble.com/bookSearch/} +
|
||||
%{isbnInquiry.asp?isbn=#{$1}">#{label}</a>}
|
||||
end
|
||||
|
||||
unless url =~ /\w+?:/
|
||||
url = "http://#{url}"
|
||||
end
|
||||
|
||||
"<a href=\"#{url}\">#{label}</a>"
|
||||
end
|
||||
end
|
||||
|
||||
class RDocFormatter
|
||||
def initialize(text)
|
||||
@text = text
|
||||
end
|
||||
|
||||
def to_html
|
||||
markup = RDocMarkup.new
|
||||
h = HyperLinkHtml.new
|
||||
markup.convert(@text, h)
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
require "rdoc/markup/simple_markup"
|
||||
require 'rdoc/markup/simple_markup/to_html'
|
||||
rescue LoadError
|
||||
# use old version if available
|
||||
require 'markup/simple_markup'
|
||||
require 'markup/simple_markup/to_html'
|
||||
end
|
||||
|
||||
module RDocSupport
|
||||
|
||||
# A simple +rdoc+ markup class which recognizes some additional
|
||||
# formatting commands suitable for Wiki use.
|
||||
class RDocMarkup < SM::SimpleMarkup
|
||||
def initialize
|
||||
super()
|
||||
|
||||
pre = '(?:\\s|^|\\\\)'
|
||||
|
||||
# links of the form
|
||||
# [[<url> description with spaces]]
|
||||
add_special(/((\\)?\[\[\S+?\s+.+?\]\])/,:TIDYLINK)
|
||||
|
||||
# and external references
|
||||
add_special(/((\\)?(link:|anchor:|http:|mailto:|ftp:|img:|www\.)\S+\w\/?)/,
|
||||
:HYPERLINK)
|
||||
|
||||
# <br/>
|
||||
add_special(%r{(#{pre}<br/>)}, :BR)
|
||||
|
||||
# and <center> ... </center>
|
||||
add_html("center", :CENTER)
|
||||
end
|
||||
|
||||
def convert(text, handler)
|
||||
super.sub(/^<p>\n/, '').sub(/<\/p>$/, '')
|
||||
end
|
||||
end
|
||||
|
||||
# Handle special hyperlinking requirments for RDoc formatted
|
||||
# entries. Requires RDoc
|
||||
|
||||
class HyperLinkHtml < SM::ToHtml
|
||||
|
||||
# Initialize the HyperLinkHtml object.
|
||||
# [path] location of the node
|
||||
# [site] object representing the whole site (typically of class
|
||||
# +Site+)
|
||||
def initialize
|
||||
super()
|
||||
add_tag(:CENTER, "<center>", "</center>")
|
||||
end
|
||||
|
||||
# handle <br/>
|
||||
def handle_special_BR(special)
|
||||
return "<br/>" if special.text[0,1] == '\\'
|
||||
special.text
|
||||
end
|
||||
|
||||
# We're invoked with a potential external hyperlink.
|
||||
# [mailto:] just gets inserted.
|
||||
# [http:] links are checked to see if they
|
||||
# reference an image. If so, that image gets inserted
|
||||
# using an <img> tag. Otherwise a conventional <a href>
|
||||
# is used.
|
||||
# [img:] insert a <tt><img></tt> tag
|
||||
# [link:] used to insert arbitrary <tt><a></tt> references
|
||||
# [anchor:] used to create an anchor
|
||||
def handle_special_HYPERLINK(special)
|
||||
text = special.text.strip
|
||||
return text[1..-1] if text[0,1] == '\\'
|
||||
url = special.text.strip
|
||||
if url =~ /([A-Za-z]+):(.*)/
|
||||
type = $1
|
||||
path = $2
|
||||
else
|
||||
type = "http"
|
||||
path = url
|
||||
url = "http://#{url}"
|
||||
end
|
||||
|
||||
case type
|
||||
when "http"
|
||||
if url =~ /\.(gif|png|jpg|jpeg|bmp)$/
|
||||
"<img src=\"#{url}\"/>"
|
||||
else
|
||||
"<a href=\"#{url}\">#{url.sub(%r{^\w+:/*}, '')}</a>"
|
||||
end
|
||||
when "img"
|
||||
"<img src=\"#{path}\"/>"
|
||||
when "link"
|
||||
"<a href=\"#{path}\">#{path}</a>"
|
||||
when "anchor"
|
||||
"<a name=\"#{path}\"></a>"
|
||||
else
|
||||
"<a href=\"#{url}\">#{url.sub(%r{^\w+:/*}, '')}</a>"
|
||||
end
|
||||
end
|
||||
|
||||
# Here's a hyperlink where the label is different to the URL
|
||||
# [[url label that may contain spaces]]
|
||||
#
|
||||
|
||||
def handle_special_TIDYLINK(special)
|
||||
text = special.text.strip
|
||||
return text[1..-1] if text[0,1] == '\\'
|
||||
unless text =~ /\[\[(\S+?)\s+(.+?)\]\]/
|
||||
return text
|
||||
end
|
||||
url = $1
|
||||
label = $2
|
||||
label = RDocFormatter.new(label).to_html
|
||||
label = label.split.select{|x| x =~ /\S/}.
|
||||
map{|x| x.chomp}.join(' ')
|
||||
|
||||
case url
|
||||
when /link:(\S+)/
|
||||
return %{<a href="#{$1}">#{label}</a>}
|
||||
when /img:(\S+)/
|
||||
return %{<img src="http://#{$1}" alt="#{label}" />}
|
||||
when /rubytalk:(\S+)/
|
||||
return %{<a href="http://ruby-talk.org/blade/#{$1}">#{label}</a>}
|
||||
when /rubygarden:(\S+)/
|
||||
return %{<a href="http://www.rubygarden.org/ruby?#{$1}">#{label}</a>}
|
||||
when /c2:(\S+)/
|
||||
return %{<a href="http://c2.com/cgi/wiki?#{$1}">#{label}</a>}
|
||||
when /isbn:(\S+)/
|
||||
return %{<a href="http://search.barnesandnoble.com/bookSearch/} +
|
||||
%{isbnInquiry.asp?isbn=#{$1}">#{label}</a>}
|
||||
end
|
||||
|
||||
unless url =~ /\w+?:/
|
||||
url = "http://#{url}"
|
||||
end
|
||||
|
||||
"<a href=\"#{url}\">#{label}</a>"
|
||||
end
|
||||
end
|
||||
|
||||
class RDocFormatter
|
||||
def initialize(text)
|
||||
@text = text
|
||||
end
|
||||
|
||||
def to_html
|
||||
markup = RDocMarkup.new
|
||||
h = HyperLinkHtml.new
|
||||
markup.convert(@text, h)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
1466
libraries/redcloth_for_tex.rb
Executable file → Normal file
1466
libraries/redcloth_for_tex.rb
Executable file → Normal file
File diff suppressed because it is too large
Load diff
214
libraries/url_rewriting_hack.rb
Executable file → Normal file
214
libraries/url_rewriting_hack.rb
Executable file → Normal file
|
@ -1,107 +1,107 @@
|
|||
# Below are some hacks to Rails internal classes that implement Instiki URLs scheme.
|
||||
# It is no doubt a bad practice to override internal implementation of anything.
|
||||
# When Rails implements some way to do it in the framework, this code should be replaced
|
||||
# with something more legitimate.
|
||||
|
||||
# In Instiki URLs are mapped to the ActionPack actions, possibly performed on a particular
|
||||
# web (sub-wiki) and page within that web.
|
||||
#
|
||||
# 1. Controller is determined by action name (default is 'wiki')
|
||||
# 2. '/name1/' maps to action 'name1', unspecified web
|
||||
# Example: http://localhost/new_system/
|
||||
# 3. Special case of above, URI '/wiki/' maps to action 'index', because Rails sets this address
|
||||
# when default controller name is specified as 'wiki', and an application root
|
||||
# (http://localhost:2500/)is requested.
|
||||
# 4. '/name1/name2/' maps to web 'name1', action 'name2'
|
||||
# Example: http://localhost/mywiki/search/
|
||||
# 5. '/name1/name2/name3/' maps to web 'name1', action 'name2',
|
||||
# Example: http://localhost/mywiki/show/HomePage
|
||||
|
||||
|
||||
require 'dispatcher'
|
||||
|
||||
# Overrides Rails DispatchServlet.parse_uri
|
||||
class DispatchServlet
|
||||
|
||||
def self.parse_uri(path)
|
||||
result = parse_path(path)
|
||||
if result
|
||||
result[:controller] = ActionMapper.map_to_controller(result[:action])
|
||||
result
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def self.parse_path(path)
|
||||
ApplicationController.logger.debug "Parsing URI '#{path}'"
|
||||
component = '([-_a-zA-Z0-9]+)'
|
||||
page_name = '(.*)'
|
||||
case path.sub(%r{^/(?:fcgi|mruby|cgi)/}, "/")
|
||||
when '/wiki/'
|
||||
{ :web => nil, :controller => 'wiki', :action => 'index' }
|
||||
when %r{^/#{component}/?$}
|
||||
{ :web => nil, :controller => 'wiki', :action => $1 }
|
||||
when %r{^/#{component}/#{component}/?$}
|
||||
{ :web => $1, :controller => 'wiki', :action => $2 }
|
||||
when %r{^/#{component}/#{component}/(.*)/?$}
|
||||
{ :web => $1, :controller => 'wiki', :action => $2, :id => drop_trailing_slash($3) }
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def self.drop_trailing_slash(line)
|
||||
if line[-1] == ?/
|
||||
line.chop
|
||||
else
|
||||
line
|
||||
end
|
||||
end
|
||||
|
||||
class ActionMapper
|
||||
|
||||
@@action_to_controller_map = {
|
||||
'file' => 'file',
|
||||
'pic' => 'file'
|
||||
}
|
||||
|
||||
def self.map_to_controller(action)
|
||||
@@action_to_controller_map[action] || 'wiki'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
require 'action_controller/url_rewriter.rb'
|
||||
|
||||
# Overrides parts of AP UrlRewriter to achieve the Instiki's legacy URL scheme
|
||||
module ActionController
|
||||
class UrlRewriter
|
||||
|
||||
VALID_OPTIONS << :web unless VALID_OPTIONS.include? :web
|
||||
|
||||
private
|
||||
|
||||
def resolve_aliases(options)
|
||||
options[:controller_prefix] = options[:web] unless options[:web].nil?
|
||||
options
|
||||
end
|
||||
|
||||
def controller_name(options, controller_prefix)
|
||||
ensure_slash_suffix(options, :controller_prefix)
|
||||
|
||||
controller_name = case options[:controller_prefix]
|
||||
when String: options[:controller_prefix]
|
||||
when false : ""
|
||||
when nil : controller_prefix || ""
|
||||
end
|
||||
# In Instiki we don't need the controller name (there is only one comtroller, anyway)
|
||||
# therefore the below line is commented out
|
||||
# controller_name << (options[:controller] + "/") if options[:controller]
|
||||
return controller_name
|
||||
end
|
||||
end
|
||||
end
|
||||
# Below are some hacks to Rails internal classes that implement Instiki URLs scheme.
|
||||
# It is no doubt a bad practice to override internal implementation of anything.
|
||||
# When Rails implements some way to do it in the framework, this code should be replaced
|
||||
# with something more legitimate.
|
||||
|
||||
# In Instiki URLs are mapped to the ActionPack actions, possibly performed on a particular
|
||||
# web (sub-wiki) and page within that web.
|
||||
#
|
||||
# 1. Controller is determined by action name (default is 'wiki')
|
||||
# 2. '/name1/' maps to action 'name1', unspecified web
|
||||
# Example: http://localhost/new_system/
|
||||
# 3. Special case of above, URI '/wiki/' maps to action 'index', because Rails sets this address
|
||||
# when default controller name is specified as 'wiki', and an application root
|
||||
# (http://localhost:2500/)is requested.
|
||||
# 4. '/name1/name2/' maps to web 'name1', action 'name2'
|
||||
# Example: http://localhost/mywiki/search/
|
||||
# 5. '/name1/name2/name3/' maps to web 'name1', action 'name2',
|
||||
# Example: http://localhost/mywiki/show/HomePage
|
||||
|
||||
|
||||
require 'dispatcher'
|
||||
|
||||
# Overrides Rails DispatchServlet.parse_uri
|
||||
class DispatchServlet
|
||||
|
||||
def self.parse_uri(path)
|
||||
result = parse_path(path)
|
||||
if result
|
||||
result[:controller] = ActionMapper.map_to_controller(result[:action])
|
||||
result
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def self.parse_path(path)
|
||||
ApplicationController.logger.debug "Parsing URI '#{path}'"
|
||||
component = '([-_a-zA-Z0-9]+)'
|
||||
page_name = '(.*)'
|
||||
case path.sub(%r{^/(?:fcgi|mruby|cgi)/}, "/")
|
||||
when '/wiki/'
|
||||
{ :web => nil, :controller => 'wiki', :action => 'index' }
|
||||
when %r{^/#{component}/?$}
|
||||
{ :web => nil, :controller => 'wiki', :action => $1 }
|
||||
when %r{^/#{component}/#{component}/?$}
|
||||
{ :web => $1, :controller => 'wiki', :action => $2 }
|
||||
when %r{^/#{component}/#{component}/(.*)/?$}
|
||||
{ :web => $1, :controller => 'wiki', :action => $2, :id => drop_trailing_slash($3) }
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def self.drop_trailing_slash(line)
|
||||
if line[-1] == ?/
|
||||
line.chop
|
||||
else
|
||||
line
|
||||
end
|
||||
end
|
||||
|
||||
class ActionMapper
|
||||
|
||||
@@action_to_controller_map = {
|
||||
'file' => 'file',
|
||||
'pic' => 'file'
|
||||
}
|
||||
|
||||
def self.map_to_controller(action)
|
||||
@@action_to_controller_map[action] || 'wiki'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
require 'action_controller/url_rewriter.rb'
|
||||
|
||||
# Overrides parts of AP UrlRewriter to achieve the Instiki's legacy URL scheme
|
||||
module ActionController
|
||||
class UrlRewriter
|
||||
|
||||
VALID_OPTIONS << :web unless VALID_OPTIONS.include? :web
|
||||
|
||||
private
|
||||
|
||||
def resolve_aliases(options)
|
||||
options[:controller_prefix] = options[:web] unless options[:web].nil?
|
||||
options
|
||||
end
|
||||
|
||||
def controller_name(options, controller_prefix)
|
||||
ensure_slash_suffix(options, :controller_prefix)
|
||||
|
||||
controller_name = case options[:controller_prefix]
|
||||
when String: options[:controller_prefix]
|
||||
when false : ""
|
||||
when nil : controller_prefix || ""
|
||||
end
|
||||
# In Instiki we don't need the controller name (there is only one comtroller, anyway)
|
||||
# therefore the below line is commented out
|
||||
# controller_name << (options[:controller] + "/") if options[:controller]
|
||||
return controller_name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
36
natives/osx/desktop_launcher/AppDelegate.h
Executable file → Normal file
36
natives/osx/desktop_launcher/AppDelegate.h
Executable file → Normal file
|
@ -1,18 +1,18 @@
|
|||
/* AppDelegate */
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface AppDelegate : NSObject
|
||||
{
|
||||
IBOutlet NSMenu* statusMenu;
|
||||
NSTask* serverCommand;
|
||||
int processID;
|
||||
BOOL shouldOpenUntitled;
|
||||
|
||||
NSNetService* service;
|
||||
}
|
||||
- (IBAction)about:(id)sender;
|
||||
- (IBAction)goToHomepage:(id)sender;
|
||||
- (IBAction)goToInstikiOrg:(id)sender;
|
||||
- (IBAction)quit:(id)sender;
|
||||
@end
|
||||
/* AppDelegate */
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface AppDelegate : NSObject
|
||||
{
|
||||
IBOutlet NSMenu* statusMenu;
|
||||
NSTask* serverCommand;
|
||||
int processID;
|
||||
BOOL shouldOpenUntitled;
|
||||
|
||||
NSNetService* service;
|
||||
}
|
||||
- (IBAction)about:(id)sender;
|
||||
- (IBAction)goToHomepage:(id)sender;
|
||||
- (IBAction)goToInstikiOrg:(id)sender;
|
||||
- (IBAction)quit:(id)sender;
|
||||
@end
|
||||
|
|
218
natives/osx/desktop_launcher/AppDelegate.mm
Executable file → Normal file
218
natives/osx/desktop_launcher/AppDelegate.mm
Executable file → Normal file
|
@ -1,109 +1,109 @@
|
|||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#import "AppDelegate.h"
|
||||
|
||||
int launch_ruby (char const* cmd)
|
||||
{
|
||||
int pId, parentID = getpid();
|
||||
if((pId = fork()) == 0) // child
|
||||
{
|
||||
NSLog(@"set child (%d) to pgrp %d", getpid(), parentID);
|
||||
setpgrp(0, parentID);
|
||||
system(cmd);
|
||||
return 0;
|
||||
}
|
||||
else // parent
|
||||
{
|
||||
NSLog(@"started child process: %d", pId);
|
||||
return pId;
|
||||
}
|
||||
}
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (NSString*)storageDirectory
|
||||
{
|
||||
NSString* dir = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/Instiki"];
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:dir attributes:nil];
|
||||
return dir;
|
||||
}
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
setpgrp(0, getpid());
|
||||
|
||||
if([[[[NSBundle mainBundle] infoDictionary] objectForKey:@"LSUIElement"] isEqualToString:@"1"])
|
||||
{
|
||||
NSStatusBar* bar = [NSStatusBar systemStatusBar];
|
||||
NSStatusItem* item = [[bar statusItemWithLength:NSVariableStatusItemLength] retain];
|
||||
[item setTitle:@"Wiki"];
|
||||
[item setHighlightMode:YES];
|
||||
[item setMenu:statusMenu];
|
||||
}
|
||||
|
||||
NSBundle* bundle = [NSBundle bundleForClass:[self class]];
|
||||
NSString* ruby = [bundle pathForResource:@"ruby" ofType:nil];
|
||||
NSString* script = [[bundle resourcePath] stringByAppendingPathComponent:@"rb_src/instiki.rb"];
|
||||
if(ruby && script)
|
||||
{
|
||||
NSString* cmd = [NSString stringWithFormat:
|
||||
@"%@ -I '%@' -I '%@' '%@' -s --storage='%@'",
|
||||
ruby,
|
||||
[[bundle resourcePath] stringByAppendingPathComponent:@"lib/ruby/1.8"],
|
||||
[[bundle resourcePath] stringByAppendingPathComponent:@"lib/ruby/1.8/powerpc-darwin"],
|
||||
script,
|
||||
[self storageDirectory]
|
||||
];
|
||||
NSLog(@"starting %@", cmd);
|
||||
processID = launch_ruby([cmd UTF8String]);
|
||||
}
|
||||
|
||||
/* public the service using rendezvous */
|
||||
service = [[NSNetService alloc]
|
||||
initWithDomain:@"" // default domain
|
||||
type:@"_http._tcp."
|
||||
name:[NSString stringWithFormat:@"%@'s Instiki", NSFullUserName()]
|
||||
port:2500];
|
||||
[service publish];
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification*)aNotification
|
||||
{
|
||||
[service stop];
|
||||
[service release];
|
||||
|
||||
kill(0, SIGTERM);
|
||||
}
|
||||
|
||||
- (IBAction)about:(id)sender
|
||||
{
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
[NSApp orderFrontStandardAboutPanel:self];
|
||||
}
|
||||
|
||||
- (IBAction)goToHomepage:(id)sender
|
||||
{
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://localhost:2500/"]];
|
||||
}
|
||||
|
||||
- (IBAction)goToInstikiOrg:(id)sender
|
||||
{
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.instiki.org/"]];
|
||||
}
|
||||
|
||||
- (BOOL)applicationShouldOpenUntitledFile:(NSApplication*)sender
|
||||
{
|
||||
return shouldOpenUntitled ?: (shouldOpenUntitled = YES, NO);
|
||||
}
|
||||
|
||||
- (BOOL)applicationOpenUntitledFile:(NSApplication*)theApplication
|
||||
{
|
||||
return [self goToHomepage:self], YES;
|
||||
}
|
||||
|
||||
- (IBAction)quit:(id)sender
|
||||
{
|
||||
[NSApp terminate:self];
|
||||
}
|
||||
|
||||
@end
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#import "AppDelegate.h"
|
||||
|
||||
int launch_ruby (char const* cmd)
|
||||
{
|
||||
int pId, parentID = getpid();
|
||||
if((pId = fork()) == 0) // child
|
||||
{
|
||||
NSLog(@"set child (%d) to pgrp %d", getpid(), parentID);
|
||||
setpgrp(0, parentID);
|
||||
system(cmd);
|
||||
return 0;
|
||||
}
|
||||
else // parent
|
||||
{
|
||||
NSLog(@"started child process: %d", pId);
|
||||
return pId;
|
||||
}
|
||||
}
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (NSString*)storageDirectory
|
||||
{
|
||||
NSString* dir = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/Instiki"];
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:dir attributes:nil];
|
||||
return dir;
|
||||
}
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
setpgrp(0, getpid());
|
||||
|
||||
if([[[[NSBundle mainBundle] infoDictionary] objectForKey:@"LSUIElement"] isEqualToString:@"1"])
|
||||
{
|
||||
NSStatusBar* bar = [NSStatusBar systemStatusBar];
|
||||
NSStatusItem* item = [[bar statusItemWithLength:NSVariableStatusItemLength] retain];
|
||||
[item setTitle:@"Wiki"];
|
||||
[item setHighlightMode:YES];
|
||||
[item setMenu:statusMenu];
|
||||
}
|
||||
|
||||
NSBundle* bundle = [NSBundle bundleForClass:[self class]];
|
||||
NSString* ruby = [bundle pathForResource:@"ruby" ofType:nil];
|
||||
NSString* script = [[bundle resourcePath] stringByAppendingPathComponent:@"rb_src/instiki.rb"];
|
||||
if(ruby && script)
|
||||
{
|
||||
NSString* cmd = [NSString stringWithFormat:
|
||||
@"%@ -I '%@' -I '%@' '%@' -s --storage='%@'",
|
||||
ruby,
|
||||
[[bundle resourcePath] stringByAppendingPathComponent:@"lib/ruby/1.8"],
|
||||
[[bundle resourcePath] stringByAppendingPathComponent:@"lib/ruby/1.8/powerpc-darwin"],
|
||||
script,
|
||||
[self storageDirectory]
|
||||
];
|
||||
NSLog(@"starting %@", cmd);
|
||||
processID = launch_ruby([cmd UTF8String]);
|
||||
}
|
||||
|
||||
/* public the service using rendezvous */
|
||||
service = [[NSNetService alloc]
|
||||
initWithDomain:@"" // default domain
|
||||
type:@"_http._tcp."
|
||||
name:[NSString stringWithFormat:@"%@'s Instiki", NSFullUserName()]
|
||||
port:2500];
|
||||
[service publish];
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification*)aNotification
|
||||
{
|
||||
[service stop];
|
||||
[service release];
|
||||
|
||||
kill(0, SIGTERM);
|
||||
}
|
||||
|
||||
- (IBAction)about:(id)sender
|
||||
{
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
[NSApp orderFrontStandardAboutPanel:self];
|
||||
}
|
||||
|
||||
- (IBAction)goToHomepage:(id)sender
|
||||
{
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://localhost:2500/"]];
|
||||
}
|
||||
|
||||
- (IBAction)goToInstikiOrg:(id)sender
|
||||
{
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.instiki.org/"]];
|
||||
}
|
||||
|
||||
- (BOOL)applicationShouldOpenUntitledFile:(NSApplication*)sender
|
||||
{
|
||||
return shouldOpenUntitled ?: (shouldOpenUntitled = YES, NO);
|
||||
}
|
||||
|
||||
- (BOOL)applicationOpenUntitledFile:(NSApplication*)theApplication
|
||||
{
|
||||
return [self goToHomepage:self], YES;
|
||||
}
|
||||
|
||||
- (IBAction)quit:(id)sender
|
||||
{
|
||||
[NSApp terminate:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
30
natives/osx/desktop_launcher/Credits.html
Executable file → Normal file
30
natives/osx/desktop_launcher/Credits.html
Executable file → Normal file
|
@ -1,16 +1,16 @@
|
|||
<dl>
|
||||
<dt>Engineering:</dt>
|
||||
<dd>Some people</dd>
|
||||
|
||||
<dt>Human Interface Design:</dt>
|
||||
<dd>Some other people</dd>
|
||||
|
||||
<dt>Testing:</dt>
|
||||
<dd>Hopefully not nobody</dd>
|
||||
|
||||
<dt>Documentation:</dt>
|
||||
<dd>Whoever</dd>
|
||||
|
||||
<dt>With special thanks to:</dt>
|
||||
<dd>Mom</dd>
|
||||
<dl>
|
||||
<dt>Engineering:</dt>
|
||||
<dd>Some people</dd>
|
||||
|
||||
<dt>Human Interface Design:</dt>
|
||||
<dd>Some other people</dd>
|
||||
|
||||
<dt>Testing:</dt>
|
||||
<dd>Hopefully not nobody</dd>
|
||||
|
||||
<dt>Documentation:</dt>
|
||||
<dd>Whoever</dd>
|
||||
|
||||
<dt>With special thanks to:</dt>
|
||||
<dd>Mom</dd>
|
||||
</dl>
|
0
natives/osx/desktop_launcher/English.lproj/InfoPlist.strings
Executable file → Normal file
0
natives/osx/desktop_launcher/English.lproj/InfoPlist.strings
Executable file → Normal file
24
natives/osx/desktop_launcher/English.lproj/MainMenu.nib/classes.nib
generated
Executable file → Normal file
24
natives/osx/desktop_launcher/English.lproj/MainMenu.nib/classes.nib
generated
Executable file → Normal file
|
@ -1,13 +1,13 @@
|
|||
{
|
||||
IBClasses = (
|
||||
{
|
||||
ACTIONS = {about = id; goToHomepage = id; goToInstikiOrg = id; quit = id; };
|
||||
CLASS = AppDelegate;
|
||||
LANGUAGE = ObjC;
|
||||
OUTLETS = {statusMenu = NSMenu; };
|
||||
SUPERCLASS = NSObject;
|
||||
},
|
||||
{CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }
|
||||
);
|
||||
IBVersion = 1;
|
||||
{
|
||||
IBClasses = (
|
||||
{
|
||||
ACTIONS = {about = id; goToHomepage = id; goToInstikiOrg = id; quit = id; };
|
||||
CLASS = AppDelegate;
|
||||
LANGUAGE = ObjC;
|
||||
OUTLETS = {statusMenu = NSMenu; };
|
||||
SUPERCLASS = NSObject;
|
||||
},
|
||||
{CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }
|
||||
);
|
||||
IBVersion = 1;
|
||||
}
|
48
natives/osx/desktop_launcher/English.lproj/MainMenu.nib/info.nib
generated
Executable file → Normal file
48
natives/osx/desktop_launcher/English.lproj/MainMenu.nib/info.nib
generated
Executable file → Normal file
|
@ -1,24 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IBDocumentLocation</key>
|
||||
<string>109 6 356 240 0 0 1440 878 </string>
|
||||
<key>IBEditorPositions</key>
|
||||
<dict>
|
||||
<key>206</key>
|
||||
<string>112 300 116 87 0 0 1440 878 </string>
|
||||
<key>29</key>
|
||||
<string>241 316 70 44 0 0 1440 878 </string>
|
||||
</dict>
|
||||
<key>IBFramework Version</key>
|
||||
<string>349.0</string>
|
||||
<key>IBOpenObjects</key>
|
||||
<array>
|
||||
<integer>206</integer>
|
||||
<integer>29</integer>
|
||||
</array>
|
||||
<key>IBSystem Version</key>
|
||||
<string>7H63</string>
|
||||
</dict>
|
||||
</plist>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IBDocumentLocation</key>
|
||||
<string>109 6 356 240 0 0 1440 878 </string>
|
||||
<key>IBEditorPositions</key>
|
||||
<dict>
|
||||
<key>206</key>
|
||||
<string>112 300 116 87 0 0 1440 878 </string>
|
||||
<key>29</key>
|
||||
<string>241 316 70 44 0 0 1440 878 </string>
|
||||
</dict>
|
||||
<key>IBFramework Version</key>
|
||||
<string>349.0</string>
|
||||
<key>IBOpenObjects</key>
|
||||
<array>
|
||||
<integer>206</integer>
|
||||
<integer>29</integer>
|
||||
</array>
|
||||
<key>IBSystem Version</key>
|
||||
<string>7H63</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
0
natives/osx/desktop_launcher/English.lproj/MainMenu.nib/objects.nib
generated
Executable file → Normal file
0
natives/osx/desktop_launcher/English.lproj/MainMenu.nib/objects.nib
generated
Executable file → Normal file
24
natives/osx/desktop_launcher/Info.plist
Executable file → Normal file
24
natives/osx/desktop_launcher/Info.plist
Executable file → Normal file
|
@ -1,13 +1,13 @@
|
|||
{
|
||||
CFBundleDevelopmentRegion = English;
|
||||
CFBundleExecutable = Instiki;
|
||||
CFBundleIconFile = "";
|
||||
CFBundleIdentifier = "com.nextangle.instiki";
|
||||
CFBundleInfoDictionaryVersion = "6.0";
|
||||
CFBundlePackageType = APPL;
|
||||
CFBundleSignature = WIKI;
|
||||
CFBundleVersion = "0.9.0";
|
||||
LSUIElement = 1;
|
||||
NSMainNibFile = MainMenu;
|
||||
NSPrincipalClass = NSApplication;
|
||||
{
|
||||
CFBundleDevelopmentRegion = English;
|
||||
CFBundleExecutable = Instiki;
|
||||
CFBundleIconFile = "";
|
||||
CFBundleIdentifier = "com.nextangle.instiki";
|
||||
CFBundleInfoDictionaryVersion = "6.0";
|
||||
CFBundlePackageType = APPL;
|
||||
CFBundleSignature = WIKI;
|
||||
CFBundleVersion = "0.9.0";
|
||||
LSUIElement = 1;
|
||||
NSMainNibFile = MainMenu;
|
||||
NSPrincipalClass = NSApplication;
|
||||
}
|
1184
natives/osx/desktop_launcher/Instiki.xcode/project.pbxproj
Executable file → Normal file
1184
natives/osx/desktop_launcher/Instiki.xcode/project.pbxproj
Executable file → Normal file
File diff suppressed because it is too large
Load diff
14
natives/osx/desktop_launcher/Instiki_Prefix.pch
Executable file → Normal file
14
natives/osx/desktop_launcher/Instiki_Prefix.pch
Executable file → Normal file
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// Prefix header for all source files of the 'Instiki' target in the 'Instiki' project
|
||||
//
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#endif
|
||||
//
|
||||
// Prefix header for all source files of the 'Instiki' target in the 'Instiki' project
|
||||
//
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#endif
|
||||
|
|
18
natives/osx/desktop_launcher/MakeDMG.sh
Executable file → Normal file
18
natives/osx/desktop_launcher/MakeDMG.sh
Executable file → Normal file
|
@ -1,9 +1,9 @@
|
|||
#!/bin/sh
|
||||
|
||||
hdiutil create -size 12m -fs HFS+ -volname Instiki -ov /tmp/Instiki_12MB.dmg
|
||||
hdiutil mount /tmp/Instiki_12MB.dmg
|
||||
# strip ~/ruby/instiki/natives/osx/build/Instiki.app/Contents/MacOS/Instiki
|
||||
ditto ~/ruby/instiki/natives/osx/desktop_launcher/build/Instiki.app /Volumes/Instiki/Instiki.app
|
||||
hdiutil unmount /Volumes/Instiki
|
||||
hdiutil convert -format UDZO -o /tmp/Instiki.dmg /tmp/Instiki_12MB.dmg
|
||||
hdiutil internet-enable -yes /tmp/Instiki.dmg
|
||||
#!/bin/sh
|
||||
|
||||
hdiutil create -size 12m -fs HFS+ -volname Instiki -ov /tmp/Instiki_12MB.dmg
|
||||
hdiutil mount /tmp/Instiki_12MB.dmg
|
||||
# strip ~/ruby/instiki/natives/osx/build/Instiki.app/Contents/MacOS/Instiki
|
||||
ditto ~/ruby/instiki/natives/osx/desktop_launcher/build/Instiki.app /Volumes/Instiki/Instiki.app
|
||||
hdiutil unmount /Volumes/Instiki
|
||||
hdiutil convert -format UDZO -o /tmp/Instiki.dmg /tmp/Instiki_12MB.dmg
|
||||
hdiutil internet-enable -yes /tmp/Instiki.dmg
|
||||
|
|
28
natives/osx/desktop_launcher/main.mm
Executable file → Normal file
28
natives/osx/desktop_launcher/main.mm
Executable file → Normal file
|
@ -1,14 +1,14 @@
|
|||
//
|
||||
// main.mm
|
||||
// Instiki
|
||||
//
|
||||
// Created by Allan Odgaard on Thu May 20 2004.
|
||||
// Copyright (c) 2004 MacroMates. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
int main (int argc, char const* argv[])
|
||||
{
|
||||
return NSApplicationMain(argc, argv);
|
||||
}
|
||||
//
|
||||
// main.mm
|
||||
// Instiki
|
||||
//
|
||||
// Created by Allan Odgaard on Thu May 20 2004.
|
||||
// Copyright (c) 2004 MacroMates. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
int main (int argc, char const* argv[])
|
||||
{
|
||||
return NSApplicationMain(argc, argv);
|
||||
}
|
||||
|
|
32
natives/osx/desktop_launcher/version.plist
Executable file → Normal file
32
natives/osx/desktop_launcher/version.plist
Executable file → Normal file
|
@ -1,16 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildVersion</key>
|
||||
<string>17</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.1</string>
|
||||
<key>ProjectName</key>
|
||||
<string>NibPBTemplates</string>
|
||||
<key>SourceVersion</key>
|
||||
<string>1150000</string>
|
||||
</dict>
|
||||
</plist>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildVersion</key>
|
||||
<string>17</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.1</string>
|
||||
<key>ProjectName</key>
|
||||
<string>NibPBTemplates</string>
|
||||
<key>SourceVersion</key>
|
||||
<string>1150000</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
0
public/images/.images_go_here
Executable file → Normal file
0
public/images/.images_go_here
Executable file → Normal file
|
@ -1,48 +1,48 @@
|
|||
function proposeAddress() {
|
||||
document.getElementById('address').value =
|
||||
document.getElementById('name').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
||||
}
|
||||
|
||||
function cleanAddress() {
|
||||
document.getElementById('address').value =
|
||||
document.getElementById('address').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
||||
}
|
||||
|
||||
function validateSetup() {
|
||||
if (document.getElementById('system_password').value == "") {
|
||||
alert("You must enter the system password");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('name').value == "") {
|
||||
alert("You must pick a name for the web");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('address').value == "") {
|
||||
alert("You must pick an address for the web");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('password').value != "" &&
|
||||
document.getElementById('password').value != document.getElementById('password_check').value) {
|
||||
alert("The password and its verification doesn't match");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// overriding auto-complete by form managers
|
||||
// code by Chris Holland, lifted from
|
||||
// http://chrisholland.blogspot.com/2004/11/banks-protect-privacy-disable.html
|
||||
function overrideAutocomplete() {
|
||||
if (document.getElementsByTagName) {
|
||||
var inputElements = document.getElementsByTagName("input");
|
||||
for (i=0; inputElements[i]; i++) {
|
||||
if (inputElements[i].className && (inputElements[i].className.indexOf("disableAutoComplete") != -1)) {
|
||||
inputElements[i].setAttribute("autocomplete","off");
|
||||
}//if current input element has the disableAutoComplete class set.
|
||||
}//loop thru input elements
|
||||
}
|
||||
}
|
||||
function proposeAddress() {
|
||||
document.getElementById('address').value =
|
||||
document.getElementById('name').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
||||
}
|
||||
|
||||
function cleanAddress() {
|
||||
document.getElementById('address').value =
|
||||
document.getElementById('address').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
||||
}
|
||||
|
||||
function validateSetup() {
|
||||
if (document.getElementById('system_password').value == "") {
|
||||
alert("You must enter the system password");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('name').value == "") {
|
||||
alert("You must pick a name for the web");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('address').value == "") {
|
||||
alert("You must pick an address for the web");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('password').value != "" &&
|
||||
document.getElementById('password').value != document.getElementById('password_check').value) {
|
||||
alert("The password and its verification doesn't match");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// overriding auto-complete by form managers
|
||||
// code by Chris Holland, lifted from
|
||||
// http://chrisholland.blogspot.com/2004/11/banks-protect-privacy-disable.html
|
||||
function overrideAutocomplete() {
|
||||
if (document.getElementsByTagName) {
|
||||
var inputElements = document.getElementsByTagName("input");
|
||||
for (i=0; inputElements[i]; i++) {
|
||||
if (inputElements[i].className && (inputElements[i].className.indexOf("disableAutoComplete") != -1)) {
|
||||
inputElements[i].setAttribute("autocomplete","off");
|
||||
}//if current input element has the disableAutoComplete class set.
|
||||
}//loop thru input elements
|
||||
}
|
||||
}
|
||||
|
|
442
public/stylesheets/instiki.css
Executable file → Normal file
442
public/stylesheets/instiki.css
Executable file → Normal file
|
@ -1,222 +1,222 @@
|
|||
#Container {
|
||||
float: none;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#Content {
|
||||
margin: 0;
|
||||
padding: 5px;
|
||||
text-align: left;
|
||||
border-top: none;
|
||||
float: left;
|
||||
}
|
||||
|
||||
body { background-color: #fff; color: #333; }
|
||||
|
||||
body, p, ol, ul, td {
|
||||
font-family: verdana, arial, helvetica, sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
a { color: #000; }
|
||||
|
||||
.newWikiWord { background-color: #eee; }
|
||||
.newWikiWord a:hover { background-color: white; }
|
||||
|
||||
a:visited { color: #666; }
|
||||
a:hover { color: #fff; background-color:#000; }
|
||||
|
||||
h1, h2, h3 { color: #333; font-family: georgia, verdana; }
|
||||
h1 { font-size: 28px }
|
||||
h2 { font-size: 19px }
|
||||
h3 { font-size: 16px }
|
||||
|
||||
h1#pageName {
|
||||
margin: 5px 0px 0px 0px;
|
||||
padding: 0px 0px 0px 0px;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
h1#pageName small {
|
||||
color: grey;
|
||||
line-height: 10px;
|
||||
font-size: 10px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
a.nav, a.nav:link, a.nav:visited { color: #000; }
|
||||
a.nav:hover { color: #fff; background-color:#000; }
|
||||
|
||||
li { margin-bottom: 7px }
|
||||
|
||||
.navigation {
|
||||
margin-top: 5px;
|
||||
font-size : 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.navigation a:hover { color: #fff; background-color:#000; }
|
||||
|
||||
.navigation a {
|
||||
font-size: 11px;
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.navigation small a {
|
||||
font-weight: normal;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.navOn{
|
||||
font-size: 11px;
|
||||
color: grey;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.help {
|
||||
font-family: verdana, arial, helvetica, sans-serif;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.inputBox {
|
||||
font-family: verdana, arial, helvetica, sans-serif;
|
||||
font-size: 11px;
|
||||
background-color: #eee;
|
||||
padding: 5px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
display: block;
|
||||
margin: 0px 0px 20px 0px;
|
||||
padding: 0px 30px;
|
||||
font-size:11px;
|
||||
line-height:17px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: #eee;
|
||||
padding: 10px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
ol.setup {
|
||||
font-size: 19px;
|
||||
font-family: georgia, verdana;
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
ol.setup li {
|
||||
margin-bottom: 20px
|
||||
}
|
||||
|
||||
.byline {
|
||||
font-size: 10px;
|
||||
font-style: italic;
|
||||
margin-bottom: 10px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.references {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.diffdel, del.diffmod {
|
||||
background: pink;
|
||||
}
|
||||
|
||||
.diffins, ins.diffmod {
|
||||
background: lightgreen;
|
||||
}
|
||||
|
||||
#footer {
|
||||
height: 14px;
|
||||
padding: .25em 0;
|
||||
}
|
||||
|
||||
#footer p {
|
||||
font-size: 10px;
|
||||
color: gray;
|
||||
font-style: italic;
|
||||
margin: 0;
|
||||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#error {
|
||||
color: darkred;
|
||||
font-style: italic;
|
||||
width: 450px;
|
||||
}
|
||||
|
||||
#info {
|
||||
color: darkgreen;
|
||||
font-style: italic;
|
||||
width: 450px;
|
||||
}
|
||||
|
||||
#TextileHelp table {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#TextileHelp table+h3 {
|
||||
margin-top: 11px;
|
||||
}
|
||||
|
||||
#TextileHelp table td {
|
||||
font-size: 11px;
|
||||
padding: 3px;
|
||||
vertical-align: top;
|
||||
border-top: 1px dotted #ccc;
|
||||
}
|
||||
|
||||
#TextileHelp table td.arrow {
|
||||
padding-right: 5px;
|
||||
padding-left: 10px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
#TextileHelp table td.label {
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
font-size: 10px;
|
||||
padding-right: 15px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
#TextileHelp h3 {
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
font-weight: normal;
|
||||
margin: 0 0 5px 0;
|
||||
padding: 5px 0 0 0;
|
||||
}
|
||||
|
||||
#TextileHelp p {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.rightHandSide {
|
||||
float: right;
|
||||
width: 147px;
|
||||
margin-left: 10px;
|
||||
padding-left: 20px;
|
||||
border-left: 1px dotted #ccc;
|
||||
}
|
||||
|
||||
.rightHandSide p {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.newsList {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.newsList p {
|
||||
margin-bottom:30px
|
||||
#Container {
|
||||
float: none;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#Content {
|
||||
margin: 0;
|
||||
padding: 5px;
|
||||
text-align: left;
|
||||
border-top: none;
|
||||
float: left;
|
||||
}
|
||||
|
||||
body { background-color: #fff; color: #333; }
|
||||
|
||||
body, p, ol, ul, td {
|
||||
font-family: verdana, arial, helvetica, sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
a { color: #000; }
|
||||
|
||||
.newWikiWord { background-color: #eee; }
|
||||
.newWikiWord a:hover { background-color: white; }
|
||||
|
||||
a:visited { color: #666; }
|
||||
a:hover { color: #fff; background-color:#000; }
|
||||
|
||||
h1, h2, h3 { color: #333; font-family: georgia, verdana; }
|
||||
h1 { font-size: 28px }
|
||||
h2 { font-size: 19px }
|
||||
h3 { font-size: 16px }
|
||||
|
||||
h1#pageName {
|
||||
margin: 5px 0px 0px 0px;
|
||||
padding: 0px 0px 0px 0px;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
h1#pageName small {
|
||||
color: grey;
|
||||
line-height: 10px;
|
||||
font-size: 10px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
a.nav, a.nav:link, a.nav:visited { color: #000; }
|
||||
a.nav:hover { color: #fff; background-color:#000; }
|
||||
|
||||
li { margin-bottom: 7px }
|
||||
|
||||
.navigation {
|
||||
margin-top: 5px;
|
||||
font-size : 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.navigation a:hover { color: #fff; background-color:#000; }
|
||||
|
||||
.navigation a {
|
||||
font-size: 11px;
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.navigation small a {
|
||||
font-weight: normal;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.navOn{
|
||||
font-size: 11px;
|
||||
color: grey;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.help {
|
||||
font-family: verdana, arial, helvetica, sans-serif;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.inputBox {
|
||||
font-family: verdana, arial, helvetica, sans-serif;
|
||||
font-size: 11px;
|
||||
background-color: #eee;
|
||||
padding: 5px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
display: block;
|
||||
margin: 0px 0px 20px 0px;
|
||||
padding: 0px 30px;
|
||||
font-size:11px;
|
||||
line-height:17px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: #eee;
|
||||
padding: 10px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
ol.setup {
|
||||
font-size: 19px;
|
||||
font-family: georgia, verdana;
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
ol.setup li {
|
||||
margin-bottom: 20px
|
||||
}
|
||||
|
||||
.byline {
|
||||
font-size: 10px;
|
||||
font-style: italic;
|
||||
margin-bottom: 10px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.references {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.diffdel, del.diffmod {
|
||||
background: pink;
|
||||
}
|
||||
|
||||
.diffins, ins.diffmod {
|
||||
background: lightgreen;
|
||||
}
|
||||
|
||||
#footer {
|
||||
height: 14px;
|
||||
padding: .25em 0;
|
||||
}
|
||||
|
||||
#footer p {
|
||||
font-size: 10px;
|
||||
color: gray;
|
||||
font-style: italic;
|
||||
margin: 0;
|
||||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#error {
|
||||
color: darkred;
|
||||
font-style: italic;
|
||||
width: 450px;
|
||||
}
|
||||
|
||||
#info {
|
||||
color: darkgreen;
|
||||
font-style: italic;
|
||||
width: 450px;
|
||||
}
|
||||
|
||||
#TextileHelp table {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#TextileHelp table+h3 {
|
||||
margin-top: 11px;
|
||||
}
|
||||
|
||||
#TextileHelp table td {
|
||||
font-size: 11px;
|
||||
padding: 3px;
|
||||
vertical-align: top;
|
||||
border-top: 1px dotted #ccc;
|
||||
}
|
||||
|
||||
#TextileHelp table td.arrow {
|
||||
padding-right: 5px;
|
||||
padding-left: 10px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
#TextileHelp table td.label {
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
font-size: 10px;
|
||||
padding-right: 15px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
#TextileHelp h3 {
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
font-weight: normal;
|
||||
margin: 0 0 5px 0;
|
||||
padding: 5px 0 0 0;
|
||||
}
|
||||
|
||||
#TextileHelp p {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.rightHandSide {
|
||||
float: right;
|
||||
width: 147px;
|
||||
margin-left: 10px;
|
||||
padding-left: 20px;
|
||||
border-left: 1px dotted #ccc;
|
||||
}
|
||||
|
||||
.rightHandSide p {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.newsList {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.newsList p {
|
||||
margin-bottom:30px
|
||||
}
|
166
script/server
166
script/server
|
@ -1,83 +1,83 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
require 'webrick'
|
||||
require 'optparse'
|
||||
require 'fileutils'
|
||||
|
||||
pwd = File.expand_path(File.dirname(__FILE__) + "/..")
|
||||
|
||||
OPTIONS = {
|
||||
|
||||
# Overridable options
|
||||
:port => 2500,
|
||||
:ip => '127.0.0.1',
|
||||
:environment => 'production',
|
||||
:server_root => File.expand_path(File.dirname(__FILE__) + '/../public/'),
|
||||
:server_type => WEBrick::SimpleServer,
|
||||
:storage => "#{File.expand_path(FileUtils.pwd)}/storage",
|
||||
}
|
||||
|
||||
ARGV.options do |opts|
|
||||
script_name = File.basename($0)
|
||||
opts.banner = "Usage: ruby #{script_name} [options]"
|
||||
|
||||
opts.separator ''
|
||||
|
||||
opts.on('-p', '--port=port', Integer,
|
||||
'Runs Instiki on the specified port.',
|
||||
'Default: 2500') { |OPTIONS[:port]| }
|
||||
opts.on('-b', '--binding=ip', String,
|
||||
'Binds Rails to the specified ip.',
|
||||
'Default: 127.0.0.1') { |OPTIONS[:ip]| }
|
||||
opts.on('-i', '--index=controller', String,
|
||||
'Specifies an index controller that requests for root will go to (instead of congratulations screen).'
|
||||
) { |OPTIONS[:index_controller]| }
|
||||
opts.on('-e', '--environment=name', String,
|
||||
'Specifies the environment to run this server under (test/development/production).',
|
||||
'Default: production') { |OPTIONS[:environment]| }
|
||||
opts.on('-d', '--daemon',
|
||||
'Make Rails run as a Daemon (only works if fork is available -- meaning on *nix).'
|
||||
) { OPTIONS[:server_type] = WEBrick::Daemon }
|
||||
opts.on('-s', '--simple', '--simple-server',
|
||||
'[deprecated] Forces Instiki not to run as a Daemon if fork is available.',
|
||||
'Since version 0.10.0 this option is ignored.'
|
||||
) { puts "Warning: -s (--simple) option is deprecated. See instiki --help for details." }
|
||||
opts.on('-t', '--storage=storage', String,
|
||||
'Makes Instiki use the specified directory for storage.',
|
||||
'Default: ./storage/[port]') { |OPTIONS[:storage]| }
|
||||
opts.on('-v', '--verbose',
|
||||
'Enable debug-level logging'
|
||||
) { OPTIONS[:verbose] = true }
|
||||
|
||||
opts.separator ''
|
||||
|
||||
opts.on('-h', '--help',
|
||||
'Show this help message.') { puts opts; exit }
|
||||
|
||||
opts.parse!
|
||||
end
|
||||
|
||||
ENV['RAILS_ENV'] = OPTIONS[:environment]
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../config/environment')
|
||||
|
||||
if OPTIONS[:verbose]
|
||||
ActionController::Base.logger.level = Logger::DEBUG
|
||||
end
|
||||
|
||||
OPTIONS[:index_controller] = 'wiki'
|
||||
require 'webrick_server'
|
||||
|
||||
if OPTIONS[:environment] == 'production'
|
||||
storage_path = OPTIONS[:storage] + "/" + OPTIONS[:port].to_s
|
||||
else
|
||||
storage_path = OPTIONS[:storage] + "/" + OPTIONS[:environment] + "/" + OPTIONS[:port].to_s
|
||||
end
|
||||
FileUtils.mkdir_p(storage_path)
|
||||
|
||||
puts "=> Starting Instiki on http://#{OPTIONS[:ip]}:#{OPTIONS[:port]}"
|
||||
puts "=> Data files are stored in #{storage_path}"
|
||||
|
||||
require 'application'
|
||||
WikiService.storage_path = storage_path
|
||||
ApplicationController.wiki = WikiService.instance
|
||||
DispatchServlet.dispatch(OPTIONS)
|
||||
#!/usr/bin/ruby
|
||||
|
||||
require 'webrick'
|
||||
require 'optparse'
|
||||
require 'fileutils'
|
||||
|
||||
pwd = File.expand_path(File.dirname(__FILE__) + "/..")
|
||||
|
||||
OPTIONS = {
|
||||
|
||||
# Overridable options
|
||||
:port => 2500,
|
||||
:ip => '127.0.0.1',
|
||||
:environment => 'production',
|
||||
:server_root => File.expand_path(File.dirname(__FILE__) + '/../public/'),
|
||||
:server_type => WEBrick::SimpleServer,
|
||||
:storage => "#{File.expand_path(FileUtils.pwd)}/storage",
|
||||
}
|
||||
|
||||
ARGV.options do |opts|
|
||||
script_name = File.basename($0)
|
||||
opts.banner = "Usage: ruby #{script_name} [options]"
|
||||
|
||||
opts.separator ''
|
||||
|
||||
opts.on('-p', '--port=port', Integer,
|
||||
'Runs Instiki on the specified port.',
|
||||
'Default: 2500') { |OPTIONS[:port]| }
|
||||
opts.on('-b', '--binding=ip', String,
|
||||
'Binds Rails to the specified ip.',
|
||||
'Default: 127.0.0.1') { |OPTIONS[:ip]| }
|
||||
opts.on('-i', '--index=controller', String,
|
||||
'Specifies an index controller that requests for root will go to (instead of congratulations screen).'
|
||||
) { |OPTIONS[:index_controller]| }
|
||||
opts.on('-e', '--environment=name', String,
|
||||
'Specifies the environment to run this server under (test/development/production).',
|
||||
'Default: production') { |OPTIONS[:environment]| }
|
||||
opts.on('-d', '--daemon',
|
||||
'Make Rails run as a Daemon (only works if fork is available -- meaning on *nix).'
|
||||
) { OPTIONS[:server_type] = WEBrick::Daemon }
|
||||
opts.on('-s', '--simple', '--simple-server',
|
||||
'[deprecated] Forces Instiki not to run as a Daemon if fork is available.',
|
||||
'Since version 0.10.0 this option is ignored.'
|
||||
) { puts "Warning: -s (--simple) option is deprecated. See instiki --help for details." }
|
||||
opts.on('-t', '--storage=storage', String,
|
||||
'Makes Instiki use the specified directory for storage.',
|
||||
'Default: ./storage/[port]') { |OPTIONS[:storage]| }
|
||||
opts.on('-v', '--verbose',
|
||||
'Enable debug-level logging'
|
||||
) { OPTIONS[:verbose] = true }
|
||||
|
||||
opts.separator ''
|
||||
|
||||
opts.on('-h', '--help',
|
||||
'Show this help message.') { puts opts; exit }
|
||||
|
||||
opts.parse!
|
||||
end
|
||||
|
||||
ENV['RAILS_ENV'] = OPTIONS[:environment]
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../config/environment')
|
||||
|
||||
if OPTIONS[:verbose]
|
||||
ActionController::Base.logger.level = Logger::DEBUG
|
||||
end
|
||||
|
||||
OPTIONS[:index_controller] = 'wiki'
|
||||
require 'webrick_server'
|
||||
|
||||
if OPTIONS[:environment] == 'production'
|
||||
storage_path = OPTIONS[:storage] + "/" + OPTIONS[:port].to_s
|
||||
else
|
||||
storage_path = OPTIONS[:storage] + "/" + OPTIONS[:environment] + "/" + OPTIONS[:port].to_s
|
||||
end
|
||||
FileUtils.mkdir_p(storage_path)
|
||||
|
||||
puts "=> Starting Instiki on http://#{OPTIONS[:ip]}:#{OPTIONS[:port]}"
|
||||
puts "=> Data files are stored in #{storage_path}"
|
||||
|
||||
require 'application'
|
||||
WikiService.storage_path = storage_path
|
||||
ApplicationController.wiki = WikiService.instance
|
||||
DispatchServlet.dispatch(OPTIONS)
|
||||
|
|
18
test/all_tests.rb
Normal file → Executable file
18
test/all_tests.rb
Normal file → Executable file
|
@ -1,9 +1,9 @@
|
|||
require 'test_helper'
|
||||
require 'find'
|
||||
|
||||
test_root = File.dirname(__FILE__)
|
||||
Find.find(test_root) { |path|
|
||||
if File.file?(path) and path =~ /.*_test\.rb$/
|
||||
require path[(test_root.size + 1)..-4]
|
||||
end
|
||||
}
|
||||
require 'test_helper'
|
||||
require 'find'
|
||||
|
||||
test_root = File.dirname(__FILE__)
|
||||
Find.find(test_root) { |path|
|
||||
if File.file?(path) and path =~ /.*_test\.rb$/
|
||||
require path[(test_root.size + 1)..-4]
|
||||
end
|
||||
}
|
||||
|
|
0
test/fixtures/rails.gif
vendored
Normal file → Executable file
0
test/fixtures/rails.gif
vendored
Normal file → Executable file
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
52
test/functional/application_test.rb
Normal file → Executable file
52
test/functional/application_test.rb
Normal file → Executable file
|
@ -1,26 +1,26 @@
|
|||
# Unit tests for ApplicationController (the abstract controller class)
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'wiki_controller'
|
||||
require 'rexml/document'
|
||||
|
||||
# Need some concrete class to test the abstract class features
|
||||
class WikiController; def rescue_action(e) logger.error(e); raise e end; end
|
||||
|
||||
class ApplicationTest < Test::Unit::TestCase
|
||||
|
||||
def setup
|
||||
setup_test_wiki
|
||||
setup_controller_test(WikiController)
|
||||
end
|
||||
|
||||
def tear_down
|
||||
tear_down_wiki
|
||||
end
|
||||
|
||||
def test_utf8_header
|
||||
r = process('show', 'web' => 'wiki1', 'id' => 'HomePage')
|
||||
assert_equal 'text/html; charset=UTF-8', r.headers['Content-Type']
|
||||
end
|
||||
|
||||
end
|
||||
# Unit tests for ApplicationController (the abstract controller class)
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'wiki_controller'
|
||||
require 'rexml/document'
|
||||
|
||||
# Need some concrete class to test the abstract class features
|
||||
class WikiController; def rescue_action(e) logger.error(e); raise e end; end
|
||||
|
||||
class ApplicationTest < Test::Unit::TestCase
|
||||
|
||||
def setup
|
||||
setup_test_wiki
|
||||
setup_controller_test(WikiController)
|
||||
end
|
||||
|
||||
def tear_down
|
||||
tear_down_wiki
|
||||
end
|
||||
|
||||
def test_utf8_header
|
||||
r = process('show', 'web' => 'wiki1', 'id' => 'HomePage')
|
||||
assert_equal 'text/html; charset=UTF-8', r.headers['Content-Type']
|
||||
end
|
||||
|
||||
end
|
||||
|
|
254
test/functional/file_controller_test.rb
Normal file → Executable file
254
test/functional/file_controller_test.rb
Normal file → Executable file
|
@ -1,127 +1,127 @@
|
|||
#!/bin/env ruby -w
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'file_controller'
|
||||
require 'fileutils'
|
||||
|
||||
# Raise errors beyond the default web-based presentation
|
||||
class FileController; def rescue_action(e) logger.error(e); raise e end; end
|
||||
|
||||
class FileControllerTest < Test::Unit::TestCase
|
||||
|
||||
FILE_AREA = RAILS_ROOT + '/storage/test/wiki1'
|
||||
FileUtils.mkdir_p(FILE_AREA) unless File.directory?(FILE_AREA)
|
||||
FileUtils.rm(Dir["#{FILE_AREA}/*"])
|
||||
|
||||
def setup
|
||||
setup_test_wiki
|
||||
setup_controller_test
|
||||
end
|
||||
|
||||
def tear_down
|
||||
tear_down_wiki
|
||||
end
|
||||
|
||||
def test_file
|
||||
process 'file', 'web' => 'wiki1', 'id' => 'foo.tgz'
|
||||
|
||||
assert_success
|
||||
assert_rendered_file 'file/file'
|
||||
end
|
||||
|
||||
def test_file_download_text_file
|
||||
File.open("#{FILE_AREA}/foo.txt", 'wb') { |f| f.write "aaa\nbbb\n" }
|
||||
|
||||
r = process 'file', 'web' => 'wiki1', 'id' => 'foo.txt'
|
||||
|
||||
assert_success
|
||||
assert_equal "aaa\nbbb\n", r.binary_content
|
||||
assert_equal 'text/plain', r.headers['Content-Type']
|
||||
end
|
||||
|
||||
def test_file_download_pdf_file
|
||||
File.open("#{FILE_AREA}/foo.pdf", 'wb') { |f| f.write "aaa\nbbb\n" }
|
||||
|
||||
r = process 'file', 'web' => 'wiki1', 'id' => 'foo.pdf'
|
||||
|
||||
assert_success
|
||||
assert_equal "aaa\nbbb\n", r.binary_content
|
||||
assert_equal 'application/pdf', r.headers['Content-Type']
|
||||
end
|
||||
|
||||
def test_pic_download_gif
|
||||
FileUtils.cp("#{RAILS_ROOT}/test/fixtures/rails.gif", FILE_AREA)
|
||||
|
||||
r = process 'pic', 'web' => 'wiki1', 'id' => 'rails.gif'
|
||||
|
||||
assert_success
|
||||
assert_equal File.size("#{FILE_AREA}/rails.gif"), r.binary_content.size
|
||||
end
|
||||
|
||||
def test_pic_unknown_pic
|
||||
r = process 'pic', 'web' => 'wiki1', 'id' => 'non-existant.gif'
|
||||
|
||||
assert_success
|
||||
assert_rendered_file 'file/file'
|
||||
end
|
||||
|
||||
def test_pic_upload_end_to_end
|
||||
# edit and re-render home page so that it has an "unknown file" link to 'rails-e2e.gif'
|
||||
@wiki.revise_page('wiki1', 'HomePage', '[[rails-e2e.gif:pic]]', Time.now, 'AnonymousBrave')
|
||||
assert_equal "<p><span class=\"newWikiWord\">rails-e2e.gif<a href=\"../pic/rails-e2e.gif\">" +
|
||||
"?</a></span></p>",
|
||||
@home.display_content
|
||||
|
||||
# rails-e2e.gif is unknown to the system, so pic action goes to the file [upload] form
|
||||
r = process 'pic', 'web' => 'wiki1', 'id' => 'rails-e2e.gif'
|
||||
assert_success
|
||||
assert_rendered_file 'file/file'
|
||||
|
||||
# User uploads the picture
|
||||
picture = File.read("#{RAILS_ROOT}/test/fixtures/rails.gif")
|
||||
r = process 'pic', 'web' => 'wiki1', 'id' => 'rails-e2e.gif', 'file' => StringIO.new(picture)
|
||||
assert_redirect_url '/'
|
||||
assert @wiki.file_yard(@web).has_file?('rails-e2e.gif')
|
||||
assert_equal(picture, File.read("#{RAILS_ROOT}/storage/test/wiki1/rails-e2e.gif"))
|
||||
|
||||
# this should refresh the page display content (cached)
|
||||
assert_equal "<p><img alt=\"rails-e2e.gif\" src=\"../pic/rails-e2e.gif\" /></p>",
|
||||
@home.display_content
|
||||
end
|
||||
|
||||
def test_pic_upload_end_to_end
|
||||
# edit and re-render home page so that it has an "unknown file" link to 'rails-e2e.gif'
|
||||
@wiki.revise_page('wiki1', 'HomePage', '[[instiki-e2e.txt:file]]', Time.now, 'AnonymousBrave')
|
||||
assert_equal "<p><span class=\"newWikiWord\">instiki-e2e.txt" +
|
||||
"<a href=\"../file/instiki-e2e.txt\">?</a></span></p>",
|
||||
@home.display_content
|
||||
|
||||
# rails-e2e.gif is unknown to the system, so pic action goes to the file [upload] form
|
||||
r = process 'file', 'web' => 'wiki1', 'id' => 'instiki-e2e.txt'
|
||||
assert_success
|
||||
assert_rendered_file 'file/file'
|
||||
|
||||
# User uploads the picture
|
||||
file = "abcdefgh\n123"
|
||||
r = process 'file', 'web' => 'wiki1', 'id' => 'instiki-e2e.txt', 'file' => StringIO.new(file)
|
||||
assert_redirect_url '/'
|
||||
assert @wiki.file_yard(@web).has_file?('instiki-e2e.txt')
|
||||
assert_equal(file, File.read("#{RAILS_ROOT}/storage/test/wiki1/instiki-e2e.txt"))
|
||||
|
||||
# this should refresh the page display content (cached)
|
||||
assert_equal "<p><a class=\"existingWikiWord\" href=\"../file/instiki-e2e.txt\">" +
|
||||
"instiki-e2e.txt</a></p>",
|
||||
@home.display_content
|
||||
end
|
||||
|
||||
def test_uploads_blocking
|
||||
@web.allow_uploads = true
|
||||
r = process 'file', 'web' => 'wiki1', 'id' => 'filename'
|
||||
assert_success
|
||||
|
||||
@web.allow_uploads = false
|
||||
r = process 'file', 'web' => 'wiki1', 'id' => 'filename'
|
||||
assert_equal '403 Forbidden', r.headers['Status']
|
||||
end
|
||||
|
||||
end
|
||||
#!/bin/env ruby -w
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'file_controller'
|
||||
require 'fileutils'
|
||||
|
||||
# Raise errors beyond the default web-based presentation
|
||||
class FileController; def rescue_action(e) logger.error(e); raise e end; end
|
||||
|
||||
class FileControllerTest < Test::Unit::TestCase
|
||||
|
||||
FILE_AREA = RAILS_ROOT + '/storage/test/wiki1'
|
||||
FileUtils.mkdir_p(FILE_AREA) unless File.directory?(FILE_AREA)
|
||||
FileUtils.rm(Dir["#{FILE_AREA}/*"])
|
||||
|
||||
def setup
|
||||
setup_test_wiki
|
||||
setup_controller_test
|
||||
end
|
||||
|
||||
def tear_down
|
||||
tear_down_wiki
|
||||
end
|
||||
|
||||
def test_file
|
||||
process 'file', 'web' => 'wiki1', 'id' => 'foo.tgz'
|
||||
|
||||
assert_success
|
||||
assert_rendered_file 'file/file'
|
||||
end
|
||||
|
||||
def test_file_download_text_file
|
||||
File.open("#{FILE_AREA}/foo.txt", 'wb') { |f| f.write "aaa\nbbb\n" }
|
||||
|
||||
r = process 'file', 'web' => 'wiki1', 'id' => 'foo.txt'
|
||||
|
||||
assert_success
|
||||
assert_equal "aaa\nbbb\n", r.binary_content
|
||||
assert_equal 'text/plain', r.headers['Content-Type']
|
||||
end
|
||||
|
||||
def test_file_download_pdf_file
|
||||
File.open("#{FILE_AREA}/foo.pdf", 'wb') { |f| f.write "aaa\nbbb\n" }
|
||||
|
||||
r = process 'file', 'web' => 'wiki1', 'id' => 'foo.pdf'
|
||||
|
||||
assert_success
|
||||
assert_equal "aaa\nbbb\n", r.binary_content
|
||||
assert_equal 'application/pdf', r.headers['Content-Type']
|
||||
end
|
||||
|
||||
def test_pic_download_gif
|
||||
FileUtils.cp("#{RAILS_ROOT}/test/fixtures/rails.gif", FILE_AREA)
|
||||
|
||||
r = process 'pic', 'web' => 'wiki1', 'id' => 'rails.gif'
|
||||
|
||||
assert_success
|
||||
assert_equal File.size("#{FILE_AREA}/rails.gif"), r.binary_content.size
|
||||
end
|
||||
|
||||
def test_pic_unknown_pic
|
||||
r = process 'pic', 'web' => 'wiki1', 'id' => 'non-existant.gif'
|
||||
|
||||
assert_success
|
||||
assert_rendered_file 'file/file'
|
||||
end
|
||||
|
||||
def test_pic_upload_end_to_end
|
||||
# edit and re-render home page so that it has an "unknown file" link to 'rails-e2e.gif'
|
||||
@wiki.revise_page('wiki1', 'HomePage', '[[rails-e2e.gif:pic]]', Time.now, 'AnonymousBrave')
|
||||
assert_equal "<p><span class=\"newWikiWord\">rails-e2e.gif<a href=\"../pic/rails-e2e.gif\">" +
|
||||
"?</a></span></p>",
|
||||
@home.display_content
|
||||
|
||||
# rails-e2e.gif is unknown to the system, so pic action goes to the file [upload] form
|
||||
r = process 'pic', 'web' => 'wiki1', 'id' => 'rails-e2e.gif'
|
||||
assert_success
|
||||
assert_rendered_file 'file/file'
|
||||
|
||||
# User uploads the picture
|
||||
picture = File.read("#{RAILS_ROOT}/test/fixtures/rails.gif")
|
||||
r = process 'pic', 'web' => 'wiki1', 'id' => 'rails-e2e.gif', 'file' => StringIO.new(picture)
|
||||
assert_redirect_url '/'
|
||||
assert @wiki.file_yard(@web).has_file?('rails-e2e.gif')
|
||||
assert_equal(picture, File.read("#{RAILS_ROOT}/storage/test/wiki1/rails-e2e.gif"))
|
||||
|
||||
# this should refresh the page display content (cached)
|
||||
assert_equal "<p><img alt=\"rails-e2e.gif\" src=\"../pic/rails-e2e.gif\" /></p>",
|
||||
@home.display_content
|
||||
end
|
||||
|
||||
def test_pic_upload_end_to_end
|
||||
# edit and re-render home page so that it has an "unknown file" link to 'rails-e2e.gif'
|
||||
@wiki.revise_page('wiki1', 'HomePage', '[[instiki-e2e.txt:file]]', Time.now, 'AnonymousBrave')
|
||||
assert_equal "<p><span class=\"newWikiWord\">instiki-e2e.txt" +
|
||||
"<a href=\"../file/instiki-e2e.txt\">?</a></span></p>",
|
||||
@home.display_content
|
||||
|
||||
# rails-e2e.gif is unknown to the system, so pic action goes to the file [upload] form
|
||||
r = process 'file', 'web' => 'wiki1', 'id' => 'instiki-e2e.txt'
|
||||
assert_success
|
||||
assert_rendered_file 'file/file'
|
||||
|
||||
# User uploads the picture
|
||||
file = "abcdefgh\n123"
|
||||
r = process 'file', 'web' => 'wiki1', 'id' => 'instiki-e2e.txt', 'file' => StringIO.new(file)
|
||||
assert_redirect_url '/'
|
||||
assert @wiki.file_yard(@web).has_file?('instiki-e2e.txt')
|
||||
assert_equal(file, File.read("#{RAILS_ROOT}/storage/test/wiki1/instiki-e2e.txt"))
|
||||
|
||||
# this should refresh the page display content (cached)
|
||||
assert_equal "<p><a class=\"existingWikiWord\" href=\"../file/instiki-e2e.txt\">" +
|
||||
"instiki-e2e.txt</a></p>",
|
||||
@home.display_content
|
||||
end
|
||||
|
||||
def test_uploads_blocking
|
||||
@web.allow_uploads = true
|
||||
r = process 'file', 'web' => 'wiki1', 'id' => 'filename'
|
||||
assert_success
|
||||
|
||||
@web.allow_uploads = false
|
||||
r = process 'file', 'web' => 'wiki1', 'id' => 'filename'
|
||||
assert_equal '403 Forbidden', r.headers['Status']
|
||||
end
|
||||
|
||||
end
|
||||
|
|
File diff suppressed because it is too large
Load diff
226
test/test_helper.rb
Executable file → Normal file
226
test/test_helper.rb
Executable file → Normal file
|
@ -1,113 +1,113 @@
|
|||
ENV['RAILS_ENV'] ||= 'test'
|
||||
require File.dirname(__FILE__) + '/../config/environment'
|
||||
require 'application'
|
||||
require 'test/unit'
|
||||
require 'action_controller/test_process'
|
||||
|
||||
# Uncomment this variable to have assert_success check that response bodies are valid XML
|
||||
$validate_xml_in_assert_success = true
|
||||
|
||||
# Convenient setup method for Test::Unit::TestCase
|
||||
class Test::Unit::TestCase
|
||||
|
||||
private
|
||||
|
||||
def setup_controller_test(controller_class = nil, host = nil)
|
||||
if controller_class
|
||||
@controller = controller_class
|
||||
elsif self.class.to_s =~ /^(\w+Controller)Test$/
|
||||
@controller = Object::const_get($1)
|
||||
else
|
||||
raise "Cannot derive the name of controller under test from class name #{self.class}"
|
||||
end
|
||||
@request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
|
||||
@request.host = host || 'localhost'
|
||||
return @request, @response
|
||||
end
|
||||
|
||||
# Wiki fixture for tests
|
||||
|
||||
def setup_test_wiki
|
||||
@wiki = ApplicationController.wiki = WikiServiceWithNoPersistence.new
|
||||
@web = @wiki.create_web('Test Wiki 1', 'wiki1')
|
||||
@home = @wiki.write_page('wiki1', 'HomePage', 'First revision of the HomePage end', Time.now,
|
||||
Author.new('AnAuthor', '127.0.0.1'))
|
||||
end
|
||||
|
||||
def setup_wiki_with_three_pages
|
||||
@oak = @wiki.write_page('wiki1', 'Oak',
|
||||
"All about oak.\n" +
|
||||
"category: trees",
|
||||
5.minutes.ago, Author.new('TreeHugger', '127.0.0.2'))
|
||||
@elephant = @wiki.write_page('wiki1', 'Elephant',
|
||||
"All about elephants.\n" +
|
||||
"category: animals",
|
||||
10.minutes.ago, Author.new('Guest', '127.0.0.2'))
|
||||
end
|
||||
|
||||
def tear_down_wiki
|
||||
ApplicationController.wiki = nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class WikiServiceWithNoPersistence
|
||||
include AbstractWikiService
|
||||
def initialize
|
||||
init_wiki_service
|
||||
end
|
||||
|
||||
def storage_path
|
||||
RAILS_ROOT + '/storage/test/'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# This module is to be included in unit tests that involve matching chunks.
|
||||
# It provides a easy way to test whether a chunk matches a particular string
|
||||
# and any the values of any fields that should be set after a match.
|
||||
class ContentStub < String
|
||||
attr_reader :chunks, :content
|
||||
def initialize(str)
|
||||
super
|
||||
@chunks = []
|
||||
end
|
||||
end
|
||||
|
||||
module ChunkMatch
|
||||
|
||||
# Asserts a number of tests for the given type and text.
|
||||
def match(chunk_type, test_text, expected_chunk_state)
|
||||
if chunk_type.respond_to? :pattern
|
||||
assert_match(chunk_type.pattern, test_text)
|
||||
end
|
||||
|
||||
content = ContentStub.new(test_text)
|
||||
chunk_type.apply_to(content)
|
||||
|
||||
# Test if requested parts are correct.
|
||||
expected_chunk_state.each_pair do |a_method, expected_value|
|
||||
assert content.chunks.last.kind_of?(chunk_type)
|
||||
assert_respond_to(content.chunks.last, a_method)
|
||||
assert_equal(expected_value, content.chunks.last.send(a_method.to_sym),
|
||||
"Wrong #{a_method} value")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if defined? $validate_xml_in_assert_success and $validate_xml_in_assert_success == true
|
||||
module Test
|
||||
module Unit
|
||||
module Assertions
|
||||
unless method_defined? :__assert_success_before_ovverride_by_instiki
|
||||
alias :__assert_success_before_ovverride_by_instiki :assert_success
|
||||
end
|
||||
def assert_success
|
||||
__assert_success_before_ovverride_by_instiki
|
||||
if @response.body.kind_of?(Proc) then # it's a file download, not an HTML content
|
||||
else assert_nothing_raised(@response.body) { REXML::Document.new(@response.body) } end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
ENV['RAILS_ENV'] ||= 'test'
|
||||
require File.dirname(__FILE__) + '/../config/environment'
|
||||
require 'application'
|
||||
require 'test/unit'
|
||||
require 'action_controller/test_process'
|
||||
|
||||
# Uncomment this variable to have assert_success check that response bodies are valid XML
|
||||
$validate_xml_in_assert_success = true
|
||||
|
||||
# Convenient setup method for Test::Unit::TestCase
|
||||
class Test::Unit::TestCase
|
||||
|
||||
private
|
||||
|
||||
def setup_controller_test(controller_class = nil, host = nil)
|
||||
if controller_class
|
||||
@controller = controller_class
|
||||
elsif self.class.to_s =~ /^(\w+Controller)Test$/
|
||||
@controller = Object::const_get($1)
|
||||
else
|
||||
raise "Cannot derive the name of controller under test from class name #{self.class}"
|
||||
end
|
||||
@request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
|
||||
@request.host = host || 'localhost'
|
||||
return @request, @response
|
||||
end
|
||||
|
||||
# Wiki fixture for tests
|
||||
|
||||
def setup_test_wiki
|
||||
@wiki = ApplicationController.wiki = WikiServiceWithNoPersistence.new
|
||||
@web = @wiki.create_web('Test Wiki 1', 'wiki1')
|
||||
@home = @wiki.write_page('wiki1', 'HomePage', 'First revision of the HomePage end', Time.now,
|
||||
Author.new('AnAuthor', '127.0.0.1'))
|
||||
end
|
||||
|
||||
def setup_wiki_with_three_pages
|
||||
@oak = @wiki.write_page('wiki1', 'Oak',
|
||||
"All about oak.\n" +
|
||||
"category: trees",
|
||||
5.minutes.ago, Author.new('TreeHugger', '127.0.0.2'))
|
||||
@elephant = @wiki.write_page('wiki1', 'Elephant',
|
||||
"All about elephants.\n" +
|
||||
"category: animals",
|
||||
10.minutes.ago, Author.new('Guest', '127.0.0.2'))
|
||||
end
|
||||
|
||||
def tear_down_wiki
|
||||
ApplicationController.wiki = nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class WikiServiceWithNoPersistence
|
||||
include AbstractWikiService
|
||||
def initialize
|
||||
init_wiki_service
|
||||
end
|
||||
|
||||
def storage_path
|
||||
RAILS_ROOT + '/storage/test/'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# This module is to be included in unit tests that involve matching chunks.
|
||||
# It provides a easy way to test whether a chunk matches a particular string
|
||||
# and any the values of any fields that should be set after a match.
|
||||
class ContentStub < String
|
||||
attr_reader :chunks, :content
|
||||
def initialize(str)
|
||||
super
|
||||
@chunks = []
|
||||
end
|
||||
end
|
||||
|
||||
module ChunkMatch
|
||||
|
||||
# Asserts a number of tests for the given type and text.
|
||||
def match(chunk_type, test_text, expected_chunk_state)
|
||||
if chunk_type.respond_to? :pattern
|
||||
assert_match(chunk_type.pattern, test_text)
|
||||
end
|
||||
|
||||
content = ContentStub.new(test_text)
|
||||
chunk_type.apply_to(content)
|
||||
|
||||
# Test if requested parts are correct.
|
||||
expected_chunk_state.each_pair do |a_method, expected_value|
|
||||
assert content.chunks.last.kind_of?(chunk_type)
|
||||
assert_respond_to(content.chunks.last, a_method)
|
||||
assert_equal(expected_value, content.chunks.last.send(a_method.to_sym),
|
||||
"Wrong #{a_method} value")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if defined? $validate_xml_in_assert_success and $validate_xml_in_assert_success == true
|
||||
module Test
|
||||
module Unit
|
||||
module Assertions
|
||||
unless method_defined? :__assert_success_before_ovverride_by_instiki
|
||||
alias :__assert_success_before_ovverride_by_instiki :assert_success
|
||||
end
|
||||
def assert_success
|
||||
__assert_success_before_ovverride_by_instiki
|
||||
if @response.body.kind_of?(Proc) then # it's a file download, not an HTML content
|
||||
else assert_nothing_raised(@response.body) { REXML::Document.new(@response.body) } end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
#!/bin/env ruby
|
||||
|
||||
require File.dirname(__FILE__) + '/../../test_helper'
|
||||
require 'chunks/category'
|
||||
require 'chunks/match'
|
||||
|
||||
class CategoryTest < Test::Unit::TestCase
|
||||
include ChunkMatch
|
||||
|
||||
def test_single_category
|
||||
match(Category, 'category: test', :list => ['test'], :hidden => nil)
|
||||
match(Category, 'category : chunk test ', :list => ['chunk test'], :hidden => nil)
|
||||
match(Category, ':category: test', :list => ['test'], :hidden => ':')
|
||||
end
|
||||
|
||||
def test_multiple_categories
|
||||
match(Category, 'category: test, multiple', :list => ['test', 'multiple'], :hidden => nil)
|
||||
match(Category, 'category : chunk test , multi category,regression test case ',
|
||||
:list => ['chunk test','multi category','regression test case'], :hidden => nil
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
#!/bin/env ruby
|
||||
|
||||
require File.dirname(__FILE__) + '/../../test_helper'
|
||||
require 'chunks/category'
|
||||
require 'chunks/match'
|
||||
|
||||
class CategoryTest < Test::Unit::TestCase
|
||||
include ChunkMatch
|
||||
|
||||
def test_single_category
|
||||
match(Category, 'category: test', :list => ['test'], :hidden => nil)
|
||||
match(Category, 'category : chunk test ', :list => ['chunk test'], :hidden => nil)
|
||||
match(Category, ':category: test', :list => ['test'], :hidden => ':')
|
||||
end
|
||||
|
||||
def test_multiple_categories
|
||||
match(Category, 'category: test, multiple', :list => ['test', 'multiple'], :hidden => nil)
|
||||
match(Category, 'category : chunk test , multi category,regression test case ',
|
||||
:list => ['chunk test','multi category','regression test case'], :hidden => nil
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
#!/bin/env ruby
|
||||
|
||||
require File.dirname(__FILE__) + '/../../test_helper'
|
||||
require 'chunks/nowiki'
|
||||
require 'chunks/match'
|
||||
|
||||
class NoWikiTest < Test::Unit::TestCase
|
||||
include ChunkMatch
|
||||
|
||||
def test_simple_nowiki
|
||||
match(NoWiki, 'This sentence contains <nowiki>[[raw text]]</nowiki>. Do not touch!',
|
||||
:plain_text => '[[raw text]]'
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
#!/bin/env ruby
|
||||
|
||||
require File.dirname(__FILE__) + '/../../test_helper'
|
||||
require 'chunks/nowiki'
|
||||
require 'chunks/match'
|
||||
|
||||
class NoWikiTest < Test::Unit::TestCase
|
||||
include ChunkMatch
|
||||
|
||||
def test_simple_nowiki
|
||||
match(NoWiki, 'This sentence contains <nowiki>[[raw text]]</nowiki>. Do not touch!',
|
||||
:plain_text => '[[raw text]]'
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,81 +1,81 @@
|
|||
#!/bin/env ruby
|
||||
|
||||
require File.dirname(__FILE__) + '/../../test_helper'
|
||||
require 'chunks/wiki'
|
||||
|
||||
class WikiTest < Test::Unit::TestCase
|
||||
|
||||
class ContentStub < String
|
||||
def chunks
|
||||
@chunks ||= []
|
||||
end
|
||||
end
|
||||
|
||||
include ChunkMatch
|
||||
|
||||
def test_simple
|
||||
match(WikiChunk::Word, 'This is a WikiWord okay?', :page_name => 'WikiWord')
|
||||
end
|
||||
|
||||
def test_escaped
|
||||
match(WikiChunk::Word, 'Do not link to an \EscapedWord',
|
||||
:page_name => 'EscapedWord', :escaped_text => 'EscapedWord'
|
||||
)
|
||||
end
|
||||
|
||||
def test_simple_brackets
|
||||
match(WikiChunk::Link, 'This is a [[bracketted link]]',
|
||||
:page_name => 'bracketted link', :escaped_text => nil
|
||||
)
|
||||
end
|
||||
|
||||
def test_complex_brackets
|
||||
match(WikiChunk::Link, 'This is a tricky link [[Sperberg-McQueen]]',
|
||||
:page_name => 'Sperberg-McQueen', :escaped_text => nil
|
||||
)
|
||||
end
|
||||
|
||||
def test_textile_link
|
||||
textile_link = ContentStub.new('"Here is a special link":SpecialLink')
|
||||
WikiChunk::Word.apply_to(textile_link)
|
||||
assert_equal '"Here is a special link":SpecialLink', textile_link
|
||||
assert textile_link.chunks.empty?
|
||||
end
|
||||
|
||||
def test_file_types
|
||||
# only link
|
||||
assert_link_parsed_as 'only text', 'only text', 'show', '[[only text]]'
|
||||
# link and text
|
||||
assert_link_parsed_as 'page name', 'link text', 'show', '[[page name|link text]]'
|
||||
# link and type (file)
|
||||
assert_link_parsed_as 'foo.tar.gz', 'foo.tar.gz', 'file', '[[foo.tar.gz:file]]'
|
||||
# link and type (pic)
|
||||
assert_link_parsed_as 'foo.tar.gz', 'foo.tar.gz', 'pic', '[[foo.tar.gz:pic]]'
|
||||
# link, text and type
|
||||
assert_link_parsed_as 'foo.tar.gz', 'FooTar', 'file', '[[foo.tar.gz|FooTar:file]]'
|
||||
|
||||
# NEGATIVE TEST CASES
|
||||
|
||||
# empty page name
|
||||
assert_link_parsed_as '|link text?', '|link text?', 'file', '[[|link text?:file]]'
|
||||
# empty link text
|
||||
assert_link_parsed_as 'page name?|', 'page name?|', 'file', '[[page name?|:file]]'
|
||||
# empty link type
|
||||
assert_link_parsed_as 'page name', 'link?:', 'show', '[[page name|link?:]]'
|
||||
# unknown link type
|
||||
assert_link_parsed_as 'page name:create_system', 'page name:create_system', 'show',
|
||||
'[[page name:create_system]]'
|
||||
end
|
||||
|
||||
def assert_link_parsed_as(expected_page_name, expected_link_text, expected_link_type, link)
|
||||
link_to_file = ContentStub.new(link)
|
||||
WikiChunk::Link.apply_to(link_to_file)
|
||||
chunk = link_to_file.chunks.last
|
||||
assert chunk
|
||||
assert_equal expected_page_name, chunk.page_name
|
||||
assert_equal expected_link_text, chunk.link_text
|
||||
assert_equal expected_link_type, chunk.link_type
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#!/bin/env ruby
|
||||
|
||||
require File.dirname(__FILE__) + '/../../test_helper'
|
||||
require 'chunks/wiki'
|
||||
|
||||
class WikiTest < Test::Unit::TestCase
|
||||
|
||||
class ContentStub < String
|
||||
def chunks
|
||||
@chunks ||= []
|
||||
end
|
||||
end
|
||||
|
||||
include ChunkMatch
|
||||
|
||||
def test_simple
|
||||
match(WikiChunk::Word, 'This is a WikiWord okay?', :page_name => 'WikiWord')
|
||||
end
|
||||
|
||||
def test_escaped
|
||||
match(WikiChunk::Word, 'Do not link to an \EscapedWord',
|
||||
:page_name => 'EscapedWord', :escaped_text => 'EscapedWord'
|
||||
)
|
||||
end
|
||||
|
||||
def test_simple_brackets
|
||||
match(WikiChunk::Link, 'This is a [[bracketted link]]',
|
||||
:page_name => 'bracketted link', :escaped_text => nil
|
||||
)
|
||||
end
|
||||
|
||||
def test_complex_brackets
|
||||
match(WikiChunk::Link, 'This is a tricky link [[Sperberg-McQueen]]',
|
||||
:page_name => 'Sperberg-McQueen', :escaped_text => nil
|
||||
)
|
||||
end
|
||||
|
||||
def test_textile_link
|
||||
textile_link = ContentStub.new('"Here is a special link":SpecialLink')
|
||||
WikiChunk::Word.apply_to(textile_link)
|
||||
assert_equal '"Here is a special link":SpecialLink', textile_link
|
||||
assert textile_link.chunks.empty?
|
||||
end
|
||||
|
||||
def test_file_types
|
||||
# only link
|
||||
assert_link_parsed_as 'only text', 'only text', 'show', '[[only text]]'
|
||||
# link and text
|
||||
assert_link_parsed_as 'page name', 'link text', 'show', '[[page name|link text]]'
|
||||
# link and type (file)
|
||||
assert_link_parsed_as 'foo.tar.gz', 'foo.tar.gz', 'file', '[[foo.tar.gz:file]]'
|
||||
# link and type (pic)
|
||||
assert_link_parsed_as 'foo.tar.gz', 'foo.tar.gz', 'pic', '[[foo.tar.gz:pic]]'
|
||||
# link, text and type
|
||||
assert_link_parsed_as 'foo.tar.gz', 'FooTar', 'file', '[[foo.tar.gz|FooTar:file]]'
|
||||
|
||||
# NEGATIVE TEST CASES
|
||||
|
||||
# empty page name
|
||||
assert_link_parsed_as '|link text?', '|link text?', 'file', '[[|link text?:file]]'
|
||||
# empty link text
|
||||
assert_link_parsed_as 'page name?|', 'page name?|', 'file', '[[page name?|:file]]'
|
||||
# empty link type
|
||||
assert_link_parsed_as 'page name', 'link?:', 'show', '[[page name|link?:]]'
|
||||
# unknown link type
|
||||
assert_link_parsed_as 'page name:create_system', 'page name:create_system', 'show',
|
||||
'[[page name:create_system]]'
|
||||
end
|
||||
|
||||
def assert_link_parsed_as(expected_page_name, expected_link_text, expected_link_type, link)
|
||||
link_to_file = ContentStub.new(link)
|
||||
WikiChunk::Link.apply_to(link_to_file)
|
||||
chunk = link_to_file.chunks.last
|
||||
assert chunk
|
||||
assert_equal expected_page_name, chunk.page_name
|
||||
assert_equal expected_link_text, chunk.link_text
|
||||
assert_equal expected_link_type, chunk.link_type
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -1,82 +1,82 @@
|
|||
#!/bin/env ruby -w
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'diff'
|
||||
|
||||
include Diff
|
||||
|
||||
class DiffTest < Test::Unit::TestCase
|
||||
def test_init
|
||||
assert(1 == 1, "tests working")
|
||||
assert_nothing_raised("object created") do
|
||||
s = SequenceMatcher.new "private Thread currentThread;",
|
||||
"private volatile Thread currentThread;",
|
||||
proc { |x| x == ' ' }
|
||||
end
|
||||
end
|
||||
|
||||
def test_matching_blocks
|
||||
s = SequenceMatcher.new "abxcd", "abcd"
|
||||
assert(s.get_matching_blocks == [[0, 0, 2], [3, 2, 2], [5, 4, 0]],
|
||||
"get_matching_blocks works")
|
||||
end
|
||||
|
||||
def test_ratio
|
||||
s = SequenceMatcher.new "abcd", "bcde"
|
||||
assert(s.ratio == 0.75, "ratio works")
|
||||
assert(s.quick_ratio == 0.75, "quick_ratio works")
|
||||
assert(s.real_quick_ratio == 1.0, "real_quick_ratio works")
|
||||
end
|
||||
|
||||
def test_longest_match
|
||||
s = SequenceMatcher.new(" abcd", "abcd abcd")
|
||||
assert(s.find_longest_match(0, 5, 0, 9) == [0, 4, 5],
|
||||
"find_longest_match works")
|
||||
s = SequenceMatcher.new()
|
||||
end
|
||||
|
||||
def test_opcodes
|
||||
s = SequenceMatcher.new("qabxcd", "abycdf")
|
||||
assert(s.get_opcodes == [
|
||||
[:delete, 0, 1, 0, 0],
|
||||
[:equal, 1, 3, 0, 2],
|
||||
[:replace, 3, 4, 2, 3],
|
||||
[:equal, 4, 6, 3, 5],
|
||||
[:insert, 6, 6, 5, 6]], "get_opcodes works")
|
||||
end
|
||||
|
||||
|
||||
def test_count_leading
|
||||
assert(Diff.count_leading(' abc', ' ') == 3,
|
||||
"count_leading works")
|
||||
end
|
||||
|
||||
def test_html2list
|
||||
a = "here is the original text"
|
||||
#p HTMLDiff.html2list(a)
|
||||
end
|
||||
|
||||
def test_html_diff
|
||||
a = "this was the original string"
|
||||
b = "this is the super string"
|
||||
assert_equal 'this <del class="diffmod">was </del>' +
|
||||
'<ins class="diffmod">is </ins>the ' +
|
||||
'<del class="diffmod">original </del>' +
|
||||
'<ins class="diffmod">super </ins>string',
|
||||
HTMLDiff.diff(a, b)
|
||||
end
|
||||
|
||||
def test_html_diff_with_multiple_paragraphs
|
||||
a = "<p>this was the original string</p>"
|
||||
b = "<p>this is</p>\r\n<p>the super string</p>\r\n<p>around the world</p>"
|
||||
|
||||
assert_equal(
|
||||
"<p>this <del class=\"diffmod\">was </del>" +
|
||||
"<ins class=\"diffmod\">is</ins></p>\r\n<p>the " +
|
||||
"<del class=\"diffmod\">original </del>" +
|
||||
"<ins class=\"diffmod\">super </ins>string</p>\r\n" +
|
||||
"<p><ins class=\"diffins\">around the world</ins></p>",
|
||||
HTMLDiff.diff(a, b)
|
||||
)
|
||||
end
|
||||
#!/bin/env ruby -w
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'diff'
|
||||
|
||||
include Diff
|
||||
|
||||
class DiffTest < Test::Unit::TestCase
|
||||
def test_init
|
||||
assert(1 == 1, "tests working")
|
||||
assert_nothing_raised("object created") do
|
||||
s = SequenceMatcher.new "private Thread currentThread;",
|
||||
"private volatile Thread currentThread;",
|
||||
proc { |x| x == ' ' }
|
||||
end
|
||||
end
|
||||
|
||||
def test_matching_blocks
|
||||
s = SequenceMatcher.new "abxcd", "abcd"
|
||||
assert(s.get_matching_blocks == [[0, 0, 2], [3, 2, 2], [5, 4, 0]],
|
||||
"get_matching_blocks works")
|
||||
end
|
||||
|
||||
def test_ratio
|
||||
s = SequenceMatcher.new "abcd", "bcde"
|
||||
assert(s.ratio == 0.75, "ratio works")
|
||||
assert(s.quick_ratio == 0.75, "quick_ratio works")
|
||||
assert(s.real_quick_ratio == 1.0, "real_quick_ratio works")
|
||||
end
|
||||
|
||||
def test_longest_match
|
||||
s = SequenceMatcher.new(" abcd", "abcd abcd")
|
||||
assert(s.find_longest_match(0, 5, 0, 9) == [0, 4, 5],
|
||||
"find_longest_match works")
|
||||
s = SequenceMatcher.new()
|
||||
end
|
||||
|
||||
def test_opcodes
|
||||
s = SequenceMatcher.new("qabxcd", "abycdf")
|
||||
assert(s.get_opcodes == [
|
||||
[:delete, 0, 1, 0, 0],
|
||||
[:equal, 1, 3, 0, 2],
|
||||
[:replace, 3, 4, 2, 3],
|
||||
[:equal, 4, 6, 3, 5],
|
||||
[:insert, 6, 6, 5, 6]], "get_opcodes works")
|
||||
end
|
||||
|
||||
|
||||
def test_count_leading
|
||||
assert(Diff.count_leading(' abc', ' ') == 3,
|
||||
"count_leading works")
|
||||
end
|
||||
|
||||
def test_html2list
|
||||
a = "here is the original text"
|
||||
#p HTMLDiff.html2list(a)
|
||||
end
|
||||
|
||||
def test_html_diff
|
||||
a = "this was the original string"
|
||||
b = "this is the super string"
|
||||
assert_equal 'this <del class="diffmod">was </del>' +
|
||||
'<ins class="diffmod">is </ins>the ' +
|
||||
'<del class="diffmod">original </del>' +
|
||||
'<ins class="diffmod">super </ins>string',
|
||||
HTMLDiff.diff(a, b)
|
||||
end
|
||||
|
||||
def test_html_diff_with_multiple_paragraphs
|
||||
a = "<p>this was the original string</p>"
|
||||
b = "<p>this is</p>\r\n<p>the super string</p>\r\n<p>around the world</p>"
|
||||
|
||||
assert_equal(
|
||||
"<p>this <del class=\"diffmod\">was </del>" +
|
||||
"<ins class=\"diffmod\">is</ins></p>\r\n<p>the " +
|
||||
"<del class=\"diffmod\">original </del>" +
|
||||
"<ins class=\"diffmod\">super </ins>string</p>\r\n" +
|
||||
"<p><ins class=\"diffins\">around the world</ins></p>",
|
||||
HTMLDiff.diff(a, b)
|
||||
)
|
||||
end
|
||||
end
|
68
test/unit/file_yard_test.rb
Normal file → Executable file
68
test/unit/file_yard_test.rb
Normal file → Executable file
|
@ -1,35 +1,35 @@
|
|||
#!/bin/env ruby -w
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'fileutils'
|
||||
require 'file_yard'
|
||||
require 'stringio'
|
||||
|
||||
class FileYardTest < Test::Unit::TestCase
|
||||
|
||||
def setup
|
||||
FileUtils.mkdir_p(file_path)
|
||||
FileUtils.rm(Dir["#{file_path}/*"])
|
||||
@yard = FileYard.new(file_path)
|
||||
end
|
||||
|
||||
def test_files
|
||||
assert_equal [], @yard.files
|
||||
|
||||
# FileYard gets the list of files from directory in the constructor
|
||||
@yard.upload_file('aaa', StringIO.new('file contents'))
|
||||
assert_equal ["#{file_path}/aaa"], Dir["#{file_path}/*"]
|
||||
assert_equal ['aaa'], @yard.files
|
||||
assert @yard.has_file?('aaa')
|
||||
assert_equal 'file contents', File.read(@yard.file_path('aaa'))
|
||||
end
|
||||
|
||||
def test_file_path
|
||||
assert_equal "#{file_path}/abcd", @yard.file_path('abcd')
|
||||
end
|
||||
|
||||
def file_path
|
||||
"#{RAILS_ROOT}/storage/test/instiki"
|
||||
end
|
||||
|
||||
#!/bin/env ruby -w
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'fileutils'
|
||||
require 'file_yard'
|
||||
require 'stringio'
|
||||
|
||||
class FileYardTest < Test::Unit::TestCase
|
||||
|
||||
def setup
|
||||
FileUtils.mkdir_p(file_path)
|
||||
FileUtils.rm(Dir["#{file_path}/*"])
|
||||
@yard = FileYard.new(file_path)
|
||||
end
|
||||
|
||||
def test_files
|
||||
assert_equal [], @yard.files
|
||||
|
||||
# FileYard gets the list of files from directory in the constructor
|
||||
@yard.upload_file('aaa', StringIO.new('file contents'))
|
||||
assert_equal ["#{file_path}/aaa"], Dir["#{file_path}/*"]
|
||||
assert_equal ['aaa'], @yard.files
|
||||
assert @yard.has_file?('aaa')
|
||||
assert_equal 'file contents', File.read(@yard.file_path('aaa'))
|
||||
end
|
||||
|
||||
def test_file_path
|
||||
assert_equal "#{file_path}/abcd", @yard.file_path('abcd')
|
||||
end
|
||||
|
||||
def file_path
|
||||
"#{RAILS_ROOT}/storage/test/instiki"
|
||||
end
|
||||
|
||||
end
|
|
@ -1,91 +1,91 @@
|
|||
#!/bin/env ruby -w
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'web'
|
||||
require 'page'
|
||||
|
||||
class PageTest < Test::Unit::TestCase
|
||||
|
||||
class MockWeb < Web
|
||||
def initialize() super(nil, 'test','test') end
|
||||
def [](wiki_word) %w( MyWay ThatWay SmartEngine ).include?(wiki_word) end
|
||||
def refresh_pages_with_references(name) end
|
||||
end
|
||||
|
||||
def setup
|
||||
@page = Page.new(
|
||||
MockWeb.new,
|
||||
"FirstPage",
|
||||
"HisWay would be MyWay in kinda ThatWay in HisWay though MyWay \\OverThere -- see SmartEngine in that SmartEngineGUI",
|
||||
Time.local(2004, 4, 4, 16, 50),
|
||||
"DavidHeinemeierHansson")
|
||||
end
|
||||
|
||||
def test_lock
|
||||
assert !@page.locked?(Time.local(2004, 4, 4, 16, 50))
|
||||
|
||||
@page.lock(Time.local(2004, 4, 4, 16, 30), "DavidHeinemeierHansson")
|
||||
|
||||
assert @page.locked?(Time.local(2004, 4, 4, 16, 50))
|
||||
assert !@page.locked?(Time.local(2004, 4, 4, 17, 1))
|
||||
|
||||
@page.unlock
|
||||
|
||||
assert !@page.locked?(Time.local(2004, 4, 4, 16, 50))
|
||||
end
|
||||
|
||||
def test_lock_duration
|
||||
@page.lock(Time.local(2004, 4, 4, 16, 30), "DavidHeinemeierHansson")
|
||||
|
||||
assert_equal 15, @page.lock_duration(Time.local(2004, 4, 4, 16, 45))
|
||||
end
|
||||
|
||||
def test_plain_name
|
||||
assert_equal "First Page", @page.plain_name
|
||||
end
|
||||
|
||||
def test_revise
|
||||
@page.revise('HisWay would be MyWay in kinda lame', Time.local(2004, 4, 4, 16, 55), 'MarianneSyhler')
|
||||
assert_equal 2, @page.revisions.length, 'Should have two revisions'
|
||||
assert_equal 'MarianneSyhler', @page.author, 'Mary should be the author now'
|
||||
assert_equal 'DavidHeinemeierHansson', @page.revisions.first.author, 'David was the first author'
|
||||
end
|
||||
|
||||
def test_revise_continous_revision
|
||||
@page.revise('HisWay would be MyWay in kinda lame', Time.local(2004, 4, 4, 16, 55), 'MarianneSyhler')
|
||||
assert_equal 2, @page.revisions.length
|
||||
|
||||
@page.revise('HisWay would be MyWay in kinda update', Time.local(2004, 4, 4, 16, 57), 'MarianneSyhler')
|
||||
assert_equal 2, @page.revisions.length
|
||||
assert_equal 'HisWay would be MyWay in kinda update', @page.revisions.last.content
|
||||
assert_equal Time.local(2004, 4, 4, 16, 57), @page.revisions.last.created_at
|
||||
|
||||
@page.revise('HisWay would be MyWay in the house', Time.local(2004, 4, 4, 16, 58), 'DavidHeinemeierHansson')
|
||||
assert_equal 3, @page.revisions.length
|
||||
assert_equal 'HisWay would be MyWay in the house', @page.revisions.last.content
|
||||
|
||||
@page.revise('HisWay would be MyWay in my way', Time.local(2004, 4, 4, 17, 30), 'DavidHeinemeierHansson')
|
||||
assert_equal 4, @page.revisions.length
|
||||
end
|
||||
|
||||
def test_revise_content_unchanged
|
||||
last_revision_before = @page.revisions.last
|
||||
revisions_number_before = @page.revisions.size
|
||||
|
||||
assert_raises(Instiki::ValidationError) {
|
||||
@page.revise(@page.revisions.last.content.dup, Time.now, 'AlexeyVerkhovsky')
|
||||
}
|
||||
|
||||
assert_same last_revision_before, @page.revisions.last
|
||||
assert_equal revisions_number_before, @page.revisions.size
|
||||
end
|
||||
|
||||
def test_rollback
|
||||
@page.revise("spot two", Time.now, "David")
|
||||
@page.revise("spot three", Time.now + 2000, "David")
|
||||
assert_equal 3, @page.revisions.length, "Should have three revisions"
|
||||
@page.rollback(1, Time.now)
|
||||
assert_equal "spot two", @page.content
|
||||
end
|
||||
|
||||
end
|
||||
#!/bin/env ruby -w
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'web'
|
||||
require 'page'
|
||||
|
||||
class PageTest < Test::Unit::TestCase
|
||||
|
||||
class MockWeb < Web
|
||||
def initialize() super(nil, 'test','test') end
|
||||
def [](wiki_word) %w( MyWay ThatWay SmartEngine ).include?(wiki_word) end
|
||||
def refresh_pages_with_references(name) end
|
||||
end
|
||||
|
||||
def setup
|
||||
@page = Page.new(
|
||||
MockWeb.new,
|
||||
"FirstPage",
|
||||
"HisWay would be MyWay in kinda ThatWay in HisWay though MyWay \\OverThere -- see SmartEngine in that SmartEngineGUI",
|
||||
Time.local(2004, 4, 4, 16, 50),
|
||||
"DavidHeinemeierHansson")
|
||||
end
|
||||
|
||||
def test_lock
|
||||
assert !@page.locked?(Time.local(2004, 4, 4, 16, 50))
|
||||
|
||||
@page.lock(Time.local(2004, 4, 4, 16, 30), "DavidHeinemeierHansson")
|
||||
|
||||
assert @page.locked?(Time.local(2004, 4, 4, 16, 50))
|
||||
assert !@page.locked?(Time.local(2004, 4, 4, 17, 1))
|
||||
|
||||
@page.unlock
|
||||
|
||||
assert !@page.locked?(Time.local(2004, 4, 4, 16, 50))
|
||||
end
|
||||
|
||||
def test_lock_duration
|
||||
@page.lock(Time.local(2004, 4, 4, 16, 30), "DavidHeinemeierHansson")
|
||||
|
||||
assert_equal 15, @page.lock_duration(Time.local(2004, 4, 4, 16, 45))
|
||||
end
|
||||
|
||||
def test_plain_name
|
||||
assert_equal "First Page", @page.plain_name
|
||||
end
|
||||
|
||||
def test_revise
|
||||
@page.revise('HisWay would be MyWay in kinda lame', Time.local(2004, 4, 4, 16, 55), 'MarianneSyhler')
|
||||
assert_equal 2, @page.revisions.length, 'Should have two revisions'
|
||||
assert_equal 'MarianneSyhler', @page.author, 'Mary should be the author now'
|
||||
assert_equal 'DavidHeinemeierHansson', @page.revisions.first.author, 'David was the first author'
|
||||
end
|
||||
|
||||
def test_revise_continous_revision
|
||||
@page.revise('HisWay would be MyWay in kinda lame', Time.local(2004, 4, 4, 16, 55), 'MarianneSyhler')
|
||||
assert_equal 2, @page.revisions.length
|
||||
|
||||
@page.revise('HisWay would be MyWay in kinda update', Time.local(2004, 4, 4, 16, 57), 'MarianneSyhler')
|
||||
assert_equal 2, @page.revisions.length
|
||||
assert_equal 'HisWay would be MyWay in kinda update', @page.revisions.last.content
|
||||
assert_equal Time.local(2004, 4, 4, 16, 57), @page.revisions.last.created_at
|
||||
|
||||
@page.revise('HisWay would be MyWay in the house', Time.local(2004, 4, 4, 16, 58), 'DavidHeinemeierHansson')
|
||||
assert_equal 3, @page.revisions.length
|
||||
assert_equal 'HisWay would be MyWay in the house', @page.revisions.last.content
|
||||
|
||||
@page.revise('HisWay would be MyWay in my way', Time.local(2004, 4, 4, 17, 30), 'DavidHeinemeierHansson')
|
||||
assert_equal 4, @page.revisions.length
|
||||
end
|
||||
|
||||
def test_revise_content_unchanged
|
||||
last_revision_before = @page.revisions.last
|
||||
revisions_number_before = @page.revisions.size
|
||||
|
||||
assert_raises(Instiki::ValidationError) {
|
||||
@page.revise(@page.revisions.last.content.dup, Time.now, 'AlexeyVerkhovsky')
|
||||
}
|
||||
|
||||
assert_same last_revision_before, @page.revisions.last
|
||||
assert_equal revisions_number_before, @page.revisions.size
|
||||
end
|
||||
|
||||
def test_rollback
|
||||
@page.revise("spot two", Time.now, "David")
|
||||
@page.revise("spot three", Time.now + 2000, "David")
|
||||
assert_equal 3, @page.revisions.length, "Should have three revisions"
|
||||
@page.rollback(1, Time.now)
|
||||
assert_equal "spot two", @page.content
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,69 +1,69 @@
|
|||
#!/bin/env ruby -w
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'redcloth_for_tex'
|
||||
|
||||
class RedClothForTexTest < Test::Unit::TestCase
|
||||
def test_basics
|
||||
assert_equal '{\bf First Page}', RedClothForTex.new("*First Page*").to_tex
|
||||
assert_equal '{\em First Page}', RedClothForTex.new("_First Page_").to_tex
|
||||
assert_equal "\\begin{itemize}\n\t\\item A\n\t\t\\item B\n\t\t\\item C\n\t\\end{itemize}", RedClothForTex.new("* A\n* B\n* C").to_tex
|
||||
end
|
||||
|
||||
def test_blocks
|
||||
assert_equal '\section*{hello}', RedClothForTex.new("h1. hello").to_tex
|
||||
assert_equal '\subsection*{hello}', RedClothForTex.new("h2. hello").to_tex
|
||||
end
|
||||
|
||||
def test_table_of_contents
|
||||
|
||||
source = <<EOL
|
||||
* [[A]]
|
||||
** [[B]]
|
||||
** [[C]]
|
||||
* D
|
||||
** [[E]]
|
||||
*** F
|
||||
EOL
|
||||
|
||||
expected_result = <<EOL
|
||||
\\pagebreak
|
||||
|
||||
\\section{A}
|
||||
Abe
|
||||
|
||||
\\subsection{B}
|
||||
Babe
|
||||
|
||||
\\subsection{C}
|
||||
\\pagebreak
|
||||
|
||||
\\section{D}
|
||||
|
||||
\\subsection{E}
|
||||
|
||||
\\subsubsection{F}
|
||||
EOL
|
||||
expected_result.chop!
|
||||
assert_equal(expected_result, table_of_contents(source, 'A' => 'Abe', 'B' => 'Babe'))
|
||||
end
|
||||
|
||||
def test_entities
|
||||
assert_equal "Beck \\& Fowler are 100\\% cool", RedClothForTex.new("Beck & Fowler are 100% cool").to_tex
|
||||
end
|
||||
|
||||
def test_bracket_links
|
||||
assert_equal "such a Horrible Day, but I won't be Made Useless", RedClothForTex.new("such a [[Horrible Day]], but I won't be [[Made Useless]]").to_tex
|
||||
end
|
||||
|
||||
def test_footnotes_on_abbreviations
|
||||
assert_equal(
|
||||
"such a Horrible Day\\footnote{1}, but I won't be Made Useless",
|
||||
RedClothForTex.new("such a [[Horrible Day]][1], but I won't be [[Made Useless]]").to_tex
|
||||
)
|
||||
end
|
||||
|
||||
def test_subsection_depth
|
||||
assert_equal "\\subsubsection*{Hello}", RedClothForTex.new("h4. Hello").to_tex
|
||||
end
|
||||
#!/bin/env ruby -w
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'redcloth_for_tex'
|
||||
|
||||
class RedClothForTexTest < Test::Unit::TestCase
|
||||
def test_basics
|
||||
assert_equal '{\bf First Page}', RedClothForTex.new("*First Page*").to_tex
|
||||
assert_equal '{\em First Page}', RedClothForTex.new("_First Page_").to_tex
|
||||
assert_equal "\\begin{itemize}\n\t\\item A\n\t\t\\item B\n\t\t\\item C\n\t\\end{itemize}", RedClothForTex.new("* A\n* B\n* C").to_tex
|
||||
end
|
||||
|
||||
def test_blocks
|
||||
assert_equal '\section*{hello}', RedClothForTex.new("h1. hello").to_tex
|
||||
assert_equal '\subsection*{hello}', RedClothForTex.new("h2. hello").to_tex
|
||||
end
|
||||
|
||||
def test_table_of_contents
|
||||
|
||||
source = <<EOL
|
||||
* [[A]]
|
||||
** [[B]]
|
||||
** [[C]]
|
||||
* D
|
||||
** [[E]]
|
||||
*** F
|
||||
EOL
|
||||
|
||||
expected_result = <<EOL
|
||||
\\pagebreak
|
||||
|
||||
\\section{A}
|
||||
Abe
|
||||
|
||||
\\subsection{B}
|
||||
Babe
|
||||
|
||||
\\subsection{C}
|
||||
\\pagebreak
|
||||
|
||||
\\section{D}
|
||||
|
||||
\\subsection{E}
|
||||
|
||||
\\subsubsection{F}
|
||||
EOL
|
||||
expected_result.chop!
|
||||
assert_equal(expected_result, table_of_contents(source, 'A' => 'Abe', 'B' => 'Babe'))
|
||||
end
|
||||
|
||||
def test_entities
|
||||
assert_equal "Beck \\& Fowler are 100\\% cool", RedClothForTex.new("Beck & Fowler are 100% cool").to_tex
|
||||
end
|
||||
|
||||
def test_bracket_links
|
||||
assert_equal "such a Horrible Day, but I won't be Made Useless", RedClothForTex.new("such a [[Horrible Day]], but I won't be [[Made Useless]]").to_tex
|
||||
end
|
||||
|
||||
def test_footnotes_on_abbreviations
|
||||
assert_equal(
|
||||
"such a Horrible Day\\footnote{1}, but I won't be Made Useless",
|
||||
RedClothForTex.new("such a [[Horrible Day]][1], but I won't be [[Made Useless]]").to_tex
|
||||
)
|
||||
end
|
||||
|
||||
def test_subsection_depth
|
||||
assert_equal "\\subsubsection*{Hello}", RedClothForTex.new("h4. Hello").to_tex
|
||||
end
|
||||
end
|
|
@ -1,255 +1,255 @@
|
|||
#!/bin/env ruby -w
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'web'
|
||||
require 'revision'
|
||||
require 'fileutils'
|
||||
|
||||
class RevisionTest < Test::Unit::TestCase
|
||||
|
||||
def setup
|
||||
setup_test_wiki
|
||||
@web.markup = :textile
|
||||
|
||||
@page = @wiki.read_page('wiki1', 'HomePage')
|
||||
['MyWay', 'SmartEngine', 'ThatWay'].each do |page|
|
||||
@wiki.write_page('wiki1', page, page, Time.now, 'Me')
|
||||
end
|
||||
|
||||
@revision = Revision.new(@page, 1,
|
||||
'HisWay would be MyWay in kinda ThatWay in HisWay though MyWay \OverThere -- ' +
|
||||
'see SmartEngine in that SmartEngineGUI',
|
||||
Time.local(2004, 4, 4, 16, 50), 'DavidHeinemeierHansson')
|
||||
end
|
||||
|
||||
def test_wiki_words
|
||||
assert_equal %w( HisWay MyWay SmartEngine SmartEngineGUI ThatWay ), @revision.wiki_words.sort
|
||||
end
|
||||
|
||||
def test_existing_pages
|
||||
assert_equal %w( MyWay SmartEngine ThatWay ), @revision.existing_pages.sort
|
||||
end
|
||||
|
||||
def test_unexisting_pages
|
||||
assert_equal %w( HisWay SmartEngineGUI ), @revision.unexisting_pages.sort
|
||||
end
|
||||
|
||||
def test_content_with_wiki_links
|
||||
assert_equal '<p><span class="newWikiWord">His Way<a href="../show/HisWay">?</a></span> ' +
|
||||
'would be <a class="existingWikiWord" href="../show/MyWay">My Way</a> in kinda ' +
|
||||
'<a class="existingWikiWord" href="../show/ThatWay">That Way</a> in ' +
|
||||
'<span class="newWikiWord">His Way<a href="../show/HisWay">?</a></span> ' +
|
||||
'though <a class="existingWikiWord" href="../show/MyWay">My Way</a> OverThere—see ' +
|
||||
'<a class="existingWikiWord" href="../show/SmartEngine">Smart Engine</a> in that ' +
|
||||
'<span class="newWikiWord">Smart Engine GUI' +
|
||||
'<a href="../show/SmartEngineGUI">?</a></span></p>',
|
||||
@revision.display_content
|
||||
end
|
||||
|
||||
def test_bluecloth
|
||||
@web.markup = :markdown
|
||||
|
||||
assert_markup_parsed_as(
|
||||
%{<h1>My Headline</h1>\n\n<p>that <span class="newWikiWord">} +
|
||||
%{Smart Engine GUI<a href="../show/SmartEngineGUI">?</a></span></p>},
|
||||
"My Headline\n===========\n\n that SmartEngineGUI")
|
||||
|
||||
code_block = [
|
||||
'This is a code block:',
|
||||
'',
|
||||
' def a_method(arg)',
|
||||
' return ThatWay',
|
||||
'',
|
||||
'Nice!'
|
||||
].join("\n")
|
||||
|
||||
assert_markup_parsed_as(
|
||||
%{<p>This is a code block:</p>\n\n<pre><code>def a_method(arg)\n} +
|
||||
%{return ThatWay\n</code></pre>\n\n<p>Nice!</p>},
|
||||
code_block)
|
||||
end
|
||||
|
||||
def test_rdoc
|
||||
@web.markup = :rdoc
|
||||
|
||||
@revision = Revision.new(@page, 1, '+hello+ that SmartEngineGUI',
|
||||
Time.local(2004, 4, 4, 16, 50), 'DavidHeinemeierHansson')
|
||||
|
||||
assert_equal "<tt>hello</tt> that <span class=\"newWikiWord\">Smart Engine GUI" +
|
||||
"<a href=\"../show/SmartEngineGUI\">?</a></span>\n\n", @revision.display_content
|
||||
end
|
||||
|
||||
def test_content_with_auto_links
|
||||
assert_markup_parsed_as(
|
||||
'<p><a href="http://www.loudthinking.com/">http://www.loudthinking.com/</a> ' +
|
||||
'points to <a class="existingWikiWord" href="../show/ThatWay">That Way</a> from ' +
|
||||
'<a href="mailto:david@loudthinking.com">david@loudthinking.com</a></p>',
|
||||
'http://www.loudthinking.com/ points to ThatWay from david@loudthinking.com')
|
||||
|
||||
end
|
||||
|
||||
def test_content_with_aliased_links
|
||||
assert_markup_parsed_as(
|
||||
'<p>Would a <a class="existingWikiWord" href="../show/SmartEngine">clever motor' +
|
||||
'</a> go by any other name?</p>',
|
||||
'Would a [[SmartEngine|clever motor]] go by any other name?')
|
||||
end
|
||||
|
||||
def test_content_with_wikiword_in_em
|
||||
assert_markup_parsed_as(
|
||||
'<p><em>should we go <a class="existingWikiWord" href="../show/ThatWay">' +
|
||||
'That Way</a> or <span class="newWikiWord">This Way<a href="../show/ThisWay">?</a>' +
|
||||
'</span> </em></p>',
|
||||
'_should we go ThatWay or ThisWay _')
|
||||
end
|
||||
|
||||
def test_content_with_wikiword_in_tag
|
||||
assert_markup_parsed_as(
|
||||
'<p>That is some <em style="WikiWord">Stylish Emphasis</em></p>',
|
||||
'That is some <em style="WikiWord">Stylish Emphasis</em>')
|
||||
end
|
||||
|
||||
def test_content_with_escaped_wikiword
|
||||
# there should be no wiki link
|
||||
assert_markup_parsed_as('<p>WikiWord</p>', '\WikiWord')
|
||||
end
|
||||
|
||||
def test_content_with_pre_blocks
|
||||
assert_markup_parsed_as(
|
||||
'A <code>class SmartEngine end</code> would not mark up <pre>CodeBlocks</pre>',
|
||||
'A <code>class SmartEngine end</code> would not mark up <pre>CodeBlocks</pre>')
|
||||
end
|
||||
|
||||
def test_content_with_autolink_in_parentheses
|
||||
assert_markup_parsed_as(
|
||||
'<p>The <span class="caps">W3C</span> body (<a href="http://www.w3c.org">' +
|
||||
'http://www.w3c.org</a>) sets web standards</p>',
|
||||
'The W3C body (http://www.w3c.org) sets web standards')
|
||||
end
|
||||
|
||||
def test_content_with_link_in_parentheses
|
||||
assert_markup_parsed_as(
|
||||
'<p>(<a href="http://wiki.org/wiki.cgi?WhatIsWiki">What is a wiki?</a>)</p>',
|
||||
'("What is a wiki?":http://wiki.org/wiki.cgi?WhatIsWiki)')
|
||||
end
|
||||
|
||||
def test_content_with_image_link
|
||||
assert_markup_parsed_as(
|
||||
'<p>This <img src="http://hobix.com/sample.jpg" alt="" /> is a Textile image link.</p>',
|
||||
'This !http://hobix.com/sample.jpg! is a Textile image link.')
|
||||
end
|
||||
|
||||
def test_content_with_nowiki_text
|
||||
assert_markup_parsed_as(
|
||||
'<p>Do not mark up [[this text]] or http://www.thislink.com.</p>',
|
||||
'Do not mark up <nowiki>[[this text]]</nowiki> ' +
|
||||
'or <nowiki>http://www.thislink.com</nowiki>.')
|
||||
end
|
||||
|
||||
def test_content_with_bracketted_wiki_word
|
||||
@web.brackets_only = true
|
||||
assert_markup_parsed_as(
|
||||
'<p>This is a WikiWord and a tricky name <span class="newWikiWord">' +
|
||||
'Sperberg-McQueen<a href="../show/Sperberg-McQueen">?</a></span>.</p>',
|
||||
'This is a WikiWord and a tricky name [[Sperberg-McQueen]].')
|
||||
end
|
||||
|
||||
def test_content_for_export
|
||||
assert_equal '<p><span class="newWikiWord">His Way</span> would be ' +
|
||||
'<a class="existingWikiWord" href="MyWay.html">My Way</a> in kinda ' +
|
||||
'<a class="existingWikiWord" href="ThatWay.html">That Way</a> in ' +
|
||||
'<span class="newWikiWord">His Way</span> though ' +
|
||||
'<a class="existingWikiWord" href="MyWay.html">My Way</a> OverThere—see ' +
|
||||
'<a class="existingWikiWord" href="SmartEngine.html">Smart Engine</a> in that ' +
|
||||
'<span class="newWikiWord">Smart Engine GUI</span></p>',
|
||||
@revision.display_content_for_export
|
||||
end
|
||||
|
||||
def test_double_replacing
|
||||
@revision.content = "VersionHistory\r\n\r\ncry VersionHistory"
|
||||
assert_equal '<p><span class="newWikiWord">Version History' +
|
||||
"<a href=\"../show/VersionHistory\">?</a></span></p>\n\n\t<p>cry " +
|
||||
'<span class="newWikiWord">Version History<a href="../show/VersionHistory">?</a>' +
|
||||
'</span></p>',
|
||||
@revision.display_content
|
||||
|
||||
@revision.clear_display_cache
|
||||
|
||||
@revision.content = "f\r\nVersionHistory\r\n\r\ncry VersionHistory"
|
||||
assert_equal "<p>f<br />\n<span class=\"newWikiWord\">Version History" +
|
||||
"<a href=\"../show/VersionHistory\">?</a></span></p>\n\n\t<p>cry " +
|
||||
"<span class=\"newWikiWord\">Version History<a href=\"../show/VersionHistory\">?</a>" +
|
||||
"</span></p>",
|
||||
@revision.display_content
|
||||
end
|
||||
|
||||
def test_difficult_wiki_words
|
||||
@revision.content = "[[It's just awesome GUI!]]"
|
||||
assert_equal "<p><span class=\"newWikiWord\">It's just awesome GUI!" +
|
||||
"<a href=\"../show/It%27s+just+awesome+GUI%21\">?</a></span></p>",
|
||||
@revision.display_content
|
||||
end
|
||||
|
||||
def test_revisions_diff
|
||||
|
||||
@page.revisions = [
|
||||
Revision.new(@page, 0, 'What a blue and lovely morning',
|
||||
Time.local(2004, 4, 4, 16, 50), 'DavidHeinemeierHansson'),
|
||||
Revision.new(@page, 1, 'What a red and lovely morning today',
|
||||
Time.local(2004, 4, 4, 16, 50), 'DavidHeinemeierHansson')
|
||||
]
|
||||
|
||||
assert_equal "<p>What a <del class=\"diffmod\">blue </del><ins class=\"diffmod\">red " +
|
||||
"</ins>and lovely <del class=\"diffmod\">morning</del><ins class=\"diffmod\">morning " +
|
||||
"today</ins></p>", @page.revisions.last.display_diff
|
||||
end
|
||||
|
||||
def test_link_to_file
|
||||
assert_markup_parsed_as(
|
||||
'<p><span class="newWikiWord">doc.pdf<a href="../file/doc.pdf">?</a></span></p>',
|
||||
'[[doc.pdf:file]]')
|
||||
end
|
||||
|
||||
def test_link_to_pic
|
||||
@wiki.file_yard(@web).upload_file('square.jpg', StringIO.new(''))
|
||||
assert_markup_parsed_as(
|
||||
'<p><img alt="Square" src="../pic/square.jpg" /></p>',
|
||||
'[[square.jpg|Square:pic]]')
|
||||
assert_markup_parsed_as(
|
||||
'<p><img alt="square.jpg" src="../pic/square.jpg" /></p>',
|
||||
'[[square.jpg:pic]]')
|
||||
end
|
||||
|
||||
def test_link_to_non_existant_pic
|
||||
assert_markup_parsed_as(
|
||||
'<p><span class="newWikiWord">NonExistant<a href="../pic/NonExistant.jpg">?</a>' +
|
||||
'</span></p>',
|
||||
'[[NonExistant.jpg|NonExistant:pic]]')
|
||||
assert_markup_parsed_as(
|
||||
'<p><span class="newWikiWord">NonExistant.jpg<a href="../pic/NonExistant.jpg">?</a>' +
|
||||
'</span></p>',
|
||||
'[[NonExistant.jpg:pic]]')
|
||||
end
|
||||
|
||||
# TODO Remove the leading underscores from this test when upgrading to RedCloth 3.0.1;
|
||||
# also add a test for the "Unhappy Face" problem (another interesting RedCloth bug)
|
||||
def __test_list_with_tildas
|
||||
list_with_tildas = <<-EOL
|
||||
* "a":~b
|
||||
* c~ d
|
||||
EOL
|
||||
|
||||
assert_markup_parsed_as(
|
||||
"<li><a href=\"~b\">a</a></li>\n" +
|
||||
"<li>c~ d</li>\n",
|
||||
list_with_tildas)
|
||||
end
|
||||
|
||||
|
||||
|
||||
def assert_markup_parsed_as(expected_output, input)
|
||||
revision = Revision.new(@page, 1, input, Time.local(2004, 4, 4, 16, 50), 'AnAuthor')
|
||||
assert_equal expected_output, revision.display_content, 'Textile output not as expected'
|
||||
end
|
||||
|
||||
end
|
||||
#!/bin/env ruby -w
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'web'
|
||||
require 'revision'
|
||||
require 'fileutils'
|
||||
|
||||
class RevisionTest < Test::Unit::TestCase
|
||||
|
||||
def setup
|
||||
setup_test_wiki
|
||||
@web.markup = :textile
|
||||
|
||||
@page = @wiki.read_page('wiki1', 'HomePage')
|
||||
['MyWay', 'SmartEngine', 'ThatWay'].each do |page|
|
||||
@wiki.write_page('wiki1', page, page, Time.now, 'Me')
|
||||
end
|
||||
|
||||
@revision = Revision.new(@page, 1,
|
||||
'HisWay would be MyWay in kinda ThatWay in HisWay though MyWay \OverThere -- ' +
|
||||
'see SmartEngine in that SmartEngineGUI',
|
||||
Time.local(2004, 4, 4, 16, 50), 'DavidHeinemeierHansson')
|
||||
end
|
||||
|
||||
def test_wiki_words
|
||||
assert_equal %w( HisWay MyWay SmartEngine SmartEngineGUI ThatWay ), @revision.wiki_words.sort
|
||||
end
|
||||
|
||||
def test_existing_pages
|
||||
assert_equal %w( MyWay SmartEngine ThatWay ), @revision.existing_pages.sort
|
||||
end
|
||||
|
||||
def test_unexisting_pages
|
||||
assert_equal %w( HisWay SmartEngineGUI ), @revision.unexisting_pages.sort
|
||||
end
|
||||
|
||||
def test_content_with_wiki_links
|
||||
assert_equal '<p><span class="newWikiWord">His Way<a href="../show/HisWay">?</a></span> ' +
|
||||
'would be <a class="existingWikiWord" href="../show/MyWay">My Way</a> in kinda ' +
|
||||
'<a class="existingWikiWord" href="../show/ThatWay">That Way</a> in ' +
|
||||
'<span class="newWikiWord">His Way<a href="../show/HisWay">?</a></span> ' +
|
||||
'though <a class="existingWikiWord" href="../show/MyWay">My Way</a> OverThere—see ' +
|
||||
'<a class="existingWikiWord" href="../show/SmartEngine">Smart Engine</a> in that ' +
|
||||
'<span class="newWikiWord">Smart Engine GUI' +
|
||||
'<a href="../show/SmartEngineGUI">?</a></span></p>',
|
||||
@revision.display_content
|
||||
end
|
||||
|
||||
def test_bluecloth
|
||||
@web.markup = :markdown
|
||||
|
||||
assert_markup_parsed_as(
|
||||
%{<h1>My Headline</h1>\n\n<p>that <span class="newWikiWord">} +
|
||||
%{Smart Engine GUI<a href="../show/SmartEngineGUI">?</a></span></p>},
|
||||
"My Headline\n===========\n\n that SmartEngineGUI")
|
||||
|
||||
code_block = [
|
||||
'This is a code block:',
|
||||
'',
|
||||
' def a_method(arg)',
|
||||
' return ThatWay',
|
||||
'',
|
||||
'Nice!'
|
||||
].join("\n")
|
||||
|
||||
assert_markup_parsed_as(
|
||||
%{<p>This is a code block:</p>\n\n<pre><code>def a_method(arg)\n} +
|
||||
%{return ThatWay\n</code></pre>\n\n<p>Nice!</p>},
|
||||
code_block)
|
||||
end
|
||||
|
||||
def test_rdoc
|
||||
@web.markup = :rdoc
|
||||
|
||||
@revision = Revision.new(@page, 1, '+hello+ that SmartEngineGUI',
|
||||
Time.local(2004, 4, 4, 16, 50), 'DavidHeinemeierHansson')
|
||||
|
||||
assert_equal "<tt>hello</tt> that <span class=\"newWikiWord\">Smart Engine GUI" +
|
||||
"<a href=\"../show/SmartEngineGUI\">?</a></span>\n\n", @revision.display_content
|
||||
end
|
||||
|
||||
def test_content_with_auto_links
|
||||
assert_markup_parsed_as(
|
||||
'<p><a href="http://www.loudthinking.com/">http://www.loudthinking.com/</a> ' +
|
||||
'points to <a class="existingWikiWord" href="../show/ThatWay">That Way</a> from ' +
|
||||
'<a href="mailto:david@loudthinking.com">david@loudthinking.com</a></p>',
|
||||
'http://www.loudthinking.com/ points to ThatWay from david@loudthinking.com')
|
||||
|
||||
end
|
||||
|
||||
def test_content_with_aliased_links
|
||||
assert_markup_parsed_as(
|
||||
'<p>Would a <a class="existingWikiWord" href="../show/SmartEngine">clever motor' +
|
||||
'</a> go by any other name?</p>',
|
||||
'Would a [[SmartEngine|clever motor]] go by any other name?')
|
||||
end
|
||||
|
||||
def test_content_with_wikiword_in_em
|
||||
assert_markup_parsed_as(
|
||||
'<p><em>should we go <a class="existingWikiWord" href="../show/ThatWay">' +
|
||||
'That Way</a> or <span class="newWikiWord">This Way<a href="../show/ThisWay">?</a>' +
|
||||
'</span> </em></p>',
|
||||
'_should we go ThatWay or ThisWay _')
|
||||
end
|
||||
|
||||
def test_content_with_wikiword_in_tag
|
||||
assert_markup_parsed_as(
|
||||
'<p>That is some <em style="WikiWord">Stylish Emphasis</em></p>',
|
||||
'That is some <em style="WikiWord">Stylish Emphasis</em>')
|
||||
end
|
||||
|
||||
def test_content_with_escaped_wikiword
|
||||
# there should be no wiki link
|
||||
assert_markup_parsed_as('<p>WikiWord</p>', '\WikiWord')
|
||||
end
|
||||
|
||||
def test_content_with_pre_blocks
|
||||
assert_markup_parsed_as(
|
||||
'A <code>class SmartEngine end</code> would not mark up <pre>CodeBlocks</pre>',
|
||||
'A <code>class SmartEngine end</code> would not mark up <pre>CodeBlocks</pre>')
|
||||
end
|
||||
|
||||
def test_content_with_autolink_in_parentheses
|
||||
assert_markup_parsed_as(
|
||||
'<p>The <span class="caps">W3C</span> body (<a href="http://www.w3c.org">' +
|
||||
'http://www.w3c.org</a>) sets web standards</p>',
|
||||
'The W3C body (http://www.w3c.org) sets web standards')
|
||||
end
|
||||
|
||||
def test_content_with_link_in_parentheses
|
||||
assert_markup_parsed_as(
|
||||
'<p>(<a href="http://wiki.org/wiki.cgi?WhatIsWiki">What is a wiki?</a>)</p>',
|
||||
'("What is a wiki?":http://wiki.org/wiki.cgi?WhatIsWiki)')
|
||||
end
|
||||
|
||||
def test_content_with_image_link
|
||||
assert_markup_parsed_as(
|
||||
'<p>This <img src="http://hobix.com/sample.jpg" alt="" /> is a Textile image link.</p>',
|
||||
'This !http://hobix.com/sample.jpg! is a Textile image link.')
|
||||
end
|
||||
|
||||
def test_content_with_nowiki_text
|
||||
assert_markup_parsed_as(
|
||||
'<p>Do not mark up [[this text]] or http://www.thislink.com.</p>',
|
||||
'Do not mark up <nowiki>[[this text]]</nowiki> ' +
|
||||
'or <nowiki>http://www.thislink.com</nowiki>.')
|
||||
end
|
||||
|
||||
def test_content_with_bracketted_wiki_word
|
||||
@web.brackets_only = true
|
||||
assert_markup_parsed_as(
|
||||
'<p>This is a WikiWord and a tricky name <span class="newWikiWord">' +
|
||||
'Sperberg-McQueen<a href="../show/Sperberg-McQueen">?</a></span>.</p>',
|
||||
'This is a WikiWord and a tricky name [[Sperberg-McQueen]].')
|
||||
end
|
||||
|
||||
def test_content_for_export
|
||||
assert_equal '<p><span class="newWikiWord">His Way</span> would be ' +
|
||||
'<a class="existingWikiWord" href="MyWay.html">My Way</a> in kinda ' +
|
||||
'<a class="existingWikiWord" href="ThatWay.html">That Way</a> in ' +
|
||||
'<span class="newWikiWord">His Way</span> though ' +
|
||||
'<a class="existingWikiWord" href="MyWay.html">My Way</a> OverThere—see ' +
|
||||
'<a class="existingWikiWord" href="SmartEngine.html">Smart Engine</a> in that ' +
|
||||
'<span class="newWikiWord">Smart Engine GUI</span></p>',
|
||||
@revision.display_content_for_export
|
||||
end
|
||||
|
||||
def test_double_replacing
|
||||
@revision.content = "VersionHistory\r\n\r\ncry VersionHistory"
|
||||
assert_equal '<p><span class="newWikiWord">Version History' +
|
||||
"<a href=\"../show/VersionHistory\">?</a></span></p>\n\n\t<p>cry " +
|
||||
'<span class="newWikiWord">Version History<a href="../show/VersionHistory">?</a>' +
|
||||
'</span></p>',
|
||||
@revision.display_content
|
||||
|
||||
@revision.clear_display_cache
|
||||
|
||||
@revision.content = "f\r\nVersionHistory\r\n\r\ncry VersionHistory"
|
||||
assert_equal "<p>f<br />\n<span class=\"newWikiWord\">Version History" +
|
||||
"<a href=\"../show/VersionHistory\">?</a></span></p>\n\n\t<p>cry " +
|
||||
"<span class=\"newWikiWord\">Version History<a href=\"../show/VersionHistory\">?</a>" +
|
||||
"</span></p>",
|
||||
@revision.display_content
|
||||
end
|
||||
|
||||
def test_difficult_wiki_words
|
||||
@revision.content = "[[It's just awesome GUI!]]"
|
||||
assert_equal "<p><span class=\"newWikiWord\">It's just awesome GUI!" +
|
||||
"<a href=\"../show/It%27s+just+awesome+GUI%21\">?</a></span></p>",
|
||||
@revision.display_content
|
||||
end
|
||||
|
||||
def test_revisions_diff
|
||||
|
||||
@page.revisions = [
|
||||
Revision.new(@page, 0, 'What a blue and lovely morning',
|
||||
Time.local(2004, 4, 4, 16, 50), 'DavidHeinemeierHansson'),
|
||||
Revision.new(@page, 1, 'What a red and lovely morning today',
|
||||
Time.local(2004, 4, 4, 16, 50), 'DavidHeinemeierHansson')
|
||||
]
|
||||
|
||||
assert_equal "<p>What a <del class=\"diffmod\">blue </del><ins class=\"diffmod\">red " +
|
||||
"</ins>and lovely <del class=\"diffmod\">morning</del><ins class=\"diffmod\">morning " +
|
||||
"today</ins></p>", @page.revisions.last.display_diff
|
||||
end
|
||||
|
||||
def test_link_to_file
|
||||
assert_markup_parsed_as(
|
||||
'<p><span class="newWikiWord">doc.pdf<a href="../file/doc.pdf">?</a></span></p>',
|
||||
'[[doc.pdf:file]]')
|
||||
end
|
||||
|
||||
def test_link_to_pic
|
||||
@wiki.file_yard(@web).upload_file('square.jpg', StringIO.new(''))
|
||||
assert_markup_parsed_as(
|
||||
'<p><img alt="Square" src="../pic/square.jpg" /></p>',
|
||||
'[[square.jpg|Square:pic]]')
|
||||
assert_markup_parsed_as(
|
||||
'<p><img alt="square.jpg" src="../pic/square.jpg" /></p>',
|
||||
'[[square.jpg:pic]]')
|
||||
end
|
||||
|
||||
def test_link_to_non_existant_pic
|
||||
assert_markup_parsed_as(
|
||||
'<p><span class="newWikiWord">NonExistant<a href="../pic/NonExistant.jpg">?</a>' +
|
||||
'</span></p>',
|
||||
'[[NonExistant.jpg|NonExistant:pic]]')
|
||||
assert_markup_parsed_as(
|
||||
'<p><span class="newWikiWord">NonExistant.jpg<a href="../pic/NonExistant.jpg">?</a>' +
|
||||
'</span></p>',
|
||||
'[[NonExistant.jpg:pic]]')
|
||||
end
|
||||
|
||||
# TODO Remove the leading underscores from this test when upgrading to RedCloth 3.0.1;
|
||||
# also add a test for the "Unhappy Face" problem (another interesting RedCloth bug)
|
||||
def __test_list_with_tildas
|
||||
list_with_tildas = <<-EOL
|
||||
* "a":~b
|
||||
* c~ d
|
||||
EOL
|
||||
|
||||
assert_markup_parsed_as(
|
||||
"<li><a href=\"~b\">a</a></li>\n" +
|
||||
"<li>c~ d</li>\n",
|
||||
list_with_tildas)
|
||||
end
|
||||
|
||||
|
||||
|
||||
def assert_markup_parsed_as(expected_output, input)
|
||||
revision = Revision.new(@page, 1, input, Time.local(2004, 4, 4, 16, 50), 'AnAuthor')
|
||||
assert_equal expected_output, revision.display_content, 'Textile output not as expected'
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,179 +1,179 @@
|
|||
#!/bin/env ruby -w
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'chunks/uri'
|
||||
|
||||
class URITest < Test::Unit::TestCase
|
||||
include ChunkMatch
|
||||
|
||||
def test_non_matches
|
||||
assert_conversion_does_not_apply(URIChunk, 'There is no URI here')
|
||||
assert_conversion_does_not_apply(URIChunk,
|
||||
'One gemstone is the garnet:reddish in colour, like ruby')
|
||||
end
|
||||
|
||||
def test_simple_uri
|
||||
# Simplest case
|
||||
match(URIChunk, 'http://www.example.com',
|
||||
:scheme =>'http', :host =>'www.example.com', :path => nil,
|
||||
:link_text => 'http://www.example.com'
|
||||
)
|
||||
# With trailing slash
|
||||
match(URIChunk, 'http://www.example.com/',
|
||||
:scheme =>'http', :host =>'www.example.com', :path => '/',
|
||||
:link_text => 'http://www.example.com/'
|
||||
)
|
||||
# Without http://
|
||||
match(URIChunk, 'www.example.com',
|
||||
:scheme =>'http', :host =>'www.example.com', :link_text => 'www.example.com'
|
||||
)
|
||||
# two parts
|
||||
match(URIChunk, 'example.com',
|
||||
:scheme =>'http',:host =>'example.com', :link_text => 'example.com'
|
||||
)
|
||||
# "unusual" base domain (was a bug in an early version)
|
||||
match(URIChunk, 'http://example.com.au/',
|
||||
:scheme =>'http', :host =>'example.com.au', :link_text => 'http://example.com.au/'
|
||||
)
|
||||
# "unusual" base domain without http://
|
||||
match(URIChunk, 'example.com.au',
|
||||
:scheme =>'http', :host =>'example.com.au', :link_text => 'example.com.au'
|
||||
)
|
||||
# Another "unusual" base domain
|
||||
match(URIChunk, 'http://www.example.co.uk/',
|
||||
:scheme =>'http', :host =>'www.example.co.uk',
|
||||
:link_text => 'http://www.example.co.uk/'
|
||||
)
|
||||
match(URIChunk, 'example.co.uk',
|
||||
:scheme =>'http', :host =>'example.co.uk', :link_text => 'example.co.uk'
|
||||
)
|
||||
# With some path at the end
|
||||
match(URIChunk, 'http://moinmoin.wikiwikiweb.de/HelpOnNavigation',
|
||||
:scheme => 'http', :host => 'moinmoin.wikiwikiweb.de', :path => '/HelpOnNavigation',
|
||||
:link_text => 'http://moinmoin.wikiwikiweb.de/HelpOnNavigation'
|
||||
)
|
||||
# With some path at the end, and withot http:// prefix
|
||||
match(URIChunk, 'moinmoin.wikiwikiweb.de/HelpOnNavigation',
|
||||
:scheme => 'http', :host => 'moinmoin.wikiwikiweb.de', :path => '/HelpOnNavigation',
|
||||
:link_text => 'moinmoin.wikiwikiweb.de/HelpOnNavigation'
|
||||
)
|
||||
# With a port number
|
||||
match(URIChunk, 'http://www.example.com:80',
|
||||
:scheme =>'http', :host =>'www.example.com', :port => '80', :path => nil,
|
||||
:link_text => 'http://www.example.com:80')
|
||||
# With a port number and a path
|
||||
match(URIChunk, 'http://www.example.com.tw:80/HelpOnNavigation',
|
||||
:scheme =>'http', :host =>'www.example.com.tw', :port => '80', :path => '/HelpOnNavigation',
|
||||
:link_text => 'http://www.example.com.tw:80/HelpOnNavigation')
|
||||
# With a query
|
||||
match(URIChunk, 'http://www.example.com.tw:80/HelpOnNavigation?arg=val',
|
||||
:scheme =>'http', :host =>'www.example.com.tw', :port => '80', :path => '/HelpOnNavigation',
|
||||
:query => 'arg=val',
|
||||
:link_text => 'http://www.example.com.tw:80/HelpOnNavigation?arg=val')
|
||||
# Query with two arguments
|
||||
match(URIChunk, 'http://www.example.com.tw:80/HelpOnNavigation?arg=val&arg2=val2',
|
||||
:scheme =>'http', :host =>'www.example.com.tw', :port => '80', :path => '/HelpOnNavigation',
|
||||
:query => 'arg=val&arg2=val2',
|
||||
:link_text => 'http://www.example.com.tw:80/HelpOnNavigation?arg=val&arg2=val2')
|
||||
# HTTPS
|
||||
match(URIChunk, 'https://www.example.com',
|
||||
:scheme =>'https', :host =>'www.example.com', :port => nil, :path => nil, :query => nil,
|
||||
:link_text => 'https://www.example.com')
|
||||
# FTP
|
||||
match(URIChunk, 'ftp://www.example.com',
|
||||
:scheme =>'ftp', :host =>'www.example.com', :port => nil, :path => nil, :query => nil,
|
||||
:link_text => 'ftp://www.example.com')
|
||||
# mailto
|
||||
match(URIChunk, 'mailto:jdoe123@example.com',
|
||||
:scheme =>'mailto', :host =>'example.com', :port => nil, :path => nil, :query => nil,
|
||||
:user => 'jdoe123', :link_text => 'mailto:jdoe123@example.com')
|
||||
# something nonexistant
|
||||
match(URIChunk, 'foobar://www.example.com',
|
||||
:scheme =>'foobar', :host =>'www.example.com', :port => nil, :path => nil, :query => nil,
|
||||
:link_text => 'foobar://www.example.com')
|
||||
|
||||
# Soap opera (the most complex case imaginable... well, not really, there should be more evil)
|
||||
match(URIChunk, 'http://www.example.com.tw:80/~jdoe123/Help%20Me%20?arg=val&arg2=val2',
|
||||
:scheme =>'http', :host =>'www.example.com.tw', :port => '80',
|
||||
:path => '/~jdoe123/Help%20Me%20', :query => 'arg=val&arg2=val2',
|
||||
:link_text => 'http://www.example.com.tw:80/~jdoe123/Help%20Me%20?arg=val&arg2=val2')
|
||||
end
|
||||
|
||||
def test_email_uri
|
||||
match(URIChunk, 'mail@example.com',
|
||||
:user => 'mail', :host => 'example.com', :link_text => 'mail@example.com'
|
||||
)
|
||||
end
|
||||
|
||||
def test_non_email
|
||||
# The @ is part of the normal text, but 'example.com' is marked up.
|
||||
match(URIChunk, 'Not an email: @example.com', :user => nil, :uri => 'http://example.com')
|
||||
end
|
||||
|
||||
def test_non_uri
|
||||
assert_conversion_does_not_apply URIChunk, 'httpd.conf'
|
||||
assert_conversion_does_not_apply URIChunk, 'libproxy.so'
|
||||
assert_conversion_does_not_apply URIChunk, 'ld.so.conf'
|
||||
end
|
||||
|
||||
def test_uri_in_text
|
||||
match(URIChunk, 'Go to: http://www.example.com/', :host => 'www.example.com', :path =>'/')
|
||||
match(URIChunk, 'http://www.example.com/ is a link.', :host => 'www.example.com')
|
||||
match(URIChunk,
|
||||
'Email david@loudthinking.com',
|
||||
:scheme =>'mailto', :user =>'david', :host =>'loudthinking.com')
|
||||
# check that trailing punctuation is not included in the hostname
|
||||
match(URIChunk, '"link":http://fake.link.com.', :scheme => 'http', :host => 'fake.link.com')
|
||||
end
|
||||
|
||||
def test_uri_in_parentheses
|
||||
match(URIChunk, 'URI (http://brackets.com.de) in brackets', :host => 'brackets.com.de')
|
||||
match(URIChunk, 'because (as shown at research.net) the results', :host => 'research.net')
|
||||
match(URIChunk,
|
||||
'A wiki (http://wiki.org/wiki.cgi?WhatIsWiki) page',
|
||||
:scheme => 'http', :host => 'wiki.org', :path => '/wiki.cgi', :query => 'WhatIsWiki'
|
||||
)
|
||||
end
|
||||
|
||||
def test_uri_list_item
|
||||
match(
|
||||
URIChunk,
|
||||
'* http://www.btinternet.com/~mail2minh/SonyEricssonP80xPlatform.sis',
|
||||
:path => '/~mail2minh/SonyEricssonP80xPlatform.sis'
|
||||
)
|
||||
end
|
||||
|
||||
def test_interesting_uri_with__comma
|
||||
# Counter-intuitively, this URL matches, but the query part includes the trailing comma.
|
||||
# It has no way to know that the query does not include the comma.
|
||||
match(
|
||||
URIChunk,
|
||||
"This text contains a URL http://someplace.org:8080/~person/stuff.cgi?arg=val, doesn't it?",
|
||||
:scheme => 'http', :host => 'someplace.org', :port => '8080', :path => '/~person/stuff.cgi',
|
||||
:query => 'arg=val,')
|
||||
end
|
||||
|
||||
def test_local_urls
|
||||
# normal
|
||||
match(LocalURIChunk, 'http://perforce:8001/toto.html',
|
||||
:scheme => 'http', :host => 'perforce',
|
||||
:port => '8001', :link_text => 'http://perforce:8001/toto.html')
|
||||
|
||||
# in parentheses
|
||||
match(LocalURIChunk, 'URI (http://localhost:2500) in brackets',
|
||||
:host => 'localhost', :port => '2500')
|
||||
match(LocalURIChunk, 'because (as shown at http://perforce:8001) the results',
|
||||
:host => 'perforce', :port => '8001')
|
||||
match(LocalURIChunk,
|
||||
'A wiki (http://localhost:2500/wiki.cgi?WhatIsWiki) page',
|
||||
:scheme => 'http', :host => 'localhost', :path => '/wiki.cgi',
|
||||
:port => '2500', :query => 'WhatIsWiki')
|
||||
end
|
||||
|
||||
def assert_conversion_does_not_apply(chunk_type, str)
|
||||
processed_str = str.dup
|
||||
URIChunk.apply_to(processed_str)
|
||||
assert_equal(str, processed_str)
|
||||
end
|
||||
|
||||
end
|
||||
#!/bin/env ruby -w
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'chunks/uri'
|
||||
|
||||
class URITest < Test::Unit::TestCase
|
||||
include ChunkMatch
|
||||
|
||||
def test_non_matches
|
||||
assert_conversion_does_not_apply(URIChunk, 'There is no URI here')
|
||||
assert_conversion_does_not_apply(URIChunk,
|
||||
'One gemstone is the garnet:reddish in colour, like ruby')
|
||||
end
|
||||
|
||||
def test_simple_uri
|
||||
# Simplest case
|
||||
match(URIChunk, 'http://www.example.com',
|
||||
:scheme =>'http', :host =>'www.example.com', :path => nil,
|
||||
:link_text => 'http://www.example.com'
|
||||
)
|
||||
# With trailing slash
|
||||
match(URIChunk, 'http://www.example.com/',
|
||||
:scheme =>'http', :host =>'www.example.com', :path => '/',
|
||||
:link_text => 'http://www.example.com/'
|
||||
)
|
||||
# Without http://
|
||||
match(URIChunk, 'www.example.com',
|
||||
:scheme =>'http', :host =>'www.example.com', :link_text => 'www.example.com'
|
||||
)
|
||||
# two parts
|
||||
match(URIChunk, 'example.com',
|
||||
:scheme =>'http',:host =>'example.com', :link_text => 'example.com'
|
||||
)
|
||||
# "unusual" base domain (was a bug in an early version)
|
||||
match(URIChunk, 'http://example.com.au/',
|
||||
:scheme =>'http', :host =>'example.com.au', :link_text => 'http://example.com.au/'
|
||||
)
|
||||
# "unusual" base domain without http://
|
||||
match(URIChunk, 'example.com.au',
|
||||
:scheme =>'http', :host =>'example.com.au', :link_text => 'example.com.au'
|
||||
)
|
||||
# Another "unusual" base domain
|
||||
match(URIChunk, 'http://www.example.co.uk/',
|
||||
:scheme =>'http', :host =>'www.example.co.uk',
|
||||
:link_text => 'http://www.example.co.uk/'
|
||||
)
|
||||
match(URIChunk, 'example.co.uk',
|
||||
:scheme =>'http', :host =>'example.co.uk', :link_text => 'example.co.uk'
|
||||
)
|
||||
# With some path at the end
|
||||
match(URIChunk, 'http://moinmoin.wikiwikiweb.de/HelpOnNavigation',
|
||||
:scheme => 'http', :host => 'moinmoin.wikiwikiweb.de', :path => '/HelpOnNavigation',
|
||||
:link_text => 'http://moinmoin.wikiwikiweb.de/HelpOnNavigation'
|
||||
)
|
||||
# With some path at the end, and withot http:// prefix
|
||||
match(URIChunk, 'moinmoin.wikiwikiweb.de/HelpOnNavigation',
|
||||
:scheme => 'http', :host => 'moinmoin.wikiwikiweb.de', :path => '/HelpOnNavigation',
|
||||
:link_text => 'moinmoin.wikiwikiweb.de/HelpOnNavigation'
|
||||
)
|
||||
# With a port number
|
||||
match(URIChunk, 'http://www.example.com:80',
|
||||
:scheme =>'http', :host =>'www.example.com', :port => '80', :path => nil,
|
||||
:link_text => 'http://www.example.com:80')
|
||||
# With a port number and a path
|
||||
match(URIChunk, 'http://www.example.com.tw:80/HelpOnNavigation',
|
||||
:scheme =>'http', :host =>'www.example.com.tw', :port => '80', :path => '/HelpOnNavigation',
|
||||
:link_text => 'http://www.example.com.tw:80/HelpOnNavigation')
|
||||
# With a query
|
||||
match(URIChunk, 'http://www.example.com.tw:80/HelpOnNavigation?arg=val',
|
||||
:scheme =>'http', :host =>'www.example.com.tw', :port => '80', :path => '/HelpOnNavigation',
|
||||
:query => 'arg=val',
|
||||
:link_text => 'http://www.example.com.tw:80/HelpOnNavigation?arg=val')
|
||||
# Query with two arguments
|
||||
match(URIChunk, 'http://www.example.com.tw:80/HelpOnNavigation?arg=val&arg2=val2',
|
||||
:scheme =>'http', :host =>'www.example.com.tw', :port => '80', :path => '/HelpOnNavigation',
|
||||
:query => 'arg=val&arg2=val2',
|
||||
:link_text => 'http://www.example.com.tw:80/HelpOnNavigation?arg=val&arg2=val2')
|
||||
# HTTPS
|
||||
match(URIChunk, 'https://www.example.com',
|
||||
:scheme =>'https', :host =>'www.example.com', :port => nil, :path => nil, :query => nil,
|
||||
:link_text => 'https://www.example.com')
|
||||
# FTP
|
||||
match(URIChunk, 'ftp://www.example.com',
|
||||
:scheme =>'ftp', :host =>'www.example.com', :port => nil, :path => nil, :query => nil,
|
||||
:link_text => 'ftp://www.example.com')
|
||||
# mailto
|
||||
match(URIChunk, 'mailto:jdoe123@example.com',
|
||||
:scheme =>'mailto', :host =>'example.com', :port => nil, :path => nil, :query => nil,
|
||||
:user => 'jdoe123', :link_text => 'mailto:jdoe123@example.com')
|
||||
# something nonexistant
|
||||
match(URIChunk, 'foobar://www.example.com',
|
||||
:scheme =>'foobar', :host =>'www.example.com', :port => nil, :path => nil, :query => nil,
|
||||
:link_text => 'foobar://www.example.com')
|
||||
|
||||
# Soap opera (the most complex case imaginable... well, not really, there should be more evil)
|
||||
match(URIChunk, 'http://www.example.com.tw:80/~jdoe123/Help%20Me%20?arg=val&arg2=val2',
|
||||
:scheme =>'http', :host =>'www.example.com.tw', :port => '80',
|
||||
:path => '/~jdoe123/Help%20Me%20', :query => 'arg=val&arg2=val2',
|
||||
:link_text => 'http://www.example.com.tw:80/~jdoe123/Help%20Me%20?arg=val&arg2=val2')
|
||||
end
|
||||
|
||||
def test_email_uri
|
||||
match(URIChunk, 'mail@example.com',
|
||||
:user => 'mail', :host => 'example.com', :link_text => 'mail@example.com'
|
||||
)
|
||||
end
|
||||
|
||||
def test_non_email
|
||||
# The @ is part of the normal text, but 'example.com' is marked up.
|
||||
match(URIChunk, 'Not an email: @example.com', :user => nil, :uri => 'http://example.com')
|
||||
end
|
||||
|
||||
def test_non_uri
|
||||
assert_conversion_does_not_apply URIChunk, 'httpd.conf'
|
||||
assert_conversion_does_not_apply URIChunk, 'libproxy.so'
|
||||
assert_conversion_does_not_apply URIChunk, 'ld.so.conf'
|
||||
end
|
||||
|
||||
def test_uri_in_text
|
||||
match(URIChunk, 'Go to: http://www.example.com/', :host => 'www.example.com', :path =>'/')
|
||||
match(URIChunk, 'http://www.example.com/ is a link.', :host => 'www.example.com')
|
||||
match(URIChunk,
|
||||
'Email david@loudthinking.com',
|
||||
:scheme =>'mailto', :user =>'david', :host =>'loudthinking.com')
|
||||
# check that trailing punctuation is not included in the hostname
|
||||
match(URIChunk, '"link":http://fake.link.com.', :scheme => 'http', :host => 'fake.link.com')
|
||||
end
|
||||
|
||||
def test_uri_in_parentheses
|
||||
match(URIChunk, 'URI (http://brackets.com.de) in brackets', :host => 'brackets.com.de')
|
||||
match(URIChunk, 'because (as shown at research.net) the results', :host => 'research.net')
|
||||
match(URIChunk,
|
||||
'A wiki (http://wiki.org/wiki.cgi?WhatIsWiki) page',
|
||||
:scheme => 'http', :host => 'wiki.org', :path => '/wiki.cgi', :query => 'WhatIsWiki'
|
||||
)
|
||||
end
|
||||
|
||||
def test_uri_list_item
|
||||
match(
|
||||
URIChunk,
|
||||
'* http://www.btinternet.com/~mail2minh/SonyEricssonP80xPlatform.sis',
|
||||
:path => '/~mail2minh/SonyEricssonP80xPlatform.sis'
|
||||
)
|
||||
end
|
||||
|
||||
def test_interesting_uri_with__comma
|
||||
# Counter-intuitively, this URL matches, but the query part includes the trailing comma.
|
||||
# It has no way to know that the query does not include the comma.
|
||||
match(
|
||||
URIChunk,
|
||||
"This text contains a URL http://someplace.org:8080/~person/stuff.cgi?arg=val, doesn't it?",
|
||||
:scheme => 'http', :host => 'someplace.org', :port => '8080', :path => '/~person/stuff.cgi',
|
||||
:query => 'arg=val,')
|
||||
end
|
||||
|
||||
def test_local_urls
|
||||
# normal
|
||||
match(LocalURIChunk, 'http://perforce:8001/toto.html',
|
||||
:scheme => 'http', :host => 'perforce',
|
||||
:port => '8001', :link_text => 'http://perforce:8001/toto.html')
|
||||
|
||||
# in parentheses
|
||||
match(LocalURIChunk, 'URI (http://localhost:2500) in brackets',
|
||||
:host => 'localhost', :port => '2500')
|
||||
match(LocalURIChunk, 'because (as shown at http://perforce:8001) the results',
|
||||
:host => 'perforce', :port => '8001')
|
||||
match(LocalURIChunk,
|
||||
'A wiki (http://localhost:2500/wiki.cgi?WhatIsWiki) page',
|
||||
:scheme => 'http', :host => 'localhost', :path => '/wiki.cgi',
|
||||
:port => '2500', :query => 'WhatIsWiki')
|
||||
end
|
||||
|
||||
def assert_conversion_does_not_apply(chunk_type, str)
|
||||
processed_str = str.dup
|
||||
URIChunk.apply_to(processed_str)
|
||||
assert_equal(str, processed_str)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,92 +1,92 @@
|
|||
#!/bin/env ruby -w
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'url_rewriting_hack'
|
||||
|
||||
class UrlRewritingHackTest < Test::Unit::TestCase
|
||||
|
||||
def test_parse_uri
|
||||
assert_equal({:controller => 'wiki', :action => 'x', :web => nil},
|
||||
DispatchServlet.parse_uri('/x/'))
|
||||
assert_equal({:web => 'x', :controller => 'wiki', :action => 'y'},
|
||||
DispatchServlet.parse_uri('/x/y'))
|
||||
assert_equal({:web => 'x', :controller => 'wiki', :action => 'y'},
|
||||
DispatchServlet.parse_uri('/x/y/'))
|
||||
assert_equal({:web => 'x', :controller => 'wiki', :action => 'y', :id => 'z'},
|
||||
DispatchServlet.parse_uri('/x/y/z'))
|
||||
assert_equal({:web => 'x', :controller => 'wiki', :action => 'y', :id => 'z'},
|
||||
DispatchServlet.parse_uri('/x/y/z/'))
|
||||
end
|
||||
|
||||
def test_parse_uri_approot
|
||||
assert_equal({:controller => 'wiki', :action => 'index', :web => nil},
|
||||
DispatchServlet.parse_uri('/wiki/'))
|
||||
end
|
||||
|
||||
def test_parse_uri_interestng_cases
|
||||
|
||||
assert_equal({:web => '_veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeery-long_web_',
|
||||
:controller => 'wiki',
|
||||
:action => 'an_action', :id => 'HomePage'
|
||||
},
|
||||
DispatchServlet.parse_uri(
|
||||
'/_veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeery-long_web_/an_action/HomePage')
|
||||
)
|
||||
|
||||
assert_equal false, DispatchServlet.parse_uri('')
|
||||
assert_equal false, DispatchServlet.parse_uri('//')
|
||||
assert_equal false, DispatchServlet.parse_uri('web')
|
||||
end
|
||||
|
||||
def test_parse_uri_liberal_with_pagenames
|
||||
|
||||
assert_equal({:controller => 'wiki', :web => 'web', :action => 'show', :id => '$HOME_PAGE'},
|
||||
DispatchServlet.parse_uri('/web/show/$HOME_PAGE'))
|
||||
|
||||
assert_equal({:controller => 'wiki', :web => 'web', :action => 'show',
|
||||
:id => 'HomePage/something_else'},
|
||||
DispatchServlet.parse_uri('/web/show/HomePage/something_else'))
|
||||
|
||||
assert_equal({:controller => 'wiki', :web => 'web', :action => 'show',
|
||||
:id => 'HomePage?arg1=value1&arg2=value2'},
|
||||
DispatchServlet.parse_uri('/web/show/HomePage?arg1=value1&arg2=value2'))
|
||||
|
||||
assert_equal({:controller => 'wiki', :web => 'web', :action => 'show',
|
||||
:id => 'Page+With+Spaces'},
|
||||
DispatchServlet.parse_uri('/web/show/Page+With+Spaces'))
|
||||
end
|
||||
|
||||
def test_url_rewriting
|
||||
request = ActionController::TestRequest.new
|
||||
ur = ActionController::UrlRewriter.new(request, 'wiki', 'show')
|
||||
|
||||
assert_equal 'http://test.host/myweb/myaction',
|
||||
ur.rewrite(:web => 'myweb', :controller => 'wiki', :action => 'myaction')
|
||||
|
||||
assert_equal 'http://test.host/myOtherWeb/',
|
||||
ur.rewrite(:web => 'myOtherWeb', :controller => 'wiki')
|
||||
|
||||
assert_equal 'http://test.host/myaction',
|
||||
ur.rewrite(:controller => 'wiki', :action => 'myaction')
|
||||
|
||||
assert_equal 'http://test.host/',
|
||||
ur.rewrite(:controller => 'wiki')
|
||||
end
|
||||
|
||||
def test_controller_mapping
|
||||
request = ActionController::TestRequest.new
|
||||
ur = ActionController::UrlRewriter.new(request, 'wiki', 'show')
|
||||
|
||||
assert_equal 'http://test.host/file',
|
||||
ur.rewrite(:controller => 'file', :action => 'file')
|
||||
assert_equal 'http://test.host/pic/abc.jpg',
|
||||
ur.rewrite(:controller => 'file', :action => 'pic', :id => 'abc.jpg')
|
||||
assert_equal 'http://test.host/web/pic/abc.jpg',
|
||||
ur.rewrite(:web => 'web', :controller => 'file', :action => 'pic', :id => 'abc.jpg')
|
||||
|
||||
# default option is wiki
|
||||
assert_equal 'http://test.host/unknown_action',
|
||||
ur.rewrite(:controller => 'wiki', :action => 'unknown_action')
|
||||
end
|
||||
|
||||
#!/bin/env ruby -w
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'url_rewriting_hack'
|
||||
|
||||
class UrlRewritingHackTest < Test::Unit::TestCase
|
||||
|
||||
def test_parse_uri
|
||||
assert_equal({:controller => 'wiki', :action => 'x', :web => nil},
|
||||
DispatchServlet.parse_uri('/x/'))
|
||||
assert_equal({:web => 'x', :controller => 'wiki', :action => 'y'},
|
||||
DispatchServlet.parse_uri('/x/y'))
|
||||
assert_equal({:web => 'x', :controller => 'wiki', :action => 'y'},
|
||||
DispatchServlet.parse_uri('/x/y/'))
|
||||
assert_equal({:web => 'x', :controller => 'wiki', :action => 'y', :id => 'z'},
|
||||
DispatchServlet.parse_uri('/x/y/z'))
|
||||
assert_equal({:web => 'x', :controller => 'wiki', :action => 'y', :id => 'z'},
|
||||
DispatchServlet.parse_uri('/x/y/z/'))
|
||||
end
|
||||
|
||||
def test_parse_uri_approot
|
||||
assert_equal({:controller => 'wiki', :action => 'index', :web => nil},
|
||||
DispatchServlet.parse_uri('/wiki/'))
|
||||
end
|
||||
|
||||
def test_parse_uri_interestng_cases
|
||||
|
||||
assert_equal({:web => '_veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeery-long_web_',
|
||||
:controller => 'wiki',
|
||||
:action => 'an_action', :id => 'HomePage'
|
||||
},
|
||||
DispatchServlet.parse_uri(
|
||||
'/_veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeery-long_web_/an_action/HomePage')
|
||||
)
|
||||
|
||||
assert_equal false, DispatchServlet.parse_uri('')
|
||||
assert_equal false, DispatchServlet.parse_uri('//')
|
||||
assert_equal false, DispatchServlet.parse_uri('web')
|
||||
end
|
||||
|
||||
def test_parse_uri_liberal_with_pagenames
|
||||
|
||||
assert_equal({:controller => 'wiki', :web => 'web', :action => 'show', :id => '$HOME_PAGE'},
|
||||
DispatchServlet.parse_uri('/web/show/$HOME_PAGE'))
|
||||
|
||||
assert_equal({:controller => 'wiki', :web => 'web', :action => 'show',
|
||||
:id => 'HomePage/something_else'},
|
||||
DispatchServlet.parse_uri('/web/show/HomePage/something_else'))
|
||||
|
||||
assert_equal({:controller => 'wiki', :web => 'web', :action => 'show',
|
||||
:id => 'HomePage?arg1=value1&arg2=value2'},
|
||||
DispatchServlet.parse_uri('/web/show/HomePage?arg1=value1&arg2=value2'))
|
||||
|
||||
assert_equal({:controller => 'wiki', :web => 'web', :action => 'show',
|
||||
:id => 'Page+With+Spaces'},
|
||||
DispatchServlet.parse_uri('/web/show/Page+With+Spaces'))
|
||||
end
|
||||
|
||||
def test_url_rewriting
|
||||
request = ActionController::TestRequest.new
|
||||
ur = ActionController::UrlRewriter.new(request, 'wiki', 'show')
|
||||
|
||||
assert_equal 'http://test.host/myweb/myaction',
|
||||
ur.rewrite(:web => 'myweb', :controller => 'wiki', :action => 'myaction')
|
||||
|
||||
assert_equal 'http://test.host/myOtherWeb/',
|
||||
ur.rewrite(:web => 'myOtherWeb', :controller => 'wiki')
|
||||
|
||||
assert_equal 'http://test.host/myaction',
|
||||
ur.rewrite(:controller => 'wiki', :action => 'myaction')
|
||||
|
||||
assert_equal 'http://test.host/',
|
||||
ur.rewrite(:controller => 'wiki')
|
||||
end
|
||||
|
||||
def test_controller_mapping
|
||||
request = ActionController::TestRequest.new
|
||||
ur = ActionController::UrlRewriter.new(request, 'wiki', 'show')
|
||||
|
||||
assert_equal 'http://test.host/file',
|
||||
ur.rewrite(:controller => 'file', :action => 'file')
|
||||
assert_equal 'http://test.host/pic/abc.jpg',
|
||||
ur.rewrite(:controller => 'file', :action => 'pic', :id => 'abc.jpg')
|
||||
assert_equal 'http://test.host/web/pic/abc.jpg',
|
||||
ur.rewrite(:web => 'web', :controller => 'file', :action => 'pic', :id => 'abc.jpg')
|
||||
|
||||
# default option is wiki
|
||||
assert_equal 'http://test.host/unknown_action',
|
||||
ur.rewrite(:controller => 'wiki', :action => 'unknown_action')
|
||||
end
|
||||
|
||||
end
|
|
@ -1,130 +1,130 @@
|
|||
#!/bin/env ruby -w
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'wiki_service'
|
||||
|
||||
class WebTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@web = Web.new nil, 'Instiki', 'instiki'
|
||||
end
|
||||
|
||||
def test_wiki_word_linking
|
||||
@web.add_page(Page.new(@web, 'SecondPage', 'Yo, yo. Have you EverBeenHated', Time.now,
|
||||
'DavidHeinemeierHansson'))
|
||||
|
||||
assert_equal('<p>Yo, yo. Have you <span class="newWikiWord">Ever Been Hated' +
|
||||
'<a href="../show/EverBeenHated">?</a></span></p>',
|
||||
@web.pages["SecondPage"].display_content)
|
||||
|
||||
@web.add_page(Page.new(@web, 'EverBeenHated', 'Yo, yo. Have you EverBeenHated', Time.now,
|
||||
'DavidHeinemeierHansson'))
|
||||
assert_equal('<p>Yo, yo. Have you <a class="existingWikiWord" ' +
|
||||
'href="../show/EverBeenHated">Ever Been Hated</a></p>',
|
||||
@web.pages['SecondPage'].display_content)
|
||||
end
|
||||
|
||||
def test_pages_by_revision
|
||||
add_sample_pages
|
||||
assert_equal 'EverBeenHated', @web.select.by_revision.first.name
|
||||
end
|
||||
|
||||
def test_pages_by_match
|
||||
add_sample_pages
|
||||
assert_equal 2, @web.select { |page| page.content =~ /me/i }.length
|
||||
assert_equal 1, @web.select { |page| page.content =~ /Who/i }.length
|
||||
assert_equal 0, @web.select { |page| page.content =~ /none/i }.length
|
||||
end
|
||||
|
||||
def test_references
|
||||
add_sample_pages
|
||||
assert_equal 1, @web.select.pages_that_reference('EverBeenHated').length
|
||||
assert_equal 0, @web.select.pages_that_reference('EverBeenInLove').length
|
||||
end
|
||||
|
||||
def test_delete
|
||||
add_sample_pages
|
||||
assert_equal 2, @web.pages.length
|
||||
@web.remove_pages([ @web.pages['EverBeenInLove'] ])
|
||||
assert_equal 1, @web.pages.length
|
||||
end
|
||||
|
||||
def test_make_link
|
||||
add_sample_pages
|
||||
|
||||
existing_page_wiki_url =
|
||||
'<a class="existingWikiWord" href="../show/EverBeenInLove">Ever Been In Love</a>'
|
||||
existing_page_published_url =
|
||||
'<a class="existingWikiWord" href="../published/EverBeenInLove">Ever Been In Love</a>'
|
||||
existing_page_static_url =
|
||||
'<a class="existingWikiWord" href="EverBeenInLove.html">Ever Been In Love</a>'
|
||||
new_page_wiki_url =
|
||||
'<span class="newWikiWord">Unknown Word<a href="../show/UnknownWord">?</a></span>'
|
||||
new_page_published_url =
|
||||
new_page_static_url =
|
||||
'<span class="newWikiWord">Unknown Word</span>'
|
||||
|
||||
# no options
|
||||
assert_equal existing_page_wiki_url, @web.make_link('EverBeenInLove')
|
||||
|
||||
# :mode => :export
|
||||
assert_equal existing_page_static_url, @web.make_link('EverBeenInLove', nil, :mode => :export)
|
||||
|
||||
# :mode => :publish
|
||||
assert_equal existing_page_published_url,
|
||||
@web.make_link('EverBeenInLove', nil, :mode => :publish)
|
||||
|
||||
# new page, no options
|
||||
assert_equal new_page_wiki_url, @web.make_link('UnknownWord')
|
||||
|
||||
# new page, :mode => :export
|
||||
assert_equal new_page_static_url, @web.make_link('UnknownWord', nil, :mode => :export)
|
||||
|
||||
# new page, :mode => :publish
|
||||
assert_equal new_page_published_url, @web.make_link('UnknownWord', nil, :mode => :publish)
|
||||
|
||||
# Escaping special characters in the name
|
||||
assert_equal(
|
||||
'<span class="newWikiWord">Smith & Wesson<a href="../show/Smith+%26+Wesson">?</a></span>',
|
||||
@web.make_link('Smith & Wesson'))
|
||||
|
||||
# optionally using text as the link text
|
||||
assert_equal(
|
||||
existing_page_published_url.sub(/>Ever Been In Love</, ">Haven't you ever been in love?<"),
|
||||
@web.make_link('EverBeenInLove', "Haven't you ever been in love?", :mode => :publish))
|
||||
|
||||
end
|
||||
|
||||
def test_initialize
|
||||
wiki_stub = Object.new
|
||||
|
||||
web = Web.new(wiki_stub, 'Wiki2', 'wiki2', '123')
|
||||
|
||||
assert_equal wiki_stub, web.wiki
|
||||
assert_equal 'Wiki2', web.name
|
||||
assert_equal 'wiki2', web.address
|
||||
assert_equal '123', web.password
|
||||
|
||||
# new web should be set for maximum features enabled
|
||||
assert_equal :textile, web.markup
|
||||
assert_equal '008B26', web.color
|
||||
assert !web.safe_mode
|
||||
assert_equal {}, web.pages
|
||||
assert web.allow_uploads
|
||||
assert_equal @wiki, web.parent_wiki
|
||||
assert_nil web.additional_style
|
||||
assert !web.published
|
||||
assert !web.brackets_only
|
||||
assert !web.count_pages
|
||||
assert web.allow_uploads
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def add_sample_pages
|
||||
@web.add_page(Page.new(@web, 'EverBeenInLove', 'Who am I me',
|
||||
Time.local(2004, 4, 4, 16, 50), 'DavidHeinemeierHansson'))
|
||||
@web.add_page(Page.new(@web, 'EverBeenHated', 'I am me EverBeenHated',
|
||||
Time.local(2004, 4, 4, 16, 51), 'DavidHeinemeierHansson'))
|
||||
end
|
||||
#!/bin/env ruby -w
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'wiki_service'
|
||||
|
||||
class WebTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@web = Web.new nil, 'Instiki', 'instiki'
|
||||
end
|
||||
|
||||
def test_wiki_word_linking
|
||||
@web.add_page(Page.new(@web, 'SecondPage', 'Yo, yo. Have you EverBeenHated', Time.now,
|
||||
'DavidHeinemeierHansson'))
|
||||
|
||||
assert_equal('<p>Yo, yo. Have you <span class="newWikiWord">Ever Been Hated' +
|
||||
'<a href="../show/EverBeenHated">?</a></span></p>',
|
||||
@web.pages["SecondPage"].display_content)
|
||||
|
||||
@web.add_page(Page.new(@web, 'EverBeenHated', 'Yo, yo. Have you EverBeenHated', Time.now,
|
||||
'DavidHeinemeierHansson'))
|
||||
assert_equal('<p>Yo, yo. Have you <a class="existingWikiWord" ' +
|
||||
'href="../show/EverBeenHated">Ever Been Hated</a></p>',
|
||||
@web.pages['SecondPage'].display_content)
|
||||
end
|
||||
|
||||
def test_pages_by_revision
|
||||
add_sample_pages
|
||||
assert_equal 'EverBeenHated', @web.select.by_revision.first.name
|
||||
end
|
||||
|
||||
def test_pages_by_match
|
||||
add_sample_pages
|
||||
assert_equal 2, @web.select { |page| page.content =~ /me/i }.length
|
||||
assert_equal 1, @web.select { |page| page.content =~ /Who/i }.length
|
||||
assert_equal 0, @web.select { |page| page.content =~ /none/i }.length
|
||||
end
|
||||
|
||||
def test_references
|
||||
add_sample_pages
|
||||
assert_equal 1, @web.select.pages_that_reference('EverBeenHated').length
|
||||
assert_equal 0, @web.select.pages_that_reference('EverBeenInLove').length
|
||||
end
|
||||
|
||||
def test_delete
|
||||
add_sample_pages
|
||||
assert_equal 2, @web.pages.length
|
||||
@web.remove_pages([ @web.pages['EverBeenInLove'] ])
|
||||
assert_equal 1, @web.pages.length
|
||||
end
|
||||
|
||||
def test_make_link
|
||||
add_sample_pages
|
||||
|
||||
existing_page_wiki_url =
|
||||
'<a class="existingWikiWord" href="../show/EverBeenInLove">Ever Been In Love</a>'
|
||||
existing_page_published_url =
|
||||
'<a class="existingWikiWord" href="../published/EverBeenInLove">Ever Been In Love</a>'
|
||||
existing_page_static_url =
|
||||
'<a class="existingWikiWord" href="EverBeenInLove.html">Ever Been In Love</a>'
|
||||
new_page_wiki_url =
|
||||
'<span class="newWikiWord">Unknown Word<a href="../show/UnknownWord">?</a></span>'
|
||||
new_page_published_url =
|
||||
new_page_static_url =
|
||||
'<span class="newWikiWord">Unknown Word</span>'
|
||||
|
||||
# no options
|
||||
assert_equal existing_page_wiki_url, @web.make_link('EverBeenInLove')
|
||||
|
||||
# :mode => :export
|
||||
assert_equal existing_page_static_url, @web.make_link('EverBeenInLove', nil, :mode => :export)
|
||||
|
||||
# :mode => :publish
|
||||
assert_equal existing_page_published_url,
|
||||
@web.make_link('EverBeenInLove', nil, :mode => :publish)
|
||||
|
||||
# new page, no options
|
||||
assert_equal new_page_wiki_url, @web.make_link('UnknownWord')
|
||||
|
||||
# new page, :mode => :export
|
||||
assert_equal new_page_static_url, @web.make_link('UnknownWord', nil, :mode => :export)
|
||||
|
||||
# new page, :mode => :publish
|
||||
assert_equal new_page_published_url, @web.make_link('UnknownWord', nil, :mode => :publish)
|
||||
|
||||
# Escaping special characters in the name
|
||||
assert_equal(
|
||||
'<span class="newWikiWord">Smith & Wesson<a href="../show/Smith+%26+Wesson">?</a></span>',
|
||||
@web.make_link('Smith & Wesson'))
|
||||
|
||||
# optionally using text as the link text
|
||||
assert_equal(
|
||||
existing_page_published_url.sub(/>Ever Been In Love</, ">Haven't you ever been in love?<"),
|
||||
@web.make_link('EverBeenInLove', "Haven't you ever been in love?", :mode => :publish))
|
||||
|
||||
end
|
||||
|
||||
def test_initialize
|
||||
wiki_stub = Object.new
|
||||
|
||||
web = Web.new(wiki_stub, 'Wiki2', 'wiki2', '123')
|
||||
|
||||
assert_equal wiki_stub, web.wiki
|
||||
assert_equal 'Wiki2', web.name
|
||||
assert_equal 'wiki2', web.address
|
||||
assert_equal '123', web.password
|
||||
|
||||
# new web should be set for maximum features enabled
|
||||
assert_equal :textile, web.markup
|
||||
assert_equal '008B26', web.color
|
||||
assert !web.safe_mode
|
||||
assert_equal {}, web.pages
|
||||
assert web.allow_uploads
|
||||
assert_equal @wiki, web.parent_wiki
|
||||
assert_nil web.additional_style
|
||||
assert !web.published
|
||||
assert !web.brackets_only
|
||||
assert !web.count_pages
|
||||
assert web.allow_uploads
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def add_sample_pages
|
||||
@web.add_page(Page.new(@web, 'EverBeenInLove', 'Who am I me',
|
||||
Time.local(2004, 4, 4, 16, 50), 'DavidHeinemeierHansson'))
|
||||
@web.add_page(Page.new(@web, 'EverBeenHated', 'I am me EverBeenHated',
|
||||
Time.local(2004, 4, 4, 16, 51), 'DavidHeinemeierHansson'))
|
||||
end
|
||||
end
|
|
@ -1,116 +1,116 @@
|
|||
#!/bin/env ruby -w
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'wiki_service'
|
||||
require 'fileutils'
|
||||
|
||||
class WikiServiceTest < Test::Unit::TestCase
|
||||
|
||||
# Clean the test storage directory before the run
|
||||
unless defined? @@storage_cleaned
|
||||
FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/*.command_log'])
|
||||
FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/*.snapshot'])
|
||||
FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/*.tex'])
|
||||
FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/*.zip'])
|
||||
FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/*.pdf'])
|
||||
FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/instiki/*'])
|
||||
@@cleaned_storage = true
|
||||
WikiService.instance.setup('pswd', 'Wiki', 'wiki')
|
||||
end
|
||||
|
||||
def setup
|
||||
@s = WikiService.instance
|
||||
@s.create_web 'Instiki', 'instiki'
|
||||
@web = @s.webs['instiki']
|
||||
end
|
||||
|
||||
def teardown
|
||||
@s.delete_web 'instiki'
|
||||
end
|
||||
|
||||
def test_read_write_page
|
||||
@s.write_page 'instiki', 'FirstPage', "Electric shocks, I love 'em",
|
||||
Time.now, 'DavidHeinemeierHansson'
|
||||
assert_equal "Electric shocks, I love 'em", @s.read_page('instiki', 'FirstPage').content
|
||||
end
|
||||
|
||||
def test_read_only_operations
|
||||
@s.write_page 'instiki', 'TestReadOnlyOperations', 'Read only operations dont change the' +
|
||||
'state of any object, and therefore should not be logged by Madeleine!',
|
||||
Time.now, 'AlexeyVerkhovsky'
|
||||
|
||||
assert_doesnt_change_state_or_log :authenticate, 'pswd'
|
||||
assert_doesnt_change_state_or_log :read_page, 'instiki', 'TestReadOnlyOperations'
|
||||
assert_doesnt_change_state_or_log :setup?
|
||||
assert_doesnt_change_state_or_log :webs
|
||||
|
||||
@s.write_page 'instiki', 'FirstPage', "Electric shocks, I love 'em",
|
||||
Time.now, 'DavidHeinemeierHansson'
|
||||
assert_equal "Electric shocks, I love 'em", @s.read_page('instiki', 'FirstPage').content
|
||||
end
|
||||
|
||||
def test_aborted_transaction
|
||||
@s.write_page 'instiki', 'FirstPage', "Electric shocks, I love 'em",
|
||||
10.minutes.ago, 'DavidHeinemeierHansson'
|
||||
|
||||
assert_doesnt_change_state('revise_page with unchanged content') {
|
||||
begin
|
||||
@s.revise_page 'instiki', 'FirstPage', "Electric shocks, I love 'em",
|
||||
Time.now, 'DavidHeinemeierHansson'
|
||||
fail 'Expected Instiki::ValidationError not raised'
|
||||
rescue Instiki::ValidationError
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def test_file_yard
|
||||
file_yard = @s.file_yard(@web)
|
||||
assert_equal FileYard, file_yard.class
|
||||
assert_equal(@s.storage_path + '/instiki', file_yard.files_path)
|
||||
end
|
||||
|
||||
|
||||
# Checks that a method call or a block doesn;t change the persisted state of the wiki
|
||||
# Usage:
|
||||
# assert_doesnt_change_state :read_page, 'instiki', 'TestReadOnlyOperations'
|
||||
# or
|
||||
# assert_doesnt_change_state {|wiki| wiki.webs}
|
||||
|
||||
def assert_doesnt_change_state(method, *args, &block)
|
||||
_assert_doesnt_change_state(including_command_log = false, method, *args, &block)
|
||||
end
|
||||
|
||||
# Same as assert_doesnt_change_state, but also asserts that no vommand log is generated
|
||||
def assert_doesnt_change_state_or_log(method, *args, &block)
|
||||
_assert_doesnt_change_state(including_command_log = true, method, *args, &block)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def _assert_doesnt_change_state(including_log, method, *args)
|
||||
WikiService.snapshot
|
||||
last_snapshot_before = last_snapshot
|
||||
|
||||
if block_given?
|
||||
yield @s
|
||||
else
|
||||
@s.send(method, *args)
|
||||
end
|
||||
|
||||
if including_log
|
||||
command_logs = Dir[RAILS_ROOT + 'storage/test/*.command_log']
|
||||
assert command_logs.empty?, "Calls to #{method} should not be logged"
|
||||
end
|
||||
|
||||
last_snapshot_after = last_snapshot
|
||||
assert last_snapshot_before == last_snapshot_after,
|
||||
'Calls to #{method} should not change the state of any persisted object'
|
||||
end
|
||||
|
||||
def last_snapshot
|
||||
snapshots = Dir[RAILS_ROOT + '/storage/test/*.snapshot']
|
||||
assert !snapshots.empty?, "No snapshots found at #{RAILS_ROOT}/storage/test/"
|
||||
File.read(snapshots.last)
|
||||
end
|
||||
|
||||
end
|
||||
#!/bin/env ruby -w
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'wiki_service'
|
||||
require 'fileutils'
|
||||
|
||||
class WikiServiceTest < Test::Unit::TestCase
|
||||
|
||||
# Clean the test storage directory before the run
|
||||
unless defined? @@storage_cleaned
|
||||
FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/*.command_log'])
|
||||
FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/*.snapshot'])
|
||||
FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/*.tex'])
|
||||
FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/*.zip'])
|
||||
FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/*.pdf'])
|
||||
FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/instiki/*'])
|
||||
@@cleaned_storage = true
|
||||
WikiService.instance.setup('pswd', 'Wiki', 'wiki')
|
||||
end
|
||||
|
||||
def setup
|
||||
@s = WikiService.instance
|
||||
@s.create_web 'Instiki', 'instiki'
|
||||
@web = @s.webs['instiki']
|
||||
end
|
||||
|
||||
def teardown
|
||||
@s.delete_web 'instiki'
|
||||
end
|
||||
|
||||
def test_read_write_page
|
||||
@s.write_page 'instiki', 'FirstPage', "Electric shocks, I love 'em",
|
||||
Time.now, 'DavidHeinemeierHansson'
|
||||
assert_equal "Electric shocks, I love 'em", @s.read_page('instiki', 'FirstPage').content
|
||||
end
|
||||
|
||||
def test_read_only_operations
|
||||
@s.write_page 'instiki', 'TestReadOnlyOperations', 'Read only operations dont change the' +
|
||||
'state of any object, and therefore should not be logged by Madeleine!',
|
||||
Time.now, 'AlexeyVerkhovsky'
|
||||
|
||||
assert_doesnt_change_state_or_log :authenticate, 'pswd'
|
||||
assert_doesnt_change_state_or_log :read_page, 'instiki', 'TestReadOnlyOperations'
|
||||
assert_doesnt_change_state_or_log :setup?
|
||||
assert_doesnt_change_state_or_log :webs
|
||||
|
||||
@s.write_page 'instiki', 'FirstPage', "Electric shocks, I love 'em",
|
||||
Time.now, 'DavidHeinemeierHansson'
|
||||
assert_equal "Electric shocks, I love 'em", @s.read_page('instiki', 'FirstPage').content
|
||||
end
|
||||
|
||||
def test_aborted_transaction
|
||||
@s.write_page 'instiki', 'FirstPage', "Electric shocks, I love 'em",
|
||||
10.minutes.ago, 'DavidHeinemeierHansson'
|
||||
|
||||
assert_doesnt_change_state('revise_page with unchanged content') {
|
||||
begin
|
||||
@s.revise_page 'instiki', 'FirstPage', "Electric shocks, I love 'em",
|
||||
Time.now, 'DavidHeinemeierHansson'
|
||||
fail 'Expected Instiki::ValidationError not raised'
|
||||
rescue Instiki::ValidationError
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def test_file_yard
|
||||
file_yard = @s.file_yard(@web)
|
||||
assert_equal FileYard, file_yard.class
|
||||
assert_equal(@s.storage_path + '/instiki', file_yard.files_path)
|
||||
end
|
||||
|
||||
|
||||
# Checks that a method call or a block doesn;t change the persisted state of the wiki
|
||||
# Usage:
|
||||
# assert_doesnt_change_state :read_page, 'instiki', 'TestReadOnlyOperations'
|
||||
# or
|
||||
# assert_doesnt_change_state {|wiki| wiki.webs}
|
||||
|
||||
def assert_doesnt_change_state(method, *args, &block)
|
||||
_assert_doesnt_change_state(including_command_log = false, method, *args, &block)
|
||||
end
|
||||
|
||||
# Same as assert_doesnt_change_state, but also asserts that no vommand log is generated
|
||||
def assert_doesnt_change_state_or_log(method, *args, &block)
|
||||
_assert_doesnt_change_state(including_command_log = true, method, *args, &block)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def _assert_doesnt_change_state(including_log, method, *args)
|
||||
WikiService.snapshot
|
||||
last_snapshot_before = last_snapshot
|
||||
|
||||
if block_given?
|
||||
yield @s
|
||||
else
|
||||
@s.send(method, *args)
|
||||
end
|
||||
|
||||
if including_log
|
||||
command_logs = Dir[RAILS_ROOT + 'storage/test/*.command_log']
|
||||
assert command_logs.empty?, "Calls to #{method} should not be logged"
|
||||
end
|
||||
|
||||
last_snapshot_after = last_snapshot
|
||||
assert last_snapshot_before == last_snapshot_after,
|
||||
'Calls to #{method} should not change the state of any persisted object'
|
||||
end
|
||||
|
||||
def last_snapshot
|
||||
snapshots = Dir[RAILS_ROOT + '/storage/test/*.snapshot']
|
||||
assert !snapshots.empty?, "No snapshots found at #{RAILS_ROOT}/storage/test/"
|
||||
File.read(snapshots.last)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
#!/bin/env ruby -w
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'wiki_words'
|
||||
|
||||
class WikiWordsTest < Test::Unit::TestCase
|
||||
|
||||
def test_utf8_characters_in_wiki_word
|
||||
assert_equal "Æåle Øen", WikiWords.separate("ÆåleØen")
|
||||
assert_equal "ÆÅØle Øen", WikiWords.separate("ÆÅØleØen")
|
||||
assert_equal "Æe ÅØle Øen", WikiWords.separate("ÆeÅØleØen")
|
||||
assert_equal "Legetøj", WikiWords.separate("Legetøj")
|
||||
end
|
||||
end
|
||||
#!/bin/env ruby -w
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'wiki_words'
|
||||
|
||||
class WikiWordsTest < Test::Unit::TestCase
|
||||
|
||||
def test_utf8_characters_in_wiki_word
|
||||
assert_equal "Æåle Øen", WikiWords.separate("ÆåleØen")
|
||||
assert_equal "ÆÅØle Øen", WikiWords.separate("ÆÅØleØen")
|
||||
assert_equal "Æe ÅØle Øen", WikiWords.separate("ÆeÅØleØen")
|
||||
assert_equal "Legetøj", WikiWords.separate("Legetøj")
|
||||
end
|
||||
end
|
||||
|
|
198
vendor/bluecloth-1.0.0/README
vendored
198
vendor/bluecloth-1.0.0/README
vendored
|
@ -1,99 +1,99 @@
|
|||
|
||||
BlueCloth
|
||||
=========
|
||||
|
||||
Version 1.0.0 - 2004/08/24
|
||||
|
||||
Original version by John Gruber <http://daringfireball.net/>.
|
||||
Ruby port by Michael Granger <http://www.deveiate.org/>.
|
||||
|
||||
BlueCloth is a Ruby implementation of [Markdown][1], a text-to-HTML conversion
|
||||
tool for web writers. To quote from the project page: Markdown allows you to
|
||||
write using an easy-to-read, easy-to-write plain text format, then convert it to
|
||||
structurally valid XHTML (or HTML).
|
||||
|
||||
It borrows a naming convention and several helpings of interface from
|
||||
[Redcloth][2], [Why the Lucky Stiff][3]'s processor for a similar text-to-HTML
|
||||
conversion syntax called [Textile][4].
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
You can install this module either by running the included `install.rb` script,
|
||||
or by simply copying `lib/bluecloth.rb` to a directory in your load path.
|
||||
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
BlueCloth uses the `StringScanner` class from the `strscan` library, which comes
|
||||
with Ruby 1.8.x and later or may be downloaded from the RAA for earlier
|
||||
versions, and the `logger` library, which is also included in 1.8.x and later.
|
||||
|
||||
|
||||
Example Usage
|
||||
-------------
|
||||
|
||||
The BlueCloth class is a subclass of Ruby's String, and can be used thusly:
|
||||
|
||||
bc = BlueCloth::new( str )
|
||||
puts bc.to_html
|
||||
|
||||
This `README` file is an example of Markdown syntax. The sample program
|
||||
`bluecloth` in the `bin/` directory can be used to convert this (or any other)
|
||||
file with Markdown syntax into HTML:
|
||||
|
||||
$ bin/bluecloth README > README.html
|
||||
|
||||
|
||||
Acknowledgements
|
||||
----------------
|
||||
|
||||
This library is a port of the canonical Perl one, and so owes most of its
|
||||
functionality to its author, John Gruber. The bugs in this code are most
|
||||
certainly an artifact of my porting it and not an artifact of the excellent code
|
||||
from which it is derived.
|
||||
|
||||
It also, as mentioned before, borrows its API liberally from RedCloth, both for
|
||||
compatibility's sake, and because I think Why's code is beautiful. His excellent
|
||||
code and peerless prose have been an inspiration to me, and this module is
|
||||
intended as the sincerest flattery.
|
||||
|
||||
Also contributing to any success this module may enjoy are those among my peers
|
||||
who have taken the time to help out, either by submitting patches, testing, or
|
||||
offering suggestions and review:
|
||||
|
||||
* Martin Chase <stillflame@FaerieMUD.org>
|
||||
* Florian Gross <flgr@ccan.de>
|
||||
|
||||
|
||||
Author/Legal
|
||||
------------
|
||||
|
||||
Original version:
|
||||
Copyright (c) 2003-2004 John Gruber
|
||||
<http://daringfireball.net/>
|
||||
All rights reserved.
|
||||
|
||||
Ruby version:
|
||||
Copyright (c) 2004 The FaerieMUD Consortium
|
||||
|
||||
BlueCloth is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
BlueCloth is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
|
||||
[1]: http://daringfireball.net/projects/markdown/
|
||||
[2]: http://www.whytheluckystiff.net/ruby/redcloth/
|
||||
[3]: http://www.whytheluckystiff.net/
|
||||
[4]: http://www.textism.com/tools/textile/
|
||||
|
||||
|
||||
$Id: README,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
$URL: svn+ssh://svn.FaerieMUD.org/usr/local/svn/BlueCloth/trunk/README $
|
||||
|
||||
BlueCloth
|
||||
=========
|
||||
|
||||
Version 1.0.0 - 2004/08/24
|
||||
|
||||
Original version by John Gruber <http://daringfireball.net/>.
|
||||
Ruby port by Michael Granger <http://www.deveiate.org/>.
|
||||
|
||||
BlueCloth is a Ruby implementation of [Markdown][1], a text-to-HTML conversion
|
||||
tool for web writers. To quote from the project page: Markdown allows you to
|
||||
write using an easy-to-read, easy-to-write plain text format, then convert it to
|
||||
structurally valid XHTML (or HTML).
|
||||
|
||||
It borrows a naming convention and several helpings of interface from
|
||||
[Redcloth][2], [Why the Lucky Stiff][3]'s processor for a similar text-to-HTML
|
||||
conversion syntax called [Textile][4].
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
You can install this module either by running the included `install.rb` script,
|
||||
or by simply copying `lib/bluecloth.rb` to a directory in your load path.
|
||||
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
BlueCloth uses the `StringScanner` class from the `strscan` library, which comes
|
||||
with Ruby 1.8.x and later or may be downloaded from the RAA for earlier
|
||||
versions, and the `logger` library, which is also included in 1.8.x and later.
|
||||
|
||||
|
||||
Example Usage
|
||||
-------------
|
||||
|
||||
The BlueCloth class is a subclass of Ruby's String, and can be used thusly:
|
||||
|
||||
bc = BlueCloth::new( str )
|
||||
puts bc.to_html
|
||||
|
||||
This `README` file is an example of Markdown syntax. The sample program
|
||||
`bluecloth` in the `bin/` directory can be used to convert this (or any other)
|
||||
file with Markdown syntax into HTML:
|
||||
|
||||
$ bin/bluecloth README > README.html
|
||||
|
||||
|
||||
Acknowledgements
|
||||
----------------
|
||||
|
||||
This library is a port of the canonical Perl one, and so owes most of its
|
||||
functionality to its author, John Gruber. The bugs in this code are most
|
||||
certainly an artifact of my porting it and not an artifact of the excellent code
|
||||
from which it is derived.
|
||||
|
||||
It also, as mentioned before, borrows its API liberally from RedCloth, both for
|
||||
compatibility's sake, and because I think Why's code is beautiful. His excellent
|
||||
code and peerless prose have been an inspiration to me, and this module is
|
||||
intended as the sincerest flattery.
|
||||
|
||||
Also contributing to any success this module may enjoy are those among my peers
|
||||
who have taken the time to help out, either by submitting patches, testing, or
|
||||
offering suggestions and review:
|
||||
|
||||
* Martin Chase <stillflame@FaerieMUD.org>
|
||||
* Florian Gross <flgr@ccan.de>
|
||||
|
||||
|
||||
Author/Legal
|
||||
------------
|
||||
|
||||
Original version:
|
||||
Copyright (c) 2003-2004 John Gruber
|
||||
<http://daringfireball.net/>
|
||||
All rights reserved.
|
||||
|
||||
Ruby version:
|
||||
Copyright (c) 2004 The FaerieMUD Consortium
|
||||
|
||||
BlueCloth is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
BlueCloth is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
|
||||
[1]: http://daringfireball.net/projects/markdown/
|
||||
[2]: http://www.whytheluckystiff.net/ruby/redcloth/
|
||||
[3]: http://www.whytheluckystiff.net/
|
||||
[4]: http://www.textism.com/tools/textile/
|
||||
|
||||
|
||||
$Id: README,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
$URL: svn+ssh://svn.FaerieMUD.org/usr/local/svn/BlueCloth/trunk/README $
|
||||
|
|
300
vendor/bluecloth-1.0.0/install.rb
vendored
300
vendor/bluecloth-1.0.0/install.rb
vendored
|
@ -1,150 +1,150 @@
|
|||
#!/usr/bin/ruby
|
||||
#
|
||||
# BlueCloth Module Install Script
|
||||
# $Id: install.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
#
|
||||
# Thanks to Masatoshi SEKI for ideas found in his install.rb.
|
||||
#
|
||||
# Copyright (c) 2001-2004 The FaerieMUD Consortium.
|
||||
#
|
||||
# This is free software. You may use, modify, and/or redistribute this
|
||||
# software under the terms of the Perl Artistic License. (See
|
||||
# http://language.perl.com/misc/Artistic.html)
|
||||
#
|
||||
|
||||
require './utils.rb'
|
||||
include UtilityFunctions
|
||||
|
||||
require 'rbconfig'
|
||||
include Config
|
||||
|
||||
require 'find'
|
||||
require 'ftools'
|
||||
|
||||
|
||||
$version = %q$Revision: 1.1 $
|
||||
$rcsId = %q$Id: install.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
|
||||
# Define required libraries
|
||||
RequiredLibraries = [
|
||||
# libraryname, nice name, RAA URL, Download URL
|
||||
[ 'strscan', "StrScan",
|
||||
'http://raa.ruby-lang.org/list.rhtml?name=strscan',
|
||||
'http://i.loveruby.net/archive/strscan/strscan-0.6.7.tar.gz' ],
|
||||
[ 'logger', "Devel-Logger",
|
||||
'http://raa.ruby-lang.org/list.rhtml?name=devel-logger',
|
||||
'http://rrr.jin.gr.jp/download/devel-logger-1_2_2.tar.gz' ],
|
||||
]
|
||||
|
||||
class Installer
|
||||
|
||||
@@PrunePatterns = [
|
||||
/CVS/,
|
||||
/~$/,
|
||||
%r:(^|/)\.:,
|
||||
/\.tpl$/,
|
||||
]
|
||||
|
||||
def initialize( testing=false )
|
||||
@ftools = (testing) ? self : File
|
||||
end
|
||||
|
||||
### Make the specified dirs (which can be a String or an Array of Strings)
|
||||
### with the specified mode.
|
||||
def makedirs( dirs, mode=0755, verbose=false )
|
||||
dirs = [ dirs ] unless dirs.is_a? Array
|
||||
|
||||
oldumask = File::umask
|
||||
File::umask( 0777 - mode )
|
||||
|
||||
for dir in dirs
|
||||
if @ftools == File
|
||||
File::mkpath( dir, $verbose )
|
||||
else
|
||||
$stderr.puts "Make path %s with mode %o" % [ dir, mode ]
|
||||
end
|
||||
end
|
||||
|
||||
File::umask( oldumask )
|
||||
end
|
||||
|
||||
def install( srcfile, dstfile, mode=nil, verbose=false )
|
||||
dstfile = File.catname(srcfile, dstfile)
|
||||
unless FileTest.exist? dstfile and File.cmp srcfile, dstfile
|
||||
$stderr.puts " install #{srcfile} -> #{dstfile}"
|
||||
else
|
||||
$stderr.puts " skipping #{dstfile}: unchanged"
|
||||
end
|
||||
end
|
||||
|
||||
public
|
||||
|
||||
def installFiles( src, dstDir, mode=0444, verbose=false )
|
||||
directories = []
|
||||
files = []
|
||||
|
||||
if File.directory?( src )
|
||||
Find.find( src ) {|f|
|
||||
Find.prune if @@PrunePatterns.find {|pat| f =~ pat}
|
||||
next if f == src
|
||||
|
||||
if FileTest.directory?( f )
|
||||
directories << f.gsub( /^#{src}#{File::Separator}/, '' )
|
||||
next
|
||||
|
||||
elsif FileTest.file?( f )
|
||||
files << f.gsub( /^#{src}#{File::Separator}/, '' )
|
||||
|
||||
else
|
||||
Find.prune
|
||||
end
|
||||
}
|
||||
else
|
||||
files << File.basename( src )
|
||||
src = File.dirname( src )
|
||||
end
|
||||
|
||||
dirs = [ dstDir ]
|
||||
dirs |= directories.collect {|d| File.join(dstDir,d)}
|
||||
makedirs( dirs, 0755, verbose )
|
||||
files.each {|f|
|
||||
srcfile = File.join(src,f)
|
||||
dstfile = File.dirname(File.join( dstDir,f ))
|
||||
|
||||
if verbose
|
||||
if mode
|
||||
$stderr.puts "Install #{srcfile} -> #{dstfile} (mode %o)" % mode
|
||||
else
|
||||
$stderr.puts "Install #{srcfile} -> #{dstfile}"
|
||||
end
|
||||
end
|
||||
|
||||
@ftools.install( srcfile, dstfile, mode, verbose )
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if $0 == __FILE__
|
||||
header "BlueCloth Installer #$version"
|
||||
|
||||
for lib in RequiredLibraries
|
||||
testForRequiredLibrary( *lib )
|
||||
end
|
||||
|
||||
viewOnly = ARGV.include? '-n'
|
||||
verbose = ARGV.include? '-v'
|
||||
|
||||
debugMsg "Sitelibdir = '#{CONFIG['sitelibdir']}'"
|
||||
sitelibdir = CONFIG['sitelibdir']
|
||||
debugMsg "Sitearchdir = '#{CONFIG['sitearchdir']}'"
|
||||
sitearchdir = CONFIG['sitearchdir']
|
||||
|
||||
message "Installing\n"
|
||||
i = Installer.new( viewOnly )
|
||||
i.installFiles( "lib", sitelibdir, 0444, verbose )
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
#!/usr/bin/ruby
|
||||
#
|
||||
# BlueCloth Module Install Script
|
||||
# $Id: install.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
#
|
||||
# Thanks to Masatoshi SEKI for ideas found in his install.rb.
|
||||
#
|
||||
# Copyright (c) 2001-2004 The FaerieMUD Consortium.
|
||||
#
|
||||
# This is free software. You may use, modify, and/or redistribute this
|
||||
# software under the terms of the Perl Artistic License. (See
|
||||
# http://language.perl.com/misc/Artistic.html)
|
||||
#
|
||||
|
||||
require './utils.rb'
|
||||
include UtilityFunctions
|
||||
|
||||
require 'rbconfig'
|
||||
include Config
|
||||
|
||||
require 'find'
|
||||
require 'ftools'
|
||||
|
||||
|
||||
$version = %q$Revision: 1.1 $
|
||||
$rcsId = %q$Id: install.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
|
||||
# Define required libraries
|
||||
RequiredLibraries = [
|
||||
# libraryname, nice name, RAA URL, Download URL
|
||||
[ 'strscan', "StrScan",
|
||||
'http://raa.ruby-lang.org/list.rhtml?name=strscan',
|
||||
'http://i.loveruby.net/archive/strscan/strscan-0.6.7.tar.gz' ],
|
||||
[ 'logger', "Devel-Logger",
|
||||
'http://raa.ruby-lang.org/list.rhtml?name=devel-logger',
|
||||
'http://rrr.jin.gr.jp/download/devel-logger-1_2_2.tar.gz' ],
|
||||
]
|
||||
|
||||
class Installer
|
||||
|
||||
@@PrunePatterns = [
|
||||
/CVS/,
|
||||
/~$/,
|
||||
%r:(^|/)\.:,
|
||||
/\.tpl$/,
|
||||
]
|
||||
|
||||
def initialize( testing=false )
|
||||
@ftools = (testing) ? self : File
|
||||
end
|
||||
|
||||
### Make the specified dirs (which can be a String or an Array of Strings)
|
||||
### with the specified mode.
|
||||
def makedirs( dirs, mode=0755, verbose=false )
|
||||
dirs = [ dirs ] unless dirs.is_a? Array
|
||||
|
||||
oldumask = File::umask
|
||||
File::umask( 0777 - mode )
|
||||
|
||||
for dir in dirs
|
||||
if @ftools == File
|
||||
File::mkpath( dir, $verbose )
|
||||
else
|
||||
$stderr.puts "Make path %s with mode %o" % [ dir, mode ]
|
||||
end
|
||||
end
|
||||
|
||||
File::umask( oldumask )
|
||||
end
|
||||
|
||||
def install( srcfile, dstfile, mode=nil, verbose=false )
|
||||
dstfile = File.catname(srcfile, dstfile)
|
||||
unless FileTest.exist? dstfile and File.cmp srcfile, dstfile
|
||||
$stderr.puts " install #{srcfile} -> #{dstfile}"
|
||||
else
|
||||
$stderr.puts " skipping #{dstfile}: unchanged"
|
||||
end
|
||||
end
|
||||
|
||||
public
|
||||
|
||||
def installFiles( src, dstDir, mode=0444, verbose=false )
|
||||
directories = []
|
||||
files = []
|
||||
|
||||
if File.directory?( src )
|
||||
Find.find( src ) {|f|
|
||||
Find.prune if @@PrunePatterns.find {|pat| f =~ pat}
|
||||
next if f == src
|
||||
|
||||
if FileTest.directory?( f )
|
||||
directories << f.gsub( /^#{src}#{File::Separator}/, '' )
|
||||
next
|
||||
|
||||
elsif FileTest.file?( f )
|
||||
files << f.gsub( /^#{src}#{File::Separator}/, '' )
|
||||
|
||||
else
|
||||
Find.prune
|
||||
end
|
||||
}
|
||||
else
|
||||
files << File.basename( src )
|
||||
src = File.dirname( src )
|
||||
end
|
||||
|
||||
dirs = [ dstDir ]
|
||||
dirs |= directories.collect {|d| File.join(dstDir,d)}
|
||||
makedirs( dirs, 0755, verbose )
|
||||
files.each {|f|
|
||||
srcfile = File.join(src,f)
|
||||
dstfile = File.dirname(File.join( dstDir,f ))
|
||||
|
||||
if verbose
|
||||
if mode
|
||||
$stderr.puts "Install #{srcfile} -> #{dstfile} (mode %o)" % mode
|
||||
else
|
||||
$stderr.puts "Install #{srcfile} -> #{dstfile}"
|
||||
end
|
||||
end
|
||||
|
||||
@ftools.install( srcfile, dstfile, mode, verbose )
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if $0 == __FILE__
|
||||
header "BlueCloth Installer #$version"
|
||||
|
||||
for lib in RequiredLibraries
|
||||
testForRequiredLibrary( *lib )
|
||||
end
|
||||
|
||||
viewOnly = ARGV.include? '-n'
|
||||
verbose = ARGV.include? '-v'
|
||||
|
||||
debugMsg "Sitelibdir = '#{CONFIG['sitelibdir']}'"
|
||||
sitelibdir = CONFIG['sitelibdir']
|
||||
debugMsg "Sitearchdir = '#{CONFIG['sitearchdir']}'"
|
||||
sitearchdir = CONFIG['sitearchdir']
|
||||
|
||||
message "Installing\n"
|
||||
i = Installer.new( viewOnly )
|
||||
i.installFiles( "lib", sitelibdir, 0444, verbose )
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
2288
vendor/bluecloth-1.0.0/lib/bluecloth.rb
vendored
2288
vendor/bluecloth-1.0.0/lib/bluecloth.rb
vendored
File diff suppressed because it is too large
Load diff
234
vendor/bluecloth-1.0.0/test.rb
vendored
234
vendor/bluecloth-1.0.0/test.rb
vendored
|
@ -1,117 +1,117 @@
|
|||
#!/usr/bin/ruby
|
||||
#
|
||||
# Test suite for BlueCloth classes
|
||||
# $Id: test.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
#
|
||||
|
||||
BEGIN {
|
||||
$basedir = File::dirname( __FILE__ )
|
||||
["lib", "tests", "redist"].each do |subdir|
|
||||
$LOAD_PATH.unshift File::join( $basedir, subdir )
|
||||
end
|
||||
|
||||
require "#{$basedir}/utils"
|
||||
include UtilityFunctions
|
||||
}
|
||||
|
||||
verboseOff {
|
||||
require 'bctestcase'
|
||||
require 'find'
|
||||
require 'test/unit'
|
||||
require 'test/unit/testsuite'
|
||||
require 'test/unit/ui/console/testrunner'
|
||||
require 'optparse'
|
||||
}
|
||||
|
||||
# Turn off output buffering
|
||||
$stderr.sync = $stdout.sync = true
|
||||
$DebugPattern = nil
|
||||
|
||||
# Initialize variables
|
||||
safelevel = 0
|
||||
patterns = []
|
||||
requires = []
|
||||
|
||||
# Parse command-line switches
|
||||
ARGV.options {|oparser|
|
||||
oparser.banner = "Usage: #$0 [options] [TARGETS]\n"
|
||||
|
||||
oparser.on( "--debug[=PATTERN]", "-d[=PATTERN]", String,
|
||||
"Turn debugging on (for tests which match PATTERN)" ) {|arg|
|
||||
if arg
|
||||
$DebugPattern = Regexp::new( arg )
|
||||
puts "Turned debugging on for %p." % $DebugPattern
|
||||
else
|
||||
$DEBUG = true
|
||||
debugMsg "Turned debugging on globally."
|
||||
end
|
||||
}
|
||||
|
||||
oparser.on( "--verbose", "-v", TrueClass, "Make progress verbose" ) {
|
||||
$VERBOSE = true
|
||||
debugMsg "Turned verbose on."
|
||||
}
|
||||
|
||||
# Handle the 'help' option
|
||||
oparser.on( "--help", "-h", "Display this text." ) {
|
||||
$stderr.puts oparser
|
||||
exit!(0)
|
||||
}
|
||||
|
||||
oparser.parse!
|
||||
}
|
||||
|
||||
# Parse test patterns
|
||||
ARGV.each {|pat| patterns << Regexp::new( pat, Regexp::IGNORECASE )}
|
||||
$stderr.puts "#{patterns.length} patterns given on the command line"
|
||||
|
||||
### Load all the tests from the tests dir
|
||||
Find.find("#{$basedir}/tests") {|file|
|
||||
Find.prune if /\/\./ =~ file or /~$/ =~ file
|
||||
Find.prune if /TEMPLATE/ =~ file
|
||||
next if File.stat( file ).directory?
|
||||
|
||||
unless patterns.empty?
|
||||
Find.prune unless patterns.find {|pat| pat =~ file}
|
||||
end
|
||||
|
||||
debugMsg "Considering '%s': " % file
|
||||
next unless file =~ /\.tests.rb$/
|
||||
debugMsg "Requiring '%s'..." % file
|
||||
require "#{file}"
|
||||
requires << file
|
||||
}
|
||||
|
||||
$stderr.puts "Required #{requires.length} files."
|
||||
unless patterns.empty?
|
||||
$stderr.puts "[" + requires.sort.join( ", " ) + "]"
|
||||
end
|
||||
|
||||
# Build the test suite
|
||||
class BlueClothTests
|
||||
class << self
|
||||
def suite
|
||||
suite = Test::Unit::TestSuite.new( "BlueCloth" )
|
||||
|
||||
if suite.respond_to?( :add )
|
||||
ObjectSpace.each_object( Class ) {|klass|
|
||||
suite.add( klass.suite ) if klass < BlueCloth::TestCase
|
||||
}
|
||||
else
|
||||
ObjectSpace.each_object( Class ) {|klass|
|
||||
suite << klass.suite if klass < BlueCloth::TestCase
|
||||
}
|
||||
end
|
||||
|
||||
return suite
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Run tests
|
||||
$SAFE = safelevel
|
||||
Test::Unit::UI::Console::TestRunner.new( BlueClothTests ).start
|
||||
|
||||
|
||||
|
||||
|
||||
#!/usr/bin/ruby
|
||||
#
|
||||
# Test suite for BlueCloth classes
|
||||
# $Id: test.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
#
|
||||
|
||||
BEGIN {
|
||||
$basedir = File::dirname( __FILE__ )
|
||||
["lib", "tests", "redist"].each do |subdir|
|
||||
$LOAD_PATH.unshift File::join( $basedir, subdir )
|
||||
end
|
||||
|
||||
require "#{$basedir}/utils"
|
||||
include UtilityFunctions
|
||||
}
|
||||
|
||||
verboseOff {
|
||||
require 'bctestcase'
|
||||
require 'find'
|
||||
require 'test/unit'
|
||||
require 'test/unit/testsuite'
|
||||
require 'test/unit/ui/console/testrunner'
|
||||
require 'optparse'
|
||||
}
|
||||
|
||||
# Turn off output buffering
|
||||
$stderr.sync = $stdout.sync = true
|
||||
$DebugPattern = nil
|
||||
|
||||
# Initialize variables
|
||||
safelevel = 0
|
||||
patterns = []
|
||||
requires = []
|
||||
|
||||
# Parse command-line switches
|
||||
ARGV.options {|oparser|
|
||||
oparser.banner = "Usage: #$0 [options] [TARGETS]\n"
|
||||
|
||||
oparser.on( "--debug[=PATTERN]", "-d[=PATTERN]", String,
|
||||
"Turn debugging on (for tests which match PATTERN)" ) {|arg|
|
||||
if arg
|
||||
$DebugPattern = Regexp::new( arg )
|
||||
puts "Turned debugging on for %p." % $DebugPattern
|
||||
else
|
||||
$DEBUG = true
|
||||
debugMsg "Turned debugging on globally."
|
||||
end
|
||||
}
|
||||
|
||||
oparser.on( "--verbose", "-v", TrueClass, "Make progress verbose" ) {
|
||||
$VERBOSE = true
|
||||
debugMsg "Turned verbose on."
|
||||
}
|
||||
|
||||
# Handle the 'help' option
|
||||
oparser.on( "--help", "-h", "Display this text." ) {
|
||||
$stderr.puts oparser
|
||||
exit!(0)
|
||||
}
|
||||
|
||||
oparser.parse!
|
||||
}
|
||||
|
||||
# Parse test patterns
|
||||
ARGV.each {|pat| patterns << Regexp::new( pat, Regexp::IGNORECASE )}
|
||||
$stderr.puts "#{patterns.length} patterns given on the command line"
|
||||
|
||||
### Load all the tests from the tests dir
|
||||
Find.find("#{$basedir}/tests") {|file|
|
||||
Find.prune if /\/\./ =~ file or /~$/ =~ file
|
||||
Find.prune if /TEMPLATE/ =~ file
|
||||
next if File.stat( file ).directory?
|
||||
|
||||
unless patterns.empty?
|
||||
Find.prune unless patterns.find {|pat| pat =~ file}
|
||||
end
|
||||
|
||||
debugMsg "Considering '%s': " % file
|
||||
next unless file =~ /\.tests.rb$/
|
||||
debugMsg "Requiring '%s'..." % file
|
||||
require "#{file}"
|
||||
requires << file
|
||||
}
|
||||
|
||||
$stderr.puts "Required #{requires.length} files."
|
||||
unless patterns.empty?
|
||||
$stderr.puts "[" + requires.sort.join( ", " ) + "]"
|
||||
end
|
||||
|
||||
# Build the test suite
|
||||
class BlueClothTests
|
||||
class << self
|
||||
def suite
|
||||
suite = Test::Unit::TestSuite.new( "BlueCloth" )
|
||||
|
||||
if suite.respond_to?( :add )
|
||||
ObjectSpace.each_object( Class ) {|klass|
|
||||
suite.add( klass.suite ) if klass < BlueCloth::TestCase
|
||||
}
|
||||
else
|
||||
ObjectSpace.each_object( Class ) {|klass|
|
||||
suite << klass.suite if klass < BlueCloth::TestCase
|
||||
}
|
||||
end
|
||||
|
||||
return suite
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Run tests
|
||||
$SAFE = safelevel
|
||||
Test::Unit::UI::Console::TestRunner.new( BlueClothTests ).start
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
142
vendor/bluecloth-1.0.0/tests/00_Class.tests.rb
vendored
142
vendor/bluecloth-1.0.0/tests/00_Class.tests.rb
vendored
|
@ -1,71 +1,71 @@
|
|||
#!/usr/bin/ruby
|
||||
#
|
||||
# Unit test for the BlueCloth class object
|
||||
# $Id: 00_Class.tests.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
#
|
||||
# Copyright (c) 2004 The FaerieMUD Consortium.
|
||||
#
|
||||
|
||||
if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
|
||||
basedir = File::dirname( __FILE__ )
|
||||
require File::join( basedir, 'bctestcase' )
|
||||
end
|
||||
|
||||
|
||||
### This test case tests ...
|
||||
class BlueClothClassTestCase < BlueCloth::TestCase
|
||||
|
||||
TestString = "foo"
|
||||
|
||||
def test_00_class_constant
|
||||
printTestHeader "BlueCloth: Class Constant"
|
||||
|
||||
assert Object::constants.include?( "BlueCloth" ),
|
||||
"No BlueCloth constant in Object"
|
||||
assert_instance_of Class, BlueCloth
|
||||
end
|
||||
|
||||
def test_01_instantiation
|
||||
printTestHeader "BlueCloth: Instantiation"
|
||||
rval = nil
|
||||
|
||||
# With no argument... ("")
|
||||
assert_nothing_raised {
|
||||
rval = BlueCloth::new
|
||||
}
|
||||
assert_instance_of BlueCloth, rval
|
||||
assert_kind_of String, rval
|
||||
assert_equal "", rval
|
||||
|
||||
# String argument
|
||||
assert_nothing_raised {
|
||||
rval = BlueCloth::new TestString
|
||||
}
|
||||
assert_instance_of BlueCloth, rval
|
||||
assert_kind_of String, rval
|
||||
assert_equal TestString, rval
|
||||
|
||||
addSetupBlock {
|
||||
debugMsg "Creating a new BlueCloth"
|
||||
@obj = BlueCloth::new( TestString )
|
||||
}
|
||||
addTeardownBlock {
|
||||
@obj = nil
|
||||
}
|
||||
end
|
||||
|
||||
def test_02_duplication
|
||||
printTestHeader "BlueCloth: Duplication"
|
||||
rval = nil
|
||||
|
||||
assert_nothing_raised {
|
||||
rval = @obj.dup
|
||||
}
|
||||
assert_instance_of BlueCloth, rval
|
||||
assert_kind_of String, rval
|
||||
assert_equal TestString, rval
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
#!/usr/bin/ruby
|
||||
#
|
||||
# Unit test for the BlueCloth class object
|
||||
# $Id: 00_Class.tests.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
#
|
||||
# Copyright (c) 2004 The FaerieMUD Consortium.
|
||||
#
|
||||
|
||||
if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
|
||||
basedir = File::dirname( __FILE__ )
|
||||
require File::join( basedir, 'bctestcase' )
|
||||
end
|
||||
|
||||
|
||||
### This test case tests ...
|
||||
class BlueClothClassTestCase < BlueCloth::TestCase
|
||||
|
||||
TestString = "foo"
|
||||
|
||||
def test_00_class_constant
|
||||
printTestHeader "BlueCloth: Class Constant"
|
||||
|
||||
assert Object::constants.include?( "BlueCloth" ),
|
||||
"No BlueCloth constant in Object"
|
||||
assert_instance_of Class, BlueCloth
|
||||
end
|
||||
|
||||
def test_01_instantiation
|
||||
printTestHeader "BlueCloth: Instantiation"
|
||||
rval = nil
|
||||
|
||||
# With no argument... ("")
|
||||
assert_nothing_raised {
|
||||
rval = BlueCloth::new
|
||||
}
|
||||
assert_instance_of BlueCloth, rval
|
||||
assert_kind_of String, rval
|
||||
assert_equal "", rval
|
||||
|
||||
# String argument
|
||||
assert_nothing_raised {
|
||||
rval = BlueCloth::new TestString
|
||||
}
|
||||
assert_instance_of BlueCloth, rval
|
||||
assert_kind_of String, rval
|
||||
assert_equal TestString, rval
|
||||
|
||||
addSetupBlock {
|
||||
debugMsg "Creating a new BlueCloth"
|
||||
@obj = BlueCloth::new( TestString )
|
||||
}
|
||||
addTeardownBlock {
|
||||
@obj = nil
|
||||
}
|
||||
end
|
||||
|
||||
def test_02_duplication
|
||||
printTestHeader "BlueCloth: Duplication"
|
||||
rval = nil
|
||||
|
||||
assert_nothing_raised {
|
||||
rval = @obj.dup
|
||||
}
|
||||
assert_instance_of BlueCloth, rval
|
||||
assert_kind_of String, rval
|
||||
assert_equal TestString, rval
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
|
3054
vendor/bluecloth-1.0.0/tests/05_Markdown.tests.rb
vendored
3054
vendor/bluecloth-1.0.0/tests/05_Markdown.tests.rb
vendored
File diff suppressed because it is too large
Load diff
114
vendor/bluecloth-1.0.0/tests/10_Bug.tests.rb
vendored
114
vendor/bluecloth-1.0.0/tests/10_Bug.tests.rb
vendored
|
@ -1,57 +1,57 @@
|
|||
#!/usr/bin/ruby
|
||||
#
|
||||
# Unit test for bugs found in BlueCloth
|
||||
# $Id: 10_Bug.tests.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
#
|
||||
# Copyright (c) 2004 The FaerieMUD Consortium.
|
||||
#
|
||||
|
||||
if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
|
||||
basedir = File::dirname( __FILE__ )
|
||||
require File::join( basedir, 'bctestcase' )
|
||||
end
|
||||
|
||||
|
||||
require 'timeout'
|
||||
|
||||
### This test case tests ...
|
||||
class BugsTestCase < BlueCloth::TestCase
|
||||
BaseDir = File::dirname( File::dirname(File::expand_path( __FILE__ )) )
|
||||
|
||||
### Test to be sure the README file can be transformed.
|
||||
def test_00_slow_block_regex
|
||||
contents = File::read( File::join(BaseDir,"README") )
|
||||
bcobj = BlueCloth::new( contents )
|
||||
|
||||
assert_nothing_raised {
|
||||
timeout( 2 ) do
|
||||
bcobj.to_html
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
### :TODO: Add more documents and test their transforms.
|
||||
|
||||
def test_10_regexp_engine_overflow_bug
|
||||
contents = File::read( File::join(BaseDir,"tests/data/re-overflow.txt") )
|
||||
bcobj = BlueCloth::new( contents )
|
||||
|
||||
assert_nothing_raised {
|
||||
bcobj.to_html
|
||||
}
|
||||
end
|
||||
|
||||
def test_15_regexp_engine_overflow_bug2
|
||||
contents = File::read( File::join(BaseDir,"tests/data/re-overflow2.txt") )
|
||||
bcobj = BlueCloth::new( contents )
|
||||
|
||||
assert_nothing_raised {
|
||||
bcobj.to_html
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
__END__
|
||||
|
||||
#!/usr/bin/ruby
|
||||
#
|
||||
# Unit test for bugs found in BlueCloth
|
||||
# $Id: 10_Bug.tests.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
#
|
||||
# Copyright (c) 2004 The FaerieMUD Consortium.
|
||||
#
|
||||
|
||||
if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
|
||||
basedir = File::dirname( __FILE__ )
|
||||
require File::join( basedir, 'bctestcase' )
|
||||
end
|
||||
|
||||
|
||||
require 'timeout'
|
||||
|
||||
### This test case tests ...
|
||||
class BugsTestCase < BlueCloth::TestCase
|
||||
BaseDir = File::dirname( File::dirname(File::expand_path( __FILE__ )) )
|
||||
|
||||
### Test to be sure the README file can be transformed.
|
||||
def test_00_slow_block_regex
|
||||
contents = File::read( File::join(BaseDir,"README") )
|
||||
bcobj = BlueCloth::new( contents )
|
||||
|
||||
assert_nothing_raised {
|
||||
timeout( 2 ) do
|
||||
bcobj.to_html
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
### :TODO: Add more documents and test their transforms.
|
||||
|
||||
def test_10_regexp_engine_overflow_bug
|
||||
contents = File::read( File::join(BaseDir,"tests/data/re-overflow.txt") )
|
||||
bcobj = BlueCloth::new( contents )
|
||||
|
||||
assert_nothing_raised {
|
||||
bcobj.to_html
|
||||
}
|
||||
end
|
||||
|
||||
def test_15_regexp_engine_overflow_bug2
|
||||
contents = File::read( File::join(BaseDir,"tests/data/re-overflow2.txt") )
|
||||
bcobj = BlueCloth::new( contents )
|
||||
|
||||
assert_nothing_raised {
|
||||
bcobj.to_html
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
__END__
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue