Massive change of SVN properties to deal with EOL style problem

This commit is contained in:
Alexey Verkhovsky 2005-01-24 18:52:04 +00:00
parent b747b611b3
commit 3b6566577c
108 changed files with 12417 additions and 12417 deletions

View file

@ -1,71 +1,71 @@
require 'fileutils' require 'fileutils'
require 'application' require 'application'
require 'instiki_errors' require 'instiki_errors'
class FileController < ApplicationController class FileController < ApplicationController
layout 'default' layout 'default'
before_filter :check_allow_uploads before_filter :check_allow_uploads
def file def file
check_path check_path
if @params['file'] if @params['file']
# form supplied # form supplied
file_yard.upload_file(@file_name, @params['file']) file_yard.upload_file(@file_name, @params['file'])
flash[:info] = "File '#{@file_name}' successfully uploaded" flash[:info] = "File '#{@file_name}' successfully uploaded"
@web.refresh_pages_with_references(@file_name) @web.refresh_pages_with_references(@file_name)
return_to_last_remembered return_to_last_remembered
elsif file_yard.has_file?(@file_name) elsif file_yard.has_file?(@file_name)
send_file(file_yard.file_path(@file_name)) send_file(file_yard.file_path(@file_name))
else else
logger.debug("File not found: #{file_yard.files_path}/#{@file_name}") logger.debug("File not found: #{file_yard.files_path}/#{@file_name}")
# go to the template, which is a file upload form # go to the template, which is a file upload form
end end
end end
def cancel_upload def cancel_upload
return_to_last_remembered return_to_last_remembered
end end
def pic def pic
check_path check_path
if @params['file'] if @params['file']
# form supplied # form supplied
file_yard.upload_file(@file_name, @params['file']) file_yard.upload_file(@file_name, @params['file'])
flash[:info] = "Image '#{@file_name}' successfully uploaded" flash[:info] = "Image '#{@file_name}' successfully uploaded"
@web.refresh_pages_with_references(@file_name) @web.refresh_pages_with_references(@file_name)
return_to_last_remembered return_to_last_remembered
elsif file_yard.has_file?(@file_name) elsif file_yard.has_file?(@file_name)
send_file(file_yard.file_path(@file_name)) send_file(file_yard.file_path(@file_name))
else else
logger.debug("Image not found: #{file_yard.files_path}/#{@file_name}") logger.debug("Image not found: #{file_yard.files_path}/#{@file_name}")
render_action 'file' render_action 'file'
end end
end end
protected protected
def check_allow_uploads def check_allow_uploads
unless @web.allow_uploads unless @web.allow_uploads
render_text 'File uploads are blocked by the webmaster', '403 Forbidden' render_text 'File uploads are blocked by the webmaster', '403 Forbidden'
return false return false
end end
end end
private private
def check_path def check_path
raise Instiki::ValidationError.new("Invalid path: no file name") unless @file_name 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: no web name") unless @web_name
raise Instiki::ValidationError.new("Invalid path: unknown web name") unless @web raise Instiki::ValidationError.new("Invalid path: unknown web name") unless @web
end end
def file_yard def file_yard
@wiki.file_yard(@web) @wiki.file_yard(@web)
end end
end end

766
app/controllers/wiki_controller.rb Executable file → Normal file
View file

@ -1,383 +1,383 @@
require 'application' require 'application'
require 'fileutils' require 'fileutils'
require 'redcloth_for_tex' require 'redcloth_for_tex'
class WikiController < ApplicationController class WikiController < ApplicationController
layout 'default', :except => [:rss_feed, :rss_with_headlines, :tex, :export_tex, :export_html] layout 'default', :except => [:rss_feed, :rss_with_headlines, :tex, :export_tex, :export_html]
def index def index
if @web_name if @web_name
redirect_show 'HomePage' redirect_show 'HomePage'
elsif not @wiki.setup? elsif not @wiki.setup?
redirect_to :action => 'new_system' redirect_to :action => 'new_system'
elsif @wiki.webs.length == 1 elsif @wiki.webs.length == 1
redirect_show 'HomePage', @wiki.webs.values.first.address redirect_show 'HomePage', @wiki.webs.values.first.address
else else
redirect_to :action => 'web_list' redirect_to :action => 'web_list'
end end
end end
# Administrating the Instiki setup -------------------------------------------- # Administrating the Instiki setup --------------------------------------------
def create_system def create_system
@wiki.setup(@params['password'], @params['web_name'], @params['web_address']) unless @wiki.setup? @wiki.setup(@params['password'], @params['web_name'], @params['web_address']) unless @wiki.setup?
redirect_show('HomePage', @params['web_address']) redirect_show('HomePage', @params['web_address'])
end end
def create_web def create_web
if @wiki.authenticate(@params['system_password']) if @wiki.authenticate(@params['system_password'])
@wiki.create_web(@params['name'], @params['address']) @wiki.create_web(@params['name'], @params['address'])
redirect_show('HomePage', @params['address']) redirect_show('HomePage', @params['address'])
else else
redirect_to :action => 'index' redirect_to :action => 'index'
end end
end end
def edit_web def edit_web
# to template # to template
end end
def new_system def new_system
redirect_to(:action => 'index') if wiki.setup? redirect_to(:action => 'index') if wiki.setup?
# otherwise, to template # otherwise, to template
end end
def new_web def new_web
redirect_to :action => 'index' if wiki.system['password'].nil? redirect_to :action => 'index' if wiki.system['password'].nil?
# otherwise, to template # otherwise, to template
end end
# Outside a single web -------------------------------------------------------- # Outside a single web --------------------------------------------------------
def authenticate def authenticate
if password_check(@params['password']) if password_check(@params['password'])
redirect_show('HomePage') redirect_show('HomePage')
else else
redirect_to :action => 'login' redirect_to :action => 'login'
end end
end end
def login def login
# to template # to template
end end
def web_list def web_list
@webs = wiki.webs.values.sort_by { |web| web.name } @webs = wiki.webs.values.sort_by { |web| web.name }
end end
# Within a single web --------------------------------------------------------- # Within a single web ---------------------------------------------------------
def authors def authors
@authors = @web.select.authors @authors = @web.select.authors
end end
def export_html def export_html
export_pages_as_zip('html') { |page| @page = page; render_to_string 'wiki/print' } export_pages_as_zip('html') { |page| @page = page; render_to_string 'wiki/print' }
end end
def export_markup def export_markup
export_pages_as_zip(@web.markup) { |page| page.content } export_pages_as_zip(@web.markup) { |page| page.content }
end end
def export_pdf def export_pdf
file_name = "#{@web.address}-tex-#{@web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')}" file_name = "#{@web.address}-tex-#{@web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')}"
file_path = @wiki.storage_path + file_name file_path = @wiki.storage_path + file_name
export_web_to_tex "#{file_path}.tex" unless FileTest.exists? "#{file_path}.tex" export_web_to_tex "#{file_path}.tex" unless FileTest.exists? "#{file_path}.tex"
convert_tex_to_pdf "#{file_path}.tex" convert_tex_to_pdf "#{file_path}.tex"
send_file "#{file_path}.pdf" send_file "#{file_path}.pdf"
end end
def export_tex def export_tex
file_name = "#{@web.address}-tex-#{@web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')}.tex" file_name = "#{@web.address}-tex-#{@web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')}.tex"
file_path = @wiki.storage_path + file_name file_path = @wiki.storage_path + file_name
export_web_to_tex(file_path) unless FileTest.exists?(file_path) export_web_to_tex(file_path) unless FileTest.exists?(file_path)
send_file file_path send_file file_path
end end
def feeds def feeds
# to template # to template
end end
def list def list
parse_category parse_category
@pages_by_name = @pages_in_category.by_name @pages_by_name = @pages_in_category.by_name
@page_names_that_are_wanted = @pages_in_category.wanted_pages @page_names_that_are_wanted = @pages_in_category.wanted_pages
@pages_that_are_orphaned = @pages_in_category.orphaned_pages @pages_that_are_orphaned = @pages_in_category.orphaned_pages
end end
def recently_revised def recently_revised
parse_category parse_category
@pages_by_revision = @pages_in_category.by_revision @pages_by_revision = @pages_in_category.by_revision
end end
def remove_orphaned_pages def remove_orphaned_pages
if wiki.authenticate(@params['system_password']) if wiki.authenticate(@params['system_password'])
wiki.remove_orphaned_pages(@web_name) wiki.remove_orphaned_pages(@web_name)
redirect_to :action => 'list' redirect_to :action => 'list'
else else
redirect_show 'HomePage' redirect_show 'HomePage'
end end
end end
def rss_with_content def rss_with_content
render_rss render_rss
end end
def rss_with_headlines def rss_with_headlines
render_rss(hide_description = true) render_rss(hide_description = true)
end end
def search def search
@query = @params['query'] @query = @params['query']
@results = @web.select { |page| page.content =~ /#{@query}/i }.sort @results = @web.select { |page| page.content =~ /#{@query}/i }.sort
redirect_show(@results.first.name) if @results.length == 1 redirect_show(@results.first.name) if @results.length == 1
end end
def update_web def update_web
if wiki.authenticate(@params['system_password']) if wiki.authenticate(@params['system_password'])
wiki.update_web( wiki.update_web(
@web.address, @params['address'], @params['name'], @web.address, @params['address'], @params['name'],
@params['markup'].intern, @params['markup'].intern,
@params['color'], @params['additional_style'], @params['color'], @params['additional_style'],
@params['safe_mode'] ? true : false, @params['safe_mode'] ? true : false,
@params['password'].empty? ? nil : @params['password'], @params['password'].empty? ? nil : @params['password'],
@params['published'] ? true : false, @params['published'] ? true : false,
@params['brackets_only'] ? true : false, @params['brackets_only'] ? true : false,
@params['count_pages'] ? true : false, @params['count_pages'] ? true : false,
@params['allow_uploads'] ? true : false @params['allow_uploads'] ? true : false
) )
redirect_show('HomePage', @params['address']) redirect_show('HomePage', @params['address'])
else else
redirect_show('HomePage') redirect_show('HomePage')
end end
end end
# Within a single page -------------------------------------------------------- # Within a single page --------------------------------------------------------
def cancel_edit def cancel_edit
@page.unlock @page.unlock
redirect_show redirect_show
end end
def edit def edit
if @page.nil? if @page.nil?
redirect_to :action => 'index' redirect_to :action => 'index'
elsif @page.locked?(Time.now) and not @params['break_lock'] elsif @page.locked?(Time.now) and not @params['break_lock']
redirect_to :web => @web_name, :action => 'locked', :id => @page_name redirect_to :web => @web_name, :action => 'locked', :id => @page_name
else else
@page.lock(Time.now, @author) @page.lock(Time.now, @author)
end end
end end
def locked def locked
# to template # to template
end end
def new def new
# to template # to template
end end
def pdf def pdf
page = wiki.read_page(@web_name, @page_name) page = wiki.read_page(@web_name, @page_name)
safe_page_name = @page.name.gsub(/\W/, '') 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_name = "#{safe_page_name}-#{@web.address}-#{@page.created_at.strftime('%Y-%m-%d-%H-%M-%S')}"
file_path = @wiki.storage_path + file_name file_path = @wiki.storage_path + file_name
export_page_to_tex("#{file_path}.tex") unless FileTest.exists?("#{file_path}.tex") export_page_to_tex("#{file_path}.tex") unless FileTest.exists?("#{file_path}.tex")
# NB: this is _very_ slow # NB: this is _very_ slow
convert_tex_to_pdf("#{file_path}.tex") convert_tex_to_pdf("#{file_path}.tex")
send_file "#{file_path}.pdf" send_file "#{file_path}.pdf"
end end
def print def print
# to template # to template
end end
def published def published
if @web.published if @web.published
@page = wiki.read_page(@web_name, @page_name || 'HomePage') @page = wiki.read_page(@web_name, @page_name || 'HomePage')
else else
redirect_show('HomePage') redirect_show('HomePage')
end end
end end
def revision def revision
get_page_and_revision get_page_and_revision
end end
def rollback def rollback
get_page_and_revision get_page_and_revision
end end
def save def save
redirect_to :action => 'index' if @page_name.nil? redirect_to :action => 'index' if @page_name.nil?
cookies['author'] = @params['author'] cookies['author'] = @params['author']
begin begin
page = @web.pages[@page_name] page = @web.pages[@page_name]
if @web.pages[@page_name] if @web.pages[@page_name]
wiki.revise_page( wiki.revise_page(
@web_name, @page_name, @params['content'], Time.now, @web_name, @page_name, @params['content'], Time.now,
Author.new(@params['author'], remote_ip) Author.new(@params['author'], remote_ip)
) )
page.unlock page.unlock
else else
wiki.write_page( wiki.write_page(
@web_name, @page_name, @params['content'], Time.now, @web_name, @page_name, @params['content'], Time.now,
Author.new(@params['author'], remote_ip) Author.new(@params['author'], remote_ip)
) )
end end
redirect_show(@page_name) redirect_show(@page_name)
rescue Instiki::ValidationError => e rescue Instiki::ValidationError => e
page.unlock if defined? page page.unlock if defined? page
flash[:error] = e flash[:error] = e
return_to_last_remembered return_to_last_remembered
end end
end end
def show def show
if @page if @page
begin begin
render_action 'page' render_action 'page'
# TODO this rescue should differentiate between errors due to rendering and errors in # 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) # the application itself (for application errors, it's better not to rescue the error at all)
rescue => e rescue => e
logger.error e logger.error e
if in_a_web? if in_a_web?
redirect_to :web => @web_name, :action => 'edit', redirect_to :web => @web_name, :action => 'edit',
:action_suffix => "#{CGI.escape(@page_name)}?msg=#{CGI.escape(e.message)}" :action_suffix => "#{CGI.escape(@page_name)}?msg=#{CGI.escape(e.message)}"
else else
raise e raise e
end end
end end
else else
redirect_to :web => @web_name, :action => 'new', :id => CGI.escape(@page_name) redirect_to :web => @web_name, :action => 'new', :id => CGI.escape(@page_name)
end end
end end
def tex def tex
@tex_content = RedClothForTex.new(@page.content).to_tex @tex_content = RedClothForTex.new(@page.content).to_tex
end end
private private
def convert_tex_to_pdf(tex_path) def convert_tex_to_pdf(tex_path)
# TODO remove earlier PDF files with the same prefix # TODO remove earlier PDF files with the same prefix
# TODO handle gracefully situation where pdflatex is not available # TODO handle gracefully situation where pdflatex is not available
logger.info `pdflatex --interaction=nonstopmode --output-directory #{File.dirname(tex_path)} #{File.basename(tex_path)}` logger.info `pdflatex --interaction=nonstopmode --output-directory #{File.dirname(tex_path)} #{File.basename(tex_path)}`
end end
def export_page_to_tex(file_path) def export_page_to_tex(file_path)
tex tex
File.open(file_path, 'w') { |f| f.write(render_to_string('wiki/tex')) } File.open(file_path, 'w') { |f| f.write(render_to_string('wiki/tex')) }
end end
def export_pages_as_zip(file_type, &block) def export_pages_as_zip(file_type, &block)
file_prefix = "#{@web.address}-#{file_type}-" file_prefix = "#{@web.address}-#{file_type}-"
timestamp = @web.revised_on.strftime('%Y-%m-%d-%H-%M-%S') timestamp = @web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')
file_path = @wiki.storage_path + file_prefix + timestamp + '.zip' file_path = @wiki.storage_path + file_prefix + timestamp + '.zip'
tmp_path = "#{file_path}.tmp" tmp_path = "#{file_path}.tmp"
Zip::ZipOutputStream.open(tmp_path) do |zip_out| Zip::ZipOutputStream.open(tmp_path) do |zip_out|
@web.select.by_name.each do |page| @web.select.by_name.each do |page|
zip_out.put_next_entry("#{page.name}.#{file_type}") zip_out.put_next_entry("#{page.name}.#{file_type}")
zip_out.puts(block.call(page)) zip_out.puts(block.call(page))
end end
# add an index file, if exporting to HTML # add an index file, if exporting to HTML
if file_type.to_s.downcase == 'html' if file_type.to_s.downcase == 'html'
zip_out.put_next_entry 'index.html' zip_out.put_next_entry 'index.html'
zip_out.puts <<-EOL zip_out.puts <<-EOL
<html> <html>
<head> <head>
<META HTTP-EQUIV="Refresh" CONTENT="0;URL=HomePage.#{file_type}"> <META HTTP-EQUIV="Refresh" CONTENT="0;URL=HomePage.#{file_type}">
</head> </head>
</html> </html>
EOL EOL
end end
end end
FileUtils.rm_rf(Dir[@wiki.storage_path + file_prefix + '*.zip']) FileUtils.rm_rf(Dir[@wiki.storage_path + file_prefix + '*.zip'])
FileUtils.mv(tmp_path, file_path) FileUtils.mv(tmp_path, file_path)
send_file file_path send_file file_path
end end
def export_web_to_tex(file_path) def export_web_to_tex(file_path)
@tex_content = table_of_contents(@web.pages['HomePage'].content.dup, render_tex_web) @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')) } File.open(file_path, 'w') { |f| f.write(render_to_string('wiki/tex_web')) }
end end
def get_page_and_revision def get_page_and_revision
@revision = @page.revisions[@params['rev'].to_i] @revision = @page.revisions[@params['rev'].to_i]
end end
def parse_category def parse_category
@categories = @web.categories @categories = @web.categories
@category = @params['category'] @category = @params['category']
if @categories.include?(@category) if @categories.include?(@category)
@pages_in_category = @web.select { |page| page.in_category?(@category) } @pages_in_category = @web.select { |page| page.in_category?(@category) }
@set_name = "category '#{@category}'" @set_name = "category '#{@category}'"
else else
@pages_in_category = PageSet.new(@web).by_name @pages_in_category = PageSet.new(@web).by_name
@set_name = 'the web' @set_name = 'the web'
end end
@category_links = @categories.map { |c| @category_links = @categories.map { |c|
if @category == c if @category == c
%{<span class="selected">#{c}</span>} %{<span class="selected">#{c}</span>}
else else
%{<a href="?category=#{c}">#{c}</a>} %{<a href="?category=#{c}">#{c}</a>}
end end
} }
end end
def password_check(password) def password_check(password)
if password == @web.password if password == @web.password
cookies['web_address'] = password cookies['web_address'] = password
true true
else else
false false
end end
end end
def redirect_show(page_name = @page_name, web = @web_name) def redirect_show(page_name = @page_name, web = @web_name)
redirect_to :web => web, :action => 'show', :id => CGI.escape(page_name) redirect_to :web => web, :action => 'show', :id => CGI.escape(page_name)
end end
def remote_ip def remote_ip
ip = @request.remote_ip ip = @request.remote_ip
logger.info(ip) logger.info(ip)
ip ip
end end
def render_rss(hide_description = false) def render_rss(hide_description = false)
@pages_by_revision = @web.select.by_revision.first(15) @pages_by_revision = @web.select.by_revision.first(15)
@hide_description = hide_description @hide_description = hide_description
@response.headers['Content-Type'] = 'text/xml' @response.headers['Content-Type'] = 'text/xml'
render 'wiki/rss_feed' render 'wiki/rss_feed'
end end
def render_tex_web def render_tex_web
@web.select.by_name.inject({}) do |tex_web, page| @web.select.by_name.inject({}) do |tex_web, page|
tex_web[page.name] = RedClothForTex.new(page.content).to_tex tex_web[page.name] = RedClothForTex.new(page.content).to_tex
tex_web tex_web
end end
end end
def render_to_string(template_name) def render_to_string(template_name)
add_variables_to_assigns add_variables_to_assigns
render template_name render template_name
@performed_render = false @performed_render = false
@template.render_file(template_name) @template.render_file(template_name)
end end
def truncate(text, length = 30, truncate_string = '...') def truncate(text, length = 30, truncate_string = '...')
if text.length > length then text[0..(length - 3)] + truncate_string else text end if text.length > length then text[0..(length - 3)] + truncate_string else text end
end end
end end

6
app/models/author.rb Executable file → Normal file
View file

@ -1,4 +1,4 @@
class Author < String class Author < String
attr_accessor :ip attr_accessor :ip
def initialize(name, ip) @ip = ip; super(name) end def initialize(name, ip) @ip = ip; super(name) end
end end

70
app/models/chunks/category.rb Executable file → Normal file
View file

@ -1,35 +1,35 @@
require 'chunks/chunk' require 'chunks/chunk'
# The category chunk looks for "category: news" on a line by # The category chunk looks for "category: news" on a line by
# itself and parses the terms after the ':' as categories. # itself and parses the terms after the ':' as categories.
# Other classes can search for Category chunks within # Other classes can search for Category chunks within
# rendered content to find out what categories this page # rendered content to find out what categories this page
# should be in. # should be in.
# #
# Category lines can be hidden using ':category: news', for example # Category lines can be hidden using ':category: news', for example
class Category < Chunk::Abstract class Category < Chunk::Abstract
def self.pattern() return /^(:)?category\s*:(.*)$/i end def self.pattern() return /^(:)?category\s*:(.*)$/i end
attr_reader :hidden, :list attr_reader :hidden, :list
def initialize(match_data) def initialize(match_data)
super(match_data) super(match_data)
@hidden = match_data[1] @hidden = match_data[1]
@list = match_data[2].split(',').map { |c| c.strip } @list = match_data[2].split(',').map { |c| c.strip }
end end
# If the chunk is hidden, erase the mask and return this chunk # If the chunk is hidden, erase the mask and return this chunk
# otherwise, surround it with a 'div' block. # otherwise, surround it with a 'div' block.
def unmask(content) def unmask(content)
return '' if hidden return '' if hidden
category_urls = @list.map{|category| url(category) }.join(', ') category_urls = @list.map{|category| url(category) }.join(', ')
replacement = '<div class="property"> category: ' + category_urls + '</div>' replacement = '<div class="property"> category: ' + category_urls + '</div>'
self if content.sub!(mask(content), replacement) self if content.sub!(mask(content), replacement)
end end
# TODO move presentation of page metadata to controller/view # TODO move presentation of page metadata to controller/view
def url(category) def url(category)
%{<a class="category_link" href="../list/?category=#{category}">#{category}</a>} %{<a class="category_link" href="../list/?category=#{category}">#{category}</a>}
end end
end end

80
app/models/chunks/chunk.rb Executable file → Normal file
View file

@ -1,40 +1,40 @@
require 'digest/md5' require 'digest/md5'
require 'uri/common' require 'uri/common'
# A chunk is a pattern of text that can be protected # A chunk is a pattern of text that can be protected
# and interrogated by a renderer. Each Chunk class has a # and interrogated by a renderer. Each Chunk class has a
# +pattern+ that states what sort of text it matches. # +pattern+ that states what sort of text it matches.
# Chunks are initalized by passing in the result of a # Chunks are initalized by passing in the result of a
# match by its pattern. # match by its pattern.
module Chunk module Chunk
class Abstract class Abstract
attr_reader :text attr_reader :text
def initialize(match_data) @text = match_data[0] end def initialize(match_data) @text = match_data[0] end
# Find all the chunks of the given type in content # Find all the chunks of the given type in content
# Each time the pattern is matched, create a new # Each time the pattern is matched, create a new
# chunk for it, and replace the occurance of the chunk # chunk for it, and replace the occurance of the chunk
# in this content with its mask. # in this content with its mask.
def self.apply_to(content) def self.apply_to(content)
content.gsub!( self.pattern ) do |match| content.gsub!( self.pattern ) do |match|
new_chunk = self.new($~) new_chunk = self.new($~)
content.chunks << new_chunk content.chunks << new_chunk
new_chunk.mask(content) new_chunk.mask(content)
end end
end end
def mask(content) def mask(content)
"chunk#{self.object_id}#{self.class.to_s.delete(':').downcase}chunk" "chunk#{self.object_id}#{self.class.to_s.delete(':').downcase}chunk"
end end
def revert(content) def revert(content)
content.sub!( Regexp.new(mask(content)), text ) content.sub!( Regexp.new(mask(content)), text )
end end
def unmask(content) def unmask(content)
self if revert(content) self if revert(content)
end end
end end
end end

76
app/models/chunks/engines.rb Executable file → Normal file
View file

@ -1,38 +1,38 @@
$: << File.dirname(__FILE__) + "../../libraries" $: << File.dirname(__FILE__) + "../../libraries"
require 'redcloth' require 'redcloth'
require 'bluecloth' require 'bluecloth'
require 'rdocsupport' require 'rdocsupport'
require 'chunks/chunk' require 'chunks/chunk'
# The markup engines are Chunks that call the one of RedCloth, BlueCloth # 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 # or RDoc to convert text. This markup occurs when the chunk is required
# to mask itself. # to mask itself.
module Engines module Engines
class Textile < Chunk::Abstract class Textile < Chunk::Abstract
def self.pattern() /^(.*)$/m end def self.pattern() /^(.*)$/m end
def mask(content) def mask(content)
RedCloth.new(text,content.options[:engine_opts]).to_html RedCloth.new(text,content.options[:engine_opts]).to_html
end end
def unmask(content) self end def unmask(content) self end
end end
class Markdown < Chunk::Abstract class Markdown < Chunk::Abstract
def self.pattern() /^(.*)$/m end def self.pattern() /^(.*)$/m end
def mask(content) def mask(content)
BlueCloth.new(text,content.options[:engine_opts]).to_html BlueCloth.new(text,content.options[:engine_opts]).to_html
end end
def unmask(content) self end def unmask(content) self end
end end
class RDoc < Chunk::Abstract class RDoc < Chunk::Abstract
def self.pattern() /^(.*)$/m end def self.pattern() /^(.*)$/m end
def mask(content) def mask(content)
RDocSupport::RDocFormatter.new(text).to_html RDocSupport::RDocFormatter.new(text).to_html
end end
def unmask(content) self end def unmask(content) self end
end end
MAP = { :textile => Textile, :markdown => Markdown, :rdoc => RDoc } MAP = { :textile => Textile, :markdown => Markdown, :rdoc => RDoc }
end end

58
app/models/chunks/include.rb Executable file → Normal file
View file

@ -1,29 +1,29 @@
require 'chunks/wiki' require 'chunks/wiki'
# Includes the contents of another page for rendering. # Includes the contents of another page for rendering.
# The include command looks like this: "[[!include PageName]]". # The include command looks like this: "[[!include PageName]]".
# It is a WikiLink since it refers to another page (PageName) # It is a WikiLink since it refers to another page (PageName)
# and the wiki content using this command must be notified # and the wiki content using this command must be notified
# of changes to that page. # of changes to that page.
# If the included page could not be found, a warning is displayed. # If the included page could not be found, a warning is displayed.
class Include < WikiChunk::WikiLink class Include < WikiChunk::WikiLink
def self.pattern() /^\[\[!include(.*)\]\]\s*$/i end def self.pattern() /^\[\[!include(.*)\]\]\s*$/i end
attr_reader :page_name attr_reader :page_name
def initialize(match_data) def initialize(match_data)
super(match_data) super(match_data)
@page_name = match_data[1].strip @page_name = match_data[1].strip
end end
# This replaces the [[!include PageName]] text with # This replaces the [[!include PageName]] text with
# the contents of PageName if it exists. Otherwise # the contents of PageName if it exists. Otherwise
# a warning is displayed. # a warning is displayed.
def mask(content) def mask(content)
page = content.web.pages[page_name] page = content.web.pages[page_name]
(page ? page.content : "<em>Could not include #{page_name}</em>") (page ? page.content : "<em>Could not include #{page_name}</em>")
end end
# Keep this chunk regardless of what happens. # Keep this chunk regardless of what happens.
def unmask(content) self end def unmask(content) self end
end end

38
app/models/chunks/literal.rb Executable file → Normal file
View file

@ -1,19 +1,19 @@
require 'chunks/chunk' require 'chunks/chunk'
# These are basic chunks that have a pattern and can be protected. # These are basic chunks that have a pattern and can be protected.
# They are used by rendering process to prevent wiki rendering # They are used by rendering process to prevent wiki rendering
# occuring within literal areas such as <code> and <pre> blocks # occuring within literal areas such as <code> and <pre> blocks
# and within HTML tags. # and within HTML tags.
module Literal module Literal
# A literal chunk that protects 'code' and 'pre' tags from wiki rendering. # A literal chunk that protects 'code' and 'pre' tags from wiki rendering.
class Pre < Chunk::Abstract class Pre < Chunk::Abstract
PRE_BLOCKS = "a|pre|code" PRE_BLOCKS = "a|pre|code"
def self.pattern() Regexp.new('<('+PRE_BLOCKS+')\b[^>]*?>.*?</\1>', Regexp::MULTILINE) end def self.pattern() Regexp.new('<('+PRE_BLOCKS+')\b[^>]*?>.*?</\1>', Regexp::MULTILINE) end
end end
# A literal chunk that protects HTML tags from wiki rendering. # A literal chunk that protects HTML tags from wiki rendering.
class Tags < Chunk::Abstract class Tags < Chunk::Abstract
TAGS = "a|img|em|strong|div|span|table|td|th|ul|ol|li|dl|dt|dd" 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 def self.pattern() Regexp.new('<(?:'+TAGS+')[^>]*?>', Regexp::MULTILINE) end
end end
end end

62
app/models/chunks/nowiki.rb Executable file → Normal file
View file

@ -1,31 +1,31 @@
require 'chunks/chunk' require 'chunks/chunk'
# This chunks allows certain parts of a wiki page to be hidden from the # 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 # rest of the rendering pipeline. It should be run at the beginning
# of the pipeline in `wiki_content.rb`. # of the pipeline in `wiki_content.rb`.
# #
# An example use of this chunk is to markup double brackets or # An example use of this chunk is to markup double brackets or
# auto URI links: # auto URI links:
# <nowiki>Here are [[double brackets]] and a URI: www.uri.org</nowiki> # <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 # 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. # so the `www.uri.org` and the double brackets will appear verbatim.
# #
# Author: Mark Reid <mark at threewordslong dot com> # Author: Mark Reid <mark at threewordslong dot com>
# Created: 8th June 2004 # Created: 8th June 2004
class NoWiki < Chunk::Abstract class NoWiki < Chunk::Abstract
def self.pattern() Regexp.new('<nowiki>(.*?)</nowiki>') end def self.pattern() Regexp.new('<nowiki>(.*?)</nowiki>') end
attr_reader :plain_text attr_reader :plain_text
def initialize(match_data) def initialize(match_data)
super(match_data) super(match_data)
@plain_text = match_data[1] @plain_text = match_data[1]
end end
# The nowiki content is not unmasked. This means the chunk will be reverted # The nowiki content is not unmasked. This means the chunk will be reverted
# using the plain text. # using the plain text.
def unmask(content) nil end def unmask(content) nil end
def revert(content) content.sub!(mask(content), plain_text) end def revert(content) content.sub!(mask(content), plain_text) end
end end

36
app/models/chunks/test.rb Executable file → Normal file
View file

@ -1,18 +1,18 @@
require 'test/unit' require 'test/unit'
class ChunkTest < Test::Unit::TestCase class ChunkTest < Test::Unit::TestCase
# Asserts a number of tests for the given type and text. # Asserts a number of tests for the given type and text.
def match(type, test_text, expected) def match(type, test_text, expected)
pattern = type.pattern pattern = type.pattern
assert_match(pattern, test_text) assert_match(pattern, test_text)
pattern =~ test_text # Previous assertion guarantees match pattern =~ test_text # Previous assertion guarantees match
chunk = type.new($~) chunk = type.new($~)
# Test if requested parts are correct. # Test if requested parts are correct.
for method_sym, value in expected do for method_sym, value in expected do
assert_respond_to(chunk, method_sym) assert_respond_to(chunk, method_sym)
assert_equal(value, chunk.method(method_sym).call, "Checking value of '#{method_sym}'") assert_equal(value, chunk.method(method_sym).call, "Checking value of '#{method_sym}'")
end end
end end
end end

358
app/models/chunks/uri.rb Executable file → Normal file
View file

@ -1,179 +1,179 @@
require 'chunks/chunk' require 'chunks/chunk'
# This wiki chunk matches arbitrary URIs, using patterns from the Ruby URI modules. # 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 # 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) # the links in various ways (shortening domain names, hiding email addresses)
# It matches email addresses and host.com.au domains without schemes (http://) # It matches email addresses and host.com.au domains without schemes (http://)
# but adds these on as required. # but adds these on as required.
# #
# The heuristic used to match a URI is designed to err on the side of caution. # 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 # 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 # 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 # to force a URI link by prefixing 'http://' to it than it is to escape and
# incorrectly marked up non-URI. # incorrectly marked up non-URI.
# #
# I'm using a part of the [ISO 3166-1 Standard][iso3166] for country name suffixes. # 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) # The generic names are from www.bnoack.com/data/countrycode2.html)
# [iso3166]: http://geotags.com/iso3166/ # [iso3166]: http://geotags.com/iso3166/
class URIChunk < Chunk::Abstract class URIChunk < Chunk::Abstract
include URI::REGEXP::PATTERN include URI::REGEXP::PATTERN
# this condition is to get rid of pesky warnings in tests # this condition is to get rid of pesky warnings in tests
unless defined? URIChunk::INTERNET_URI_REGEXP unless defined? URIChunk::INTERNET_URI_REGEXP
GENERIC = '(?:aero|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org)' 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)' 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 # These are needed otherwise HOST will match almost anything
TLDS = "(?:#{GENERIC}|#{COUNTRY})" TLDS = "(?:#{GENERIC}|#{COUNTRY})"
# Redefine USERINFO so that it must have non-zero length # Redefine USERINFO so that it must have non-zero length
USERINFO = "(?:[#{UNRESERVED};:&=+$,]|#{ESCAPED})+" USERINFO = "(?:[#{UNRESERVED};:&=+$,]|#{ESCAPED})+"
# unreserved_no_ending = alphanum | mark, but URI_ENDING [)!] excluded # unreserved_no_ending = alphanum | mark, but URI_ENDING [)!] excluded
UNRESERVED_NO_ENDING = "-_.~*'(#{ALNUM}" UNRESERVED_NO_ENDING = "-_.~*'(#{ALNUM}"
# this ensures that query or fragment do not end with URI_ENDING # this ensures that query or fragment do not end with URI_ENDING
# and enable us to use a much simpler self.pattern Regexp # and enable us to use a much simpler self.pattern Regexp
# uric_no_ending = reserved | unreserved_no_ending | escaped # uric_no_ending = reserved | unreserved_no_ending | escaped
URIC_NO_ENDING = "(?:[#{UNRESERVED_NO_ENDING}#{RESERVED}]|#{ESCAPED})" URIC_NO_ENDING = "(?:[#{UNRESERVED_NO_ENDING}#{RESERVED}]|#{ESCAPED})"
# query = *uric # query = *uric
QUERY = "#{URIC_NO_ENDING}*" QUERY = "#{URIC_NO_ENDING}*"
# fragment = *uric # fragment = *uric
FRAGMENT = "#{URIC_NO_ENDING}*" FRAGMENT = "#{URIC_NO_ENDING}*"
# DOMLABEL is defined in the ruby uri library, TLDS is defined above # DOMLABEL is defined in the ruby uri library, TLDS is defined above
INTERNET_HOSTNAME = "(?:#{DOMLABEL}\\.)+#{TLDS}" INTERNET_HOSTNAME = "(?:#{DOMLABEL}\\.)+#{TLDS}"
# Correct a typo bug in ruby 1.8.x lib/uri/common.rb # Correct a typo bug in ruby 1.8.x lib/uri/common.rb
PORT = '\\d*' PORT = '\\d*'
INTERNET_URI = INTERNET_URI =
"(?:(#{SCHEME}):/{0,2})?" + # Optional scheme: (\1) "(?:(#{SCHEME}):/{0,2})?" + # Optional scheme: (\1)
"(?:(#{USERINFO})@)?" + # Optional userinfo@ (\2) "(?:(#{USERINFO})@)?" + # Optional userinfo@ (\2)
"(#{INTERNET_HOSTNAME})" + # Mandatory hostname (\3) "(#{INTERNET_HOSTNAME})" + # Mandatory hostname (\3)
"(?::(#{PORT}))?" + # Optional :port (\4) "(?::(#{PORT}))?" + # Optional :port (\4)
"(#{ABS_PATH})?" + # Optional absolute path (\5) "(#{ABS_PATH})?" + # Optional absolute path (\5)
"(?:\\?(#{QUERY}))?" + # Optional ?query (\6) "(?:\\?(#{QUERY}))?" + # Optional ?query (\6)
"(?:\\#(#{FRAGMENT}))?" # Optional #fragment (\7) "(?:\\#(#{FRAGMENT}))?" # Optional #fragment (\7)
TEXTILE_SYNTAX_PREFIX = '(!)?' TEXTILE_SYNTAX_PREFIX = '(!)?'
INTERNET_URI_REGEXP = Regexp.new(TEXTILE_SYNTAX_PREFIX + INTERNET_URI, Regexp::EXTENDED, 'N') INTERNET_URI_REGEXP = Regexp.new(TEXTILE_SYNTAX_PREFIX + INTERNET_URI, Regexp::EXTENDED, 'N')
end end
def URIChunk.pattern def URIChunk.pattern
INTERNET_URI_REGEXP INTERNET_URI_REGEXP
end end
attr_reader :user, :host, :port, :path, :query, :fragment, :link_text attr_reader :user, :host, :port, :path, :query, :fragment, :link_text
def self.apply_to(content) def self.apply_to(content)
content.gsub!( self.pattern ) do |matched_text| content.gsub!( self.pattern ) do |matched_text|
chunk = self.new($~) chunk = self.new($~)
if chunk.textile_url? or chunk.textile_image? if chunk.textile_url? or chunk.textile_image?
# do not substitute # do not substitute
matched_text matched_text
else else
content.chunks << chunk content.chunks << chunk
chunk.mask(content) chunk.mask(content)
end end
end end
end end
def initialize(match_data) def initialize(match_data)
super(match_data) super(match_data)
@link_text = match_data[0] @link_text = match_data[0]
@textile_prefix, @original_scheme, @user, @host, @port, @path, @query, @fragment = @textile_prefix, @original_scheme, @user, @host, @port, @path, @query, @fragment =
match_data[1..-1] match_data[1..-1]
treat_trailing_character treat_trailing_character
end end
def textile_url? def textile_url?
@textile_prefix == '":' @textile_prefix == '":'
end end
def textile_image? def textile_image?
@textile_prefix == '!' and @trailing_punctuation == '!' @textile_prefix == '!' and @trailing_punctuation == '!'
end end
def treat_trailing_character def treat_trailing_character
# If the last character matched by URI pattern is in ! or ), this may be part of the markup, # 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 # not a URL. We should handle it as such. It is possible to do it by a regexp, but
# much easier to do programmatically # much easier to do programmatically
last_char = @link_text[-1..-1] last_char = @link_text[-1..-1]
if last_char == ')' or last_char == '!' if last_char == ')' or last_char == '!'
@trailing_punctuation = last_char @trailing_punctuation = last_char
@link_text.chop! @link_text.chop!
[@original_scheme, @user, @host, @port, @path, @query, @fragment].compact.last.chop! [@original_scheme, @user, @host, @port, @path, @query, @fragment].compact.last.chop!
end end
end end
# If the text should be escaped then don't keep this chunk. # If the text should be escaped then don't keep this chunk.
# Otherwise only keep this chunk if it was substituted back into the # Otherwise only keep this chunk if it was substituted back into the
# content. # content.
def unmask(content) def unmask(content)
return nil if escaped_text return nil if escaped_text
return self if content.sub!(mask(content), "<a href=\"#{uri}\">#{link_text}</a>") return self if content.sub!(mask(content), "<a href=\"#{uri}\">#{link_text}</a>")
end end
# If there is no hostname in the URI, do not render it # If there is no hostname in the URI, do not render it
# It's probably only contains the scheme, eg 'something:' # It's probably only contains the scheme, eg 'something:'
def escaped_text() ( host.nil? ? @uri : nil ) end def escaped_text() ( host.nil? ? @uri : nil ) end
def scheme def scheme
@original_scheme or (@user ? 'mailto' : 'http') @original_scheme or (@user ? 'mailto' : 'http')
end end
def scheme_delimiter def scheme_delimiter
scheme == 'mailto' ? ':' : '://' scheme == 'mailto' ? ':' : '://'
end end
def user_delimiter def user_delimiter
'@' unless @user.nil? '@' unless @user.nil?
end end
def port_delimiter def port_delimiter
':' unless @port.nil? ':' unless @port.nil?
end end
def query_delimiter def query_delimiter
'?' unless @query.nil? '?' unless @query.nil?
end end
def uri def uri
[scheme, scheme_delimiter, user, user_delimiter, host, port_delimiter, port, path, [scheme, scheme_delimiter, user, user_delimiter, host, port_delimiter, port, path,
query_delimiter, query].compact.join query_delimiter, query].compact.join
end end
end end
# uri with mandatory scheme but less restrictive hostname, like # uri with mandatory scheme but less restrictive hostname, like
# http://localhost:2500/blah.html # http://localhost:2500/blah.html
class LocalURIChunk < URIChunk class LocalURIChunk < URIChunk
unless defined? LocalURIChunk::LOCAL_URI_REGEXP unless defined? LocalURIChunk::LOCAL_URI_REGEXP
# hostname can be just a simple word like 'localhost' # hostname can be just a simple word like 'localhost'
ANY_HOSTNAME = "(?:#{DOMLABEL}\\.)*#{TOPLABEL}\\.?" ANY_HOSTNAME = "(?:#{DOMLABEL}\\.)*#{TOPLABEL}\\.?"
# The basic URI expression as a string # The basic URI expression as a string
# Scheme and hostname are mandatory # Scheme and hostname are mandatory
LOCAL_URI = LOCAL_URI =
"(?:(#{SCHEME})://)+" + # Mandatory scheme:// (\1) "(?:(#{SCHEME})://)+" + # Mandatory scheme:// (\1)
"(?:(#{USERINFO})@)?" + # Optional userinfo@ (\2) "(?:(#{USERINFO})@)?" + # Optional userinfo@ (\2)
"(#{ANY_HOSTNAME})" + # Mandatory hostname (\3) "(#{ANY_HOSTNAME})" + # Mandatory hostname (\3)
"(?::(#{PORT}))?" + # Optional :port (\4) "(?::(#{PORT}))?" + # Optional :port (\4)
"(#{ABS_PATH})?" + # Optional absolute path (\5) "(#{ABS_PATH})?" + # Optional absolute path (\5)
"(?:\\?(#{QUERY}))?" + # Optional ?query (\6) "(?:\\?(#{QUERY}))?" + # Optional ?query (\6)
"(?:\\#(#{FRAGMENT}))?" # Optional #fragment (\7) "(?:\\#(#{FRAGMENT}))?" # Optional #fragment (\7)
LOCAL_URI_REGEXP = Regexp.new(TEXTILE_SYNTAX_PREFIX + LOCAL_URI, Regexp::EXTENDED, 'N') LOCAL_URI_REGEXP = Regexp.new(TEXTILE_SYNTAX_PREFIX + LOCAL_URI, Regexp::EXTENDED, 'N')
end end
def LocalURIChunk.pattern def LocalURIChunk.pattern
LOCAL_URI_REGEXP LOCAL_URI_REGEXP
end end
end end

284
app/models/chunks/wiki.rb Executable file → Normal file
View file

@ -1,142 +1,142 @@
require 'wiki_words' require 'wiki_words'
require 'chunks/chunk' require 'chunks/chunk'
require 'chunks/wiki' require 'chunks/wiki'
require 'cgi' require 'cgi'
# Contains all the methods for finding and replacing wiki related links. # Contains all the methods for finding and replacing wiki related links.
module WikiChunk module WikiChunk
include Chunk include Chunk
# A wiki link is the top-level class for anything that refers to # A wiki link is the top-level class for anything that refers to
# another wiki page. # another wiki page.
class WikiLink < Chunk::Abstract class WikiLink < Chunk::Abstract
attr_reader :page_name, :link_text, :link_type attr_reader :page_name, :link_text, :link_type
def initialize(*args) def initialize(*args)
super super
@link_type = 'show' @link_type = 'show'
end end
def self.apply_to(content) def self.apply_to(content)
content.gsub!( self.pattern ) do |matched_text| content.gsub!( self.pattern ) do |matched_text|
chunk = self.new($~) chunk = self.new($~)
if chunk.textile_url? if chunk.textile_url?
# do not substitute # do not substitute
matched_text matched_text
else else
content.chunks << chunk content.chunks << chunk
chunk.mask(content) chunk.mask(content)
end end
end end
end end
def textile_url? def textile_url?
not @textile_link_suffix.nil? not @textile_link_suffix.nil?
end end
# By default, no escaped text # By default, no escaped text
def escaped_text() nil end def escaped_text() nil end
# Replace link with a mask, but if the word is escaped, then don't replace it # Replace link with a mask, but if the word is escaped, then don't replace it
def mask(content) def mask(content)
escaped_text || super(content) escaped_text || super(content)
end end
def revert(content) content.sub!(mask(content), text) end def revert(content) content.sub!(mask(content), text) end
# Do not keep this chunk if it is escaped. # Do not keep this chunk if it is escaped.
# Otherwise, pass the link procedure a page_name and link_text and # Otherwise, pass the link procedure a page_name and link_text and
# get back a string of HTML to replace the mask with. # get back a string of HTML to replace the mask with.
def unmask(content) def unmask(content)
if escaped_text if escaped_text
return self return self
else else
chunk_found = content.sub!(mask(content)) do |match| chunk_found = content.sub!(mask(content)) do |match|
content.page_link(page_name, link_text, link_type) content.page_link(page_name, link_text, link_type)
end end
if chunk_found if chunk_found
return self return self
else else
return nil return nil
end end
end end
end end
end end
# This chunk matches a WikiWord. WikiWords can be escaped # This chunk matches a WikiWord. WikiWords can be escaped
# by prepending a '\'. When this is the case, the +escaped_text+ # by prepending a '\'. When this is the case, the +escaped_text+
# method will return the WikiWord instead of the usual +nil+. # method will return the WikiWord instead of the usual +nil+.
# The +page_name+ method returns the matched WikiWord. # The +page_name+ method returns the matched WikiWord.
class Word < WikiLink class Word < WikiLink
unless defined? WIKI_LINK unless defined? WIKI_LINK
WIKI_WORD = Regexp.new('(":)?(\\\\)?(' + WikiWords::WIKI_WORD_PATTERN + ')\b', 0, "utf-8") WIKI_WORD = Regexp.new('(":)?(\\\\)?(' + WikiWords::WIKI_WORD_PATTERN + ')\b', 0, "utf-8")
end end
def self.pattern def self.pattern
WIKI_WORD WIKI_WORD
end end
def initialize(match_data) def initialize(match_data)
super(match_data) super(match_data)
@textile_link_suffix, @escape, @page_name = match_data[1..3] @textile_link_suffix, @escape, @page_name = match_data[1..3]
end end
def escaped_text def escaped_text
page_name unless @escape.nil? page_name unless @escape.nil?
end end
def link_text() WikiWords.separate(page_name) end def link_text() WikiWords.separate(page_name) end
end end
# This chunk handles [[bracketted wiki words]] and # This chunk handles [[bracketted wiki words]] and
# [[AliasedWords|aliased wiki words]]. The first part of an # [[AliasedWords|aliased wiki words]]. The first part of an
# aliased wiki word must be a WikiWord. If the WikiWord # aliased wiki word must be a WikiWord. If the WikiWord
# is aliased, the +link_text+ field will contain the # is aliased, the +link_text+ field will contain the
# alias, otherwise +link_text+ will contain the entire # alias, otherwise +link_text+ will contain the entire
# contents within the double brackets. # contents within the double brackets.
# #
# NOTE: This chunk must be tested before WikiWord since # NOTE: This chunk must be tested before WikiWord since
# a WikiWords can be a substring of a WikiLink. # a WikiWords can be a substring of a WikiLink.
class Link < WikiLink class Link < WikiLink
unless defined? WIKI_LINK unless defined? WIKI_LINK
WIKI_LINK = /(":)?\[\[([^\]]+)\]\]/ WIKI_LINK = /(":)?\[\[([^\]]+)\]\]/
LINK_TYPE_SEPARATION = Regexp.new('^(.+):((file)|(pic))$', 0, 'utf-8') LINK_TYPE_SEPARATION = Regexp.new('^(.+):((file)|(pic))$', 0, 'utf-8')
ALIAS_SEPARATION = Regexp.new('^(.+)\|(.+)$', 0, 'utf-8') ALIAS_SEPARATION = Regexp.new('^(.+)\|(.+)$', 0, 'utf-8')
end end
def self.pattern() WIKI_LINK end def self.pattern() WIKI_LINK end
def initialize(match_data) def initialize(match_data)
super(match_data) super(match_data)
@textile_link_suffix, @page_name = match_data[1..2] @textile_link_suffix, @page_name = match_data[1..2]
@link_text = @page_name @link_text = @page_name
separate_link_type separate_link_type
separate_alias separate_alias
end end
private private
# if link wihin the brackets has a form of [[filename:file]] or [[filename:pic]], # if link wihin the brackets has a form of [[filename:file]] or [[filename:pic]],
# this means a link to a picture or a file # this means a link to a picture or a file
def separate_link_type def separate_link_type
link_type_match = LINK_TYPE_SEPARATION.match(@page_name) link_type_match = LINK_TYPE_SEPARATION.match(@page_name)
if link_type_match if link_type_match
@link_text = @page_name = link_type_match[1] @link_text = @page_name = link_type_match[1]
@link_type = link_type_match[2..3].compact[0] @link_type = link_type_match[2..3].compact[0]
end end
end end
# link text may be different from page name. this will look like [[actual page|link text]] # link text may be different from page name. this will look like [[actual page|link text]]
def separate_alias def separate_alias
alias_match = ALIAS_SEPARATION.match(@page_name) alias_match = ALIAS_SEPARATION.match(@page_name)
if alias_match if alias_match
@page_name, @link_text = alias_match[1..2] @page_name, @link_text = alias_match[1..2]
end end
# note that [[filename|link text:file]] is also supported # note that [[filename|link text:file]] is also supported
end end
end end
end end

View file

@ -1,45 +1,45 @@
require 'instiki_errors' require 'instiki_errors'
class FileYard class FileYard
attr_reader :files_path attr_reader :files_path
def initialize(files_path) def initialize(files_path)
@files_path = files_path @files_path = files_path
@files = Dir["#{files_path}/*"].collect{|path| File.basename(path) if File.file?(path) }.compact @files = Dir["#{files_path}/*"].collect{|path| File.basename(path) if File.file?(path) }.compact
end end
def upload_file(name, io) def upload_file(name, io)
sanitize_file_name(name) sanitize_file_name(name)
if io.kind_of?(Tempfile) if io.kind_of?(Tempfile)
io.close io.close
FileUtils.mv(io.path, file_path(name)) FileUtils.mv(io.path, file_path(name))
else else
File.open(file_path(name), 'wb') { |f| f.write(io.read) } File.open(file_path(name), 'wb') { |f| f.write(io.read) }
end end
# just in case, restrict read access and prohibit write access to the uploaded file # just in case, restrict read access and prohibit write access to the uploaded file
FileUtils.chmod(0440, file_path(name)) FileUtils.chmod(0440, file_path(name))
end end
def files def files
Dir["#{files_path}/*"].collect{|path| File.basename(path) if File.file?(path)}.compact Dir["#{files_path}/*"].collect{|path| File.basename(path) if File.file?(path)}.compact
end end
def has_file?(name) def has_file?(name)
files.include?(name) files.include?(name)
end end
def file_path(name) def file_path(name)
"#{files_path}/#{name}" "#{files_path}/#{name}"
end end
SANE_FILE_NAME = /[-_\.A-Za-z0-9]{1,255}/ SANE_FILE_NAME = /[-_\.A-Za-z0-9]{1,255}/
def sanitize_file_name(name) def sanitize_file_name(name)
unless name =~ SANE_FILE_NAME unless name =~ SANE_FILE_NAME
raise Instiki::ValidationError.new("Invalid file name: '#{name}'.\n" + raise Instiki::ValidationError.new("Invalid file name: '#{name}'.\n" +
"Only latin characters, digits, dots, underscores and dashes are accepted.") "Only latin characters, digits, dots, underscores and dashes are accepted.")
end end
end end
end end

184
app/models/page.rb Executable file → Normal file
View file

@ -1,92 +1,92 @@
require 'date' require 'date'
require 'page_lock' require 'page_lock'
require 'revision' require 'revision'
require 'wiki_words' require 'wiki_words'
require 'chunks/wiki' require 'chunks/wiki'
class Page class Page
include PageLock include PageLock
attr_reader :name, :web attr_reader :name, :web
attr_accessor :revisions attr_accessor :revisions
def initialize(web, name, content, created_at, author) def initialize(web, name, content, created_at, author)
@web, @name, @revisions = web, name, [] @web, @name, @revisions = web, name, []
revise(content, created_at, author) revise(content, created_at, author)
end end
def revise(content, created_at, author) def revise(content, created_at, author)
if not @revisions.empty? and content == @revisions.last.content if not @revisions.empty? and content == @revisions.last.content
raise Instiki::ValidationError.new( raise Instiki::ValidationError.new(
"You have tried to save page '#{name}' without changing its content") "You have tried to save page '#{name}' without changing its content")
end end
# A user may change a page, look at it and make some more changes - several times. # 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 # 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 # by the same author, not more than 30 minutes ago, then update the last revision instead of
# creating a new one # creating a new one
if !@revisions.empty? && continous_revision?(created_at, author) if !@revisions.empty? && continous_revision?(created_at, author)
@revisions.last.created_at = created_at @revisions.last.created_at = created_at
@revisions.last.content = content @revisions.last.content = content
@revisions.last.clear_display_cache @revisions.last.clear_display_cache
else else
@revisions << Revision.new(self, @revisions.length, content, created_at, author) @revisions << Revision.new(self, @revisions.length, content, created_at, author)
end end
web.refresh_pages_with_references(name) if @revisions.length == 1 web.refresh_pages_with_references(name) if @revisions.length == 1
end end
def rollback(revision_number, created_at, author_ip = nil) def rollback(revision_number, created_at, author_ip = nil)
roll_back_revision = @revisions[revision_number].dup roll_back_revision = @revisions[revision_number].dup
revise(roll_back_revision.content, created_at, Author.new(roll_back_revision.author, author_ip)) revise(roll_back_revision.content, created_at, Author.new(roll_back_revision.author, author_ip))
end end
def revisions? def revisions?
revisions.length > 1 revisions.length > 1
end end
def revised_on def revised_on
created_on created_on
end end
def in_category?(cat) def in_category?(cat)
cat.nil? || cat.empty? || categories.include?(cat) cat.nil? || cat.empty? || categories.include?(cat)
end end
def categories def categories
display_content.find_chunks(Category).map { |cat| cat.list }.flatten display_content.find_chunks(Category).map { |cat| cat.list }.flatten
end end
def authors def authors
revisions.collect { |rev| rev.author } revisions.collect { |rev| rev.author }
end end
def references def references
web.select.pages_that_reference(name) web.select.pages_that_reference(name)
end end
# Returns the original wiki-word name as separate words, so "MyPage" becomes "My Page". # Returns the original wiki-word name as separate words, so "MyPage" becomes "My Page".
def plain_name def plain_name
web.brackets_only ? name : WikiWords.separate(name) web.brackets_only ? name : WikiWords.separate(name)
end end
def link(options = {}) def link(options = {})
web.make_link(name, nil, options) web.make_link(name, nil, options)
end end
def author_link(options = {}) def author_link(options = {})
web.make_link(author, nil, options) web.make_link(author, nil, options)
end end
private private
def continous_revision?(created_at, author) def continous_revision?(created_at, author)
@revisions.last.author == author && @revisions.last.created_at + 30.minutes > created_at @revisions.last.author == author && @revisions.last.created_at + 30.minutes > created_at
end end
# Forward method calls to the current revision, so the page responds to all revision calls # Forward method calls to the current revision, so the page responds to all revision calls
def method_missing(method_symbol) def method_missing(method_symbol)
revisions.last.send(method_symbol) revisions.last.send(method_symbol)
end end
end end

46
app/models/page_lock.rb Executable file → Normal file
View file

@ -1,24 +1,24 @@
# Contains all the lock methods to be mixed in with the page # Contains all the lock methods to be mixed in with the page
module PageLock module PageLock
LOCKING_PERIOD = 30 * 60 # 30 minutes LOCKING_PERIOD = 30 * 60 # 30 minutes
def lock(time, locked_by) def lock(time, locked_by)
@locked_at, @locked_by = time, locked_by @locked_at, @locked_by = time, locked_by
end end
def lock_duration(time) def lock_duration(time)
((time - @locked_at) / 60).to_i unless @locked_at.nil? ((time - @locked_at) / 60).to_i unless @locked_at.nil?
end end
def unlock def unlock
@locked_at = nil @locked_at = nil
end end
def locked?(comparison_time) def locked?(comparison_time)
@locked_at + LOCKING_PERIOD > comparison_time unless @locked_at.nil? @locked_at + LOCKING_PERIOD > comparison_time unless @locked_at.nil?
end end
def locked_by_link def locked_by_link
web.make_link(@locked_by) web.make_link(@locked_by)
end end
end end

144
app/models/page_set.rb Executable file → Normal file
View file

@ -1,73 +1,73 @@
# Container for a set of pages with methods for manipulation. # Container for a set of pages with methods for manipulation.
class PageSet < Array class PageSet < Array
attr_reader :web attr_reader :web
def initialize(web, pages = nil, condition = nil) def initialize(web, pages = nil, condition = nil)
@web = web @web = web
# if pages is not specified, make a list of all pages in the web # if pages is not specified, make a list of all pages in the web
if pages.nil? if pages.nil?
super(web.pages.values) super(web.pages.values)
# otherwise use specified pages and condition to produce a set of pages # otherwise use specified pages and condition to produce a set of pages
elsif condition.nil? elsif condition.nil?
super(pages) super(pages)
else else
super(pages.select { |page| condition[page] }) super(pages.select { |page| condition[page] })
end end
end end
def most_recent_revision def most_recent_revision
self.map { |page| page.created_at }.max || Time.at(0) self.map { |page| page.created_at }.max || Time.at(0)
end end
def by_name def by_name
PageSet.new(@web, sort_by { |page| page.name }) PageSet.new(@web, sort_by { |page| page.name })
end end
alias :sort :by_name alias :sort :by_name
def by_revision def by_revision
PageSet.new(@web, sort_by { |page| page.created_at }).reverse PageSet.new(@web, sort_by { |page| page.created_at }).reverse
end end
def pages_that_reference(page_name) def pages_that_reference(page_name)
self.select { |page| page.wiki_words.include?(page_name) } self.select { |page| page.wiki_words.include?(page_name) }
end end
def pages_authored_by(author) def pages_authored_by(author)
self.select { |page| page.authors.include?(author) } self.select { |page| page.authors.include?(author) }
end end
def characters def characters
self.inject(0) { |chars,page| chars += page.content.size } self.inject(0) { |chars,page| chars += page.content.size }
end end
# Returns all the orphaned pages in this page set. That is, # Returns all the orphaned pages in this page set. That is,
# pages in this set for which there is no reference in the web. # pages in this set for which there is no reference in the web.
# The HomePage and author pages are always assumed to have # The HomePage and author pages are always assumed to have
# references and so cannot be orphans # references and so cannot be orphans
def orphaned_pages def orphaned_pages
references = web.select.wiki_words + ["HomePage"] + web.select.authors references = web.select.wiki_words + ["HomePage"] + web.select.authors
self.reject { |page| references.include?(page.name) } self.reject { |page| references.include?(page.name) }
end end
# Returns all the wiki words in this page set for which # Returns all the wiki words in this page set for which
# there are no pages in this page set's web # there are no pages in this page set's web
def wanted_pages def wanted_pages
wiki_words - web.select.names wiki_words - web.select.names
end end
def names def names
self.map { |page| page.name } self.map { |page| page.name }
end end
def wiki_words def wiki_words
self.inject([]) { |wiki_words, page| wiki_words << page.wiki_words }.flatten.uniq self.inject([]) { |wiki_words, page| wiki_words << page.wiki_words }.flatten.uniq
end end
def authors def authors
self.inject([]) { |authors, page| authors << page.authors }.flatten.uniq.sort self.inject([]) { |authors, page| authors << page.authors }.flatten.uniq.sort
end end
end end

166
app/models/revision.rb Executable file → Normal file
View file

@ -1,83 +1,83 @@
require 'diff' require 'diff'
require 'wiki_content' require 'wiki_content'
require 'chunks/wiki' require 'chunks/wiki'
require 'date' require 'date'
require 'author' require 'author'
require 'page' require 'page'
class Revision class Revision
attr_accessor :page, :number, :content, :created_at, :author attr_accessor :page, :number, :content, :created_at, :author
def initialize(page, number, content, created_at, author) def initialize(page, number, content, created_at, author)
@page, @number, @created_at, @author = page, number, created_at, author @page, @number, @created_at, @author = page, number, created_at, author
self.content = content self.content = content
end end
def created_on def created_on
Date.new(@created_at.year, @created_at.mon, @created_at.day) Date.new(@created_at.year, @created_at.mon, @created_at.day)
end end
def pretty_created_at def pretty_created_at
# Must use DateTime because Time doesn't support %e on at least some platforms # Must use DateTime because Time doesn't support %e on at least some platforms
DateTime.new( DateTime.new(
@created_at.year, @created_at.mon, @created_at.day, @created_at.hour, @created_at.min @created_at.year, @created_at.mon, @created_at.day, @created_at.hour, @created_at.min
).strftime "%B %e, %Y %H:%M" ).strftime "%B %e, %Y %H:%M"
end end
def next_revision def next_revision
page.revisions[number + 1] page.revisions[number + 1]
end end
def previous_revision def previous_revision
number > 0 ? page.revisions[number - 1] : nil number > 0 ? page.revisions[number - 1] : nil
end end
# Returns an array of all the WikiWords present in the content of this revision. # Returns an array of all the WikiWords present in the content of this revision.
def wiki_words def wiki_words
unless @wiki_words_cache unless @wiki_words_cache
wiki_chunks = display_content.find_chunks(WikiChunk::WikiLink) wiki_chunks = display_content.find_chunks(WikiChunk::WikiLink)
@wiki_words_cache = wiki_chunks.map { |c| ( c.escaped_text ? nil : c.page_name ) }.compact.uniq @wiki_words_cache = wiki_chunks.map { |c| ( c.escaped_text ? nil : c.page_name ) }.compact.uniq
end end
@wiki_words_cache @wiki_words_cache
end end
# Returns an array of all the WikiWords present in the content of this revision. # Returns an array of all the WikiWords present in the content of this revision.
# that already exists as a page in the web. # that already exists as a page in the web.
def existing_pages def existing_pages
wiki_words.select { |wiki_word| page.web.pages[wiki_word] } wiki_words.select { |wiki_word| page.web.pages[wiki_word] }
end end
# Returns an array of all the WikiWords present in the content of this revision # 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. # that *doesn't* already exists as a page in the web.
def unexisting_pages def unexisting_pages
wiki_words - existing_pages wiki_words - existing_pages
end end
# Explicit check for new type of display cache with find_chunks method. # Explicit check for new type of display cache with find_chunks method.
# Ensures new version works with older snapshots. # Ensures new version works with older snapshots.
def display_content def display_content
unless @display_cache && @display_cache.respond_to?(:find_chunks) unless @display_cache && @display_cache.respond_to?(:find_chunks)
@display_cache = WikiContent.new(self) @display_cache = WikiContent.new(self)
end end
@display_cache @display_cache
end end
def display_diff def display_diff
previous_revision ? HTMLDiff.diff(previous_revision.display_content, display_content) : display_content previous_revision ? HTMLDiff.diff(previous_revision.display_content, display_content) : display_content
end end
def clear_display_cache def clear_display_cache
@display_cache = @published_cache = @wiki_words_cache = nil @display_cache = @published_cache = @wiki_words_cache = nil
end end
def display_published def display_published
@published_cache = WikiContent.new(self, {:mode => :publish}) if @published_cache.nil? @published_cache = WikiContent.new(self, {:mode => :publish}) if @published_cache.nil?
@published_cache @published_cache
end end
def display_content_for_export def display_content_for_export
WikiContent.new(self, {:mode => :export} ) WikiContent.new(self, {:mode => :export} )
end end
end end

308
app/models/web.rb Executable file → Normal file
View file

@ -1,155 +1,155 @@
require "cgi" require "cgi"
require "page" require "page"
require "page_set" require "page_set"
require "wiki_words" require "wiki_words"
require "zip/zip" require "zip/zip"
class Web class Web
attr_accessor :name, :address, :password, :markup, :color, :safe_mode, :pages attr_accessor :name, :address, :password, :markup, :color, :safe_mode, :pages
attr_accessor :additional_style, :published, :brackets_only, :count_pages, :allow_uploads attr_accessor :additional_style, :published, :brackets_only, :count_pages, :allow_uploads
def initialize(parent_wiki, name, address, password = nil) def initialize(parent_wiki, name, address, password = nil)
@wiki, @name, @address, @password = parent_wiki, name, address, password @wiki, @name, @address, @password = parent_wiki, name, address, password
# default values # default values
@markup = :textile @markup = :textile
@color = '008B26' @color = '008B26'
@safe_mode = false @safe_mode = false
@pages = {} @pages = {}
@allow_uploads = true @allow_uploads = true
@additional_style = nil @additional_style = nil
@published = false @published = false
@brackets_only = false @brackets_only = false
@count_pages = false @count_pages = false
@allow_uploads = true @allow_uploads = true
end end
def add_page(page) def add_page(page)
@pages[page.name] = page @pages[page.name] = page
end end
def remove_pages(pages_to_be_removed) def remove_pages(pages_to_be_removed)
pages.delete_if { |page_name, page| pages_to_be_removed.include?(page) } pages.delete_if { |page_name, page| pages_to_be_removed.include?(page) }
end end
def select(&condition) def select(&condition)
PageSet.new(self, @pages.values, condition) PageSet.new(self, @pages.values, condition)
end end
def revised_on def revised_on
select.most_recent_revision select.most_recent_revision
end end
def authors def authors
select.authors select.authors
end end
def categories def categories
select.map { |page| page.categories }.flatten.uniq.sort select.map { |page| page.categories }.flatten.uniq.sort
end end
# Create a link for the given page name and link text based # Create a link for the given page name and link text based
# on the render mode in options and whether the page exists # on the render mode in options and whether the page exists
# in the this web. # in the this web.
def make_link(name, text = nil, options = {}) def make_link(name, text = nil, options = {})
text = CGI.escapeHTML(text || WikiWords.separate(name)) text = CGI.escapeHTML(text || WikiWords.separate(name))
mode = options[:mode] mode = options[:mode]
link_type = options[:link_type] || 'show' link_type = options[:link_type] || 'show'
case link_type case link_type
when 'show' when 'show'
make_page_link(mode, name, text) make_page_link(mode, name, text)
when 'file' when 'file'
make_file_link(mode, name, text) make_file_link(mode, name, text)
when 'pic' when 'pic'
make_pic_link(mode, name, text) make_pic_link(mode, name, text)
else else
raise "Unknown link type: #{link_type}" raise "Unknown link type: #{link_type}"
end end
end end
def make_page_link(mode, name, text) def make_page_link(mode, name, text)
link = CGI.escape(name) link = CGI.escape(name)
case mode case mode
when :export when :export
if has_page?(name) then "<a class=\"existingWikiWord\" href=\"#{link}.html\">#{text}</a>" if has_page?(name) then "<a class=\"existingWikiWord\" href=\"#{link}.html\">#{text}</a>"
else "<span class=\"newWikiWord\">#{text}</span>" end else "<span class=\"newWikiWord\">#{text}</span>" end
when :publish when :publish
if has_page?(name) then "<a class=\"existingWikiWord\" href=\"../published/#{link}\">#{text}</a>" if has_page?(name) then "<a class=\"existingWikiWord\" href=\"../published/#{link}\">#{text}</a>"
else "<span class=\"newWikiWord\">#{text}</span>" end else "<span class=\"newWikiWord\">#{text}</span>" end
else else
if has_page?(name) if has_page?(name)
"<a class=\"existingWikiWord\" href=\"../show/#{link}\">#{text}</a>" "<a class=\"existingWikiWord\" href=\"../show/#{link}\">#{text}</a>"
else else
"<span class=\"newWikiWord\">#{text}<a href=\"../show/#{link}\">?</a></span>" "<span class=\"newWikiWord\">#{text}<a href=\"../show/#{link}\">?</a></span>"
end end
end end
end end
def make_file_link(mode, name, text) def make_file_link(mode, name, text)
link = CGI.escape(name) link = CGI.escape(name)
case mode case mode
when :export when :export
if has_file?(name) then "<a class=\"existingWikiWord\" href=\"#{link}.html\">#{text}</a>" if has_file?(name) then "<a class=\"existingWikiWord\" href=\"#{link}.html\">#{text}</a>"
else "<span class=\"newWikiWord\">#{text}</span>" end else "<span class=\"newWikiWord\">#{text}</span>" end
when :publish when :publish
if has_file?(name) then "<a class=\"existingWikiWord\" href=\"../published/#{link}\">#{text}</a>" if has_file?(name) then "<a class=\"existingWikiWord\" href=\"../published/#{link}\">#{text}</a>"
else "<span class=\"newWikiWord\">#{text}</span>" end else "<span class=\"newWikiWord\">#{text}</span>" end
else else
if has_file?(name) if has_file?(name)
"<a class=\"existingWikiWord\" href=\"../file/#{link}\">#{text}</a>" "<a class=\"existingWikiWord\" href=\"../file/#{link}\">#{text}</a>"
else else
"<span class=\"newWikiWord\">#{text}<a href=\"../file/#{link}\">?</a></span>" "<span class=\"newWikiWord\">#{text}<a href=\"../file/#{link}\">?</a></span>"
end end
end end
end end
def make_pic_link(mode, name, text) def make_pic_link(mode, name, text)
link = CGI.escape(name) link = CGI.escape(name)
case mode case mode
when :export when :export
if has_file?(name) then "<img alt=\"#{text}\" src=\"#{link}\" />" if has_file?(name) then "<img alt=\"#{text}\" src=\"#{link}\" />"
else "<img alt=\"#{text}\" src=\"no image\" />" end else "<img alt=\"#{text}\" src=\"no image\" />" end
when :publish when :publish
if has_file?(name) then "<img alt=\"#{text}\" src=\"#{link}\" />" if has_file?(name) then "<img alt=\"#{text}\" src=\"#{link}\" />"
else "<span class=\"newWikiWord\">#{text}</span>" end else "<span class=\"newWikiWord\">#{text}</span>" end
else else
if has_file?(name) then "<img alt=\"#{text}\" src=\"../pic/#{link}\" />" if has_file?(name) then "<img alt=\"#{text}\" src=\"../pic/#{link}\" />"
else "<span class=\"newWikiWord\">#{text}<a href=\"../pic/#{link}\">?</a></span>" end else "<span class=\"newWikiWord\">#{text}<a href=\"../pic/#{link}\">?</a></span>" end
end end
end end
def has_page?(name) def has_page?(name)
pages[name] pages[name]
end end
def has_file?(name) def has_file?(name)
wiki.file_yard(self).has_file?(name) wiki.file_yard(self).has_file?(name)
end end
# Clears the display cache for all the pages with references to # Clears the display cache for all the pages with references to
def refresh_pages_with_references(page_name) def refresh_pages_with_references(page_name)
select.pages_that_reference(page_name).each { |page| select.pages_that_reference(page_name).each { |page|
page.revisions.each { |revision| revision.clear_display_cache } page.revisions.each { |revision| revision.clear_display_cache }
} }
end end
def refresh_revisions def refresh_revisions
select.each { |page| page.revisions.each { |revision| revision.clear_display_cache } } select.each { |page| page.revisions.each { |revision| revision.clear_display_cache } }
end end
private private
# Returns an array of all the wiki words in any current revision # Returns an array of all the wiki words in any current revision
def wiki_words def wiki_words
pages.values.inject([]) { |wiki_words, page| wiki_words << page.wiki_words }.flatten.uniq pages.values.inject([]) { |wiki_words, page| wiki_words << page.wiki_words }.flatten.uniq
end end
# Returns an array of all the page names on this web # Returns an array of all the page names on this web
def page_names def page_names
pages.keys pages.keys
end end
# This ensures compatibility with 0.9 storages # This ensures compatibility with 0.9 storages
def wiki def wiki
@wiki ||= WikiService.instance @wiki ||= WikiService.instance
end end
end end

192
app/models/wiki_content.rb Executable file → Normal file
View file

@ -1,97 +1,97 @@
require 'cgi' require 'cgi'
require 'chunks/engines' require 'chunks/engines'
require 'chunks/category' require 'chunks/category'
require 'chunks/include' require 'chunks/include'
require 'chunks/wiki' require 'chunks/wiki'
require 'chunks/literal' require 'chunks/literal'
require 'chunks/uri' require 'chunks/uri'
require 'chunks/nowiki' require 'chunks/nowiki'
# Wiki content is just a string that can process itself with a chain of # 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 # actions. The actions can modify wiki content so that certain parts of
# it are protected from being rendered by later actions. # it are protected from being rendered by later actions.
# #
# When wiki content is rendered, it can be interrogated to find out # When wiki content is rendered, it can be interrogated to find out
# which chunks were rendered. This means things like categories, wiki # which chunks were rendered. This means things like categories, wiki
# links, can be determined. # links, can be determined.
# #
# Exactly how wiki content is rendered is determined by a number of # Exactly how wiki content is rendered is determined by a number of
# settings that are optionally passed in to a constructor. The current # settings that are optionally passed in to a constructor. The current
# options are: # options are:
# * :engine # * :engine
# => The structural markup engine to use (Textile, Markdown, RDoc) # => The structural markup engine to use (Textile, Markdown, RDoc)
# * :engine_opts # * :engine_opts
# => A list of options to pass to the markup engines (safe modes, etc) # => A list of options to pass to the markup engines (safe modes, etc)
# * :pre_engine_actions # * :pre_engine_actions
# => A list of render actions or chunks to be processed before the # => A list of render actions or chunks to be processed before the
# markup engine is applied. By default this is: # markup engine is applied. By default this is:
# Category, Include, URIChunk, WikiChunk::Link, WikiChunk::Word # Category, Include, URIChunk, WikiChunk::Link, WikiChunk::Word
# * :post_engine_actions # * :post_engine_actions
# => A list of render actions or chunks to apply after the markup # => A list of render actions or chunks to apply after the markup
# engine. By default these are: # engine. By default these are:
# Literal::Pre, Literal::Tags # Literal::Pre, Literal::Tags
# * :mode # * :mode
# => How should the content be rendered? For normal display (:display), # => How should the content be rendered? For normal display (:display),
# publishing (:publish) or export (:export)? # publishing (:publish) or export (:export)?
# #
# AUTHOR: Mark Reid <mark @ threewordslong . com> # AUTHOR: Mark Reid <mark @ threewordslong . com>
# CREATED: 15th May 2004 # CREATED: 15th May 2004
# UPDATED: 22nd May 2004 # UPDATED: 22nd May 2004
class WikiContent < String class WikiContent < String
PRE_ENGINE_ACTIONS = [ NoWiki, Category, Include, WikiChunk::Link, URIChunk, LocalURIChunk, PRE_ENGINE_ACTIONS = [ NoWiki, Category, Include, WikiChunk::Link, URIChunk, LocalURIChunk,
WikiChunk::Word ] WikiChunk::Word ]
POST_ENGINE_ACTIONS = [ Literal::Pre, Literal::Tags ] POST_ENGINE_ACTIONS = [ Literal::Pre, Literal::Tags ]
DEFAULT_OPTS = { DEFAULT_OPTS = {
:pre_engine_actions => PRE_ENGINE_ACTIONS, :pre_engine_actions => PRE_ENGINE_ACTIONS,
:post_engine_actions => POST_ENGINE_ACTIONS, :post_engine_actions => POST_ENGINE_ACTIONS,
:engine => Engines::Textile, :engine => Engines::Textile,
:engine_opts => [], :engine_opts => [],
:mode => [:display] :mode => [:display]
} }
attr_reader :web, :options, :rendered, :chunks attr_reader :web, :options, :rendered, :chunks
# Create a new wiki content string from the given one. # Create a new wiki content string from the given one.
# The options are explained at the top of this file. # The options are explained at the top of this file.
def initialize(revision, options = {}) def initialize(revision, options = {})
@revision = revision @revision = revision
@web = @revision.page.web @web = @revision.page.web
# Deep copy of DEFAULT_OPTS to ensure that changes to PRE/POST_ENGINE_ACTIONS stay local # 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 = Marshal.load(Marshal.dump(DEFAULT_OPTS)).update(options)
@options[:engine] = Engines::MAP[@web.markup] || Engines::Textile @options[:engine] = Engines::MAP[@web.markup] || Engines::Textile
@options[:engine_opts] = (@web.safe_mode ? [:filter_html, :filter_styles] : []) @options[:engine_opts] = (@web.safe_mode ? [:filter_html, :filter_styles] : [])
@options[:pre_engine_actions].delete(WikiChunk::Word) if @web.brackets_only @options[:pre_engine_actions].delete(WikiChunk::Word) if @web.brackets_only
super(@revision.content) super(@revision.content)
begin begin
render!(@options[:pre_engine_actions] + [@options[:engine]] + @options[:post_engine_actions]) render!(@options[:pre_engine_actions] + [@options[:engine]] + @options[:post_engine_actions])
# FIXME this is where all the parsing problems were shoved under the carpet # FIXME this is where all the parsing problems were shoved under the carpet
# rescue => e # rescue => e
# @rendered = e.message # @rendered = e.message
end end
end end
# Call @web.page_link using current options. # Call @web.page_link using current options.
def page_link(name, text, link_type) def page_link(name, text, link_type)
@options[:link_type] = link_type || :show @options[:link_type] = link_type || :show
@web.make_link(name, text, @options) @web.make_link(name, text, @options)
end end
# Find all the chunks of the given types # Find all the chunks of the given types
def find_chunks(chunk_type) def find_chunks(chunk_type)
rendered.select { |chunk| chunk.kind_of?(chunk_type) } rendered.select { |chunk| chunk.kind_of?(chunk_type) }
end end
# Render this content using the specified actions. # Render this content using the specified actions.
def render!(chunk_types) def render!(chunk_types)
@chunks = [] @chunks = []
chunk_types.each { |chunk_type| chunk_type.apply_to(self) } chunk_types.each { |chunk_type| chunk_type.apply_to(self) }
@rendered = @chunks.map { |chunk| chunk.unmask(self) }.compact @rendered = @chunks.map { |chunk| chunk.unmask(self) }.compact
(@chunks - @rendered).each { |chunk| chunk.revert(self) } (@chunks - @rendered).each { |chunk| chunk.revert(self) }
end end
end end

444
app/models/wiki_service.rb Executable file → Normal file
View file

@ -1,222 +1,222 @@
require 'open-uri' require 'open-uri'
require 'yaml' require 'yaml'
require 'madeleine' require 'madeleine'
require 'madeleine/automatic' require 'madeleine/automatic'
require 'madeleine/zmarshal' require 'madeleine/zmarshal'
require 'web' require 'web'
require 'page' require 'page'
require 'author' require 'author'
require 'file_yard' require 'file_yard'
module AbstractWikiService module AbstractWikiService
attr_reader :webs, :system attr_reader :webs, :system
def authenticate(password) def authenticate(password)
password == (@system[:password] || 'instiki') password == (@system[:password] || 'instiki')
end end
def create_web(name, address, password = nil) def create_web(name, address, password = nil)
@webs[address] = Web.new(self, name, address, password) unless @webs[address] @webs[address] = Web.new(self, name, address, password) unless @webs[address]
end end
def delete_web(address) def delete_web(address)
@webs[address] = nil @webs[address] = nil
end end
def file_yard(web) def file_yard(web)
raise "Web #{@web.name} does not belong to this wiki service" unless @webs.values.include?(web) raise "Web #{@web.name} does not belong to this wiki service" unless @webs.values.include?(web)
# TODO cache FileYards # TODO cache FileYards
FileYard.new("#{self.storage_path}/#{web.address}") FileYard.new("#{self.storage_path}/#{web.address}")
end end
def init_wiki_service def init_wiki_service
@webs = {} @webs = {}
@system = {} @system = {}
end end
def read_page(web_address, page_name) def read_page(web_address, page_name)
ApplicationController.logger.debug "Reading page '#{page_name}' from web '#{web_address}'" ApplicationController.logger.debug "Reading page '#{page_name}' from web '#{web_address}'"
web = @webs[web_address] web = @webs[web_address]
if web.nil? if web.nil?
ApplicationController.logger.debug "Web '#{web_address}' not found" ApplicationController.logger.debug "Web '#{web_address}' not found"
return nil return nil
else else
page = web.pages[page_name] page = web.pages[page_name]
ApplicationController.logger.debug "Page '#{page_name}' #{page.nil? ? 'not' : ''} found" ApplicationController.logger.debug "Page '#{page_name}' #{page.nil? ? 'not' : ''} found"
return page return page
end end
end end
def remove_orphaned_pages(web_address) def remove_orphaned_pages(web_address)
@webs[web_address].remove_pages(@webs[web_address].select.orphaned_pages) @webs[web_address].remove_pages(@webs[web_address].select.orphaned_pages)
end end
def revise_page(web_address, page_name, content, revised_on, author) def revise_page(web_address, page_name, content, revised_on, author)
page = read_page(web_address, page_name) page = read_page(web_address, page_name)
page.revise(content, revised_on, author) page.revise(content, revised_on, author)
page page
end end
def rollback_page(web_address, page_name, revision_number, created_at, author_id = nil) def rollback_page(web_address, page_name, revision_number, created_at, author_id = nil)
page = read_page(web_address, page_name) page = read_page(web_address, page_name)
page.rollback(revision_number, created_at, author_id) page.rollback(revision_number, created_at, author_id)
page page
end end
def setup(password, web_name, web_address) def setup(password, web_name, web_address)
@system[:password] = password @system[:password] = password
create_web(web_name, web_address) create_web(web_name, web_address)
end end
def setup? def setup?
not (@webs.empty?) not (@webs.empty?)
end end
def update_web(old_address, new_address, name, markup, color, additional_style, safe_mode = false, 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, password = nil, published = false, brackets_only = false, count_pages = false,
allow_uploads = true) allow_uploads = true)
if old_address != new_address if old_address != new_address
@webs[new_address] = @webs[old_address] @webs[new_address] = @webs[old_address]
@webs.delete(old_address) @webs.delete(old_address)
@webs[new_address].address = new_address @webs[new_address].address = new_address
end end
web = @webs[new_address] web = @webs[new_address]
web.refresh_revisions if settings_changed?(web, markup, safe_mode, brackets_only) web.refresh_revisions if settings_changed?(web, markup, safe_mode, brackets_only)
web.name, web.markup, web.color, web.additional_style, web.safe_mode = web.name, web.markup, web.color, web.additional_style, web.safe_mode =
name, markup, color, additional_style, safe_mode name, markup, color, additional_style, safe_mode
web.password, web.published, web.brackets_only, web.count_pages, web.allow_uploads = web.password, web.published, web.brackets_only, web.count_pages, web.allow_uploads =
password, published, brackets_only, count_pages, allow_uploads password, published, brackets_only, count_pages, allow_uploads
end end
def write_page(web_address, page_name, content, written_on, author) def write_page(web_address, page_name, content, written_on, author)
page = Page.new(@webs[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) @webs[web_address].add_page(page)
page page
end end
def storage_path def storage_path
self.class.storage_path self.class.storage_path
end end
private private
def settings_changed?(web, markup, safe_mode, brackets_only) def settings_changed?(web, markup, safe_mode, brackets_only)
web.markup != markup || web.markup != markup ||
web.safe_mode != safe_mode || web.safe_mode != safe_mode ||
web.brackets_only != brackets_only web.brackets_only != brackets_only
end end
end end
class WikiService class WikiService
include AbstractWikiService include AbstractWikiService
include Madeleine::Automatic::Interceptor include Madeleine::Automatic::Interceptor
# These methods do not change the state of persistent objects, and # These methods do not change the state of persistent objects, and
# should not be ogged by Madeleine # should not be ogged by Madeleine
automatic_read_only :authenticate, :read_page, :setup?, :webs, :storage_path, :file_yard automatic_read_only :authenticate, :read_page, :setup?, :webs, :storage_path, :file_yard
@@storage_path = './storage/' @@storage_path = './storage/'
class << self class << self
def storage_path=(storage_path) def storage_path=(storage_path)
@@storage_path = storage_path @@storage_path = storage_path
end end
def storage_path def storage_path
@@storage_path @@storage_path
end end
def clean_storage def clean_storage
MadeleineServer.clean_storage(self) MadeleineServer.clean_storage(self)
end end
def instance def instance
@madeleine ||= MadeleineServer.new(self) @madeleine ||= MadeleineServer.new(self)
@system = @madeleine.system @system = @madeleine.system
return @system return @system
end end
def snapshot def snapshot
@madeleine.snapshot @madeleine.snapshot
end end
end end
def initialize def initialize
init_wiki_service init_wiki_service
end end
end end
class MadeleineServer class MadeleineServer
attr_reader :storage_path attr_reader :storage_path
# Clears all the command_log and snapshot files located in the storage directory, so the # Clears all the command_log and snapshot files located in the storage directory, so the
# database is essentially dropped and recreated as blank # database is essentially dropped and recreated as blank
def self.clean_storage(service) def self.clean_storage(service)
begin begin
Dir.foreach(service.storage_path) do |file| Dir.foreach(service.storage_path) do |file|
if file =~ /(command_log|snapshot)$/ if file =~ /(command_log|snapshot)$/
File.delete(File.join(service.storage_path, file)) File.delete(File.join(service.storage_path, file))
end end
end end
rescue rescue
Dir.mkdir(service.storage_path) Dir.mkdir(service.storage_path)
end end
end end
def initialize(service) def initialize(service)
@storage_path = service.storage_path @storage_path = service.storage_path
@server = Madeleine::Automatic::AutomaticSnapshotMadeleine.new(service.storage_path, @server = Madeleine::Automatic::AutomaticSnapshotMadeleine.new(service.storage_path,
Madeleine::ZMarshal.new) { Madeleine::ZMarshal.new) {
service.new service.new
} }
start_snapshot_thread start_snapshot_thread
end end
def command_log_present? def command_log_present?
not Dir[storage_path + '/*.command_log'].empty? not Dir[storage_path + '/*.command_log'].empty?
end end
def snapshot def snapshot
@server.take_snapshot @server.take_snapshot
end end
def start_snapshot_thread def start_snapshot_thread
Thread.new(@server) { Thread.new(@server) {
hours_since_last_snapshot = 0 hours_since_last_snapshot = 0
while true while true
begin begin
hours_since_last_snapshot += 1 hours_since_last_snapshot += 1
# Take a snapshot if there is a command log, or 24 hours # Take a snapshot if there is a command log, or 24 hours
# have passed since the last snapshot # have passed since the last snapshot
if command_log_present? or hours_since_last_snapshot >= 24 if command_log_present? or hours_since_last_snapshot >= 24
ActionController::Base.logger.info "[#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}] " + ActionController::Base.logger.info "[#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}] " +
'Taking a Madeleine snapshot' 'Taking a Madeleine snapshot'
snapshot snapshot
hours_since_last_snapshot = 0 hours_since_last_snapshot = 0
end end
sleep(1.hour) sleep(1.hour)
rescue => e rescue => e
ActionController::Base.logger.error(e) ActionController::Base.logger.error(e)
# wait for a minute (not to spoof the log with the same error) # wait for a minute (not to spoof the log with the same error)
# and go back into the loop, to keep trying # and go back into the loop, to keep trying
sleep(1.minute) sleep(1.minute)
ActionController::Base.logger.info("Retrying to save a snapshot") ActionController::Base.logger.info("Retrying to save a snapshot")
end end
end end
} }
end end
def system def system
@server.system @server.system
end end
end end

46
app/models/wiki_words.rb Executable file → Normal file
View file

@ -1,23 +1,23 @@
# Contains all the methods for finding and replacing wiki words # Contains all the methods for finding and replacing wiki words
module WikiWords module WikiWords
# In order of appearance: Latin, greek, cyrillian, armenian # In order of appearance: Latin, greek, cyrillian, armenian
I18N_HIGHER_CASE_LETTERS = I18N_HIGHER_CASE_LETTERS =
"À<EFBFBD>?ÂÃÄÅĀĄĂÆÇĆČĈĊĎ<C48A>?ÈÉÊËĒĘĚĔĖĜĞĠĢĤĦÌ<C4A6><>?ĪĨĬĮİIJĴĶ<C4B4>?ĽĹĻĿÑŃŇŅŊÒÓÔÕÖØŌ<C398>?ŎŒŔŘŖŚŠŞŜȘŤŢŦȚÙÚÛÜŪŮŰŬŨŲŴ<C5B2>?ŶŸŹŽŻ" + "À<EFBFBD>?ÂÃÄÅĀĄĂÆÇĆČĈĊĎ<C48A>?ÈÉÊËĒĘĚĔĖĜĞĠĢĤĦÌ<C4A6><>?ĪĨĬĮİIJĴĶ<C4B4>?ĽĹĻĿÑŃŇŅŊÒÓÔÕÖØŌ<C398>?ŎŒŔŘŖŚŠŞŜȘŤŢŦȚÙÚÛÜŪŮŰŬŨŲŴ<C5B2>?ŶŸŹŽŻ" +
"ΑΒΓΔΕΖΗΘΙΚΛΜ<EFBFBD>?ΞΟΠΡΣΤΥΦΧΨΩ" + "ΑΒΓΔΕΖΗΘΙΚΛΜ<EFBFBD>?ΞΟΠΡΣΤΥΦΧΨΩ" +
"ΆΈΉΊΌΎ<EFBFBD>?ѠѢѤѦѨѪѬѮѰѲѴѶѸѺѼѾҀҊҌҎ<D28C>?ҒҔҖҘҚҜҞҠҢҤҦҨҪҬҮҰҲҴҶҸҺҼҾ<D2BC>?ӃӅӇӉӋ<D389>?<3F>?ӒӔӖӘӚӜӞӠӢӤӦӨӪӬӮӰӲӴӸЖ" + "ΆΈΉΊΌΎ<EFBFBD>?ѠѢѤѦѨѪѬѮѰѲѴѶѸѺѼѾҀҊҌҎ<D28C>?ҒҔҖҘҚҜҞҠҢҤҦҨҪҬҮҰҲҴҶҸҺҼҾ<D2BC>?ӃӅӇӉӋ<D389>?<3F>?ӒӔӖӘӚӜӞӠӢӤӦӨӪӬӮӰӲӴӸЖ" +
"ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀ<EFBFBD>?ՂՃՄՅՆՇՈՉՊՋՌ<D58B>?<3F>?<3F>?ՑՒՓՔՕՖ" "ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀ<EFBFBD>?ՂՃՄՅՆՇՈՉՊՋՌ<D58B>?<3F>?<3F>?ՑՒՓՔՕՖ"
I18N_LOWER_CASE_LETTERS = I18N_LOWER_CASE_LETTERS =
"àáâãäå<EFBFBD>?ąăæçć<C3A7>?ĉċ<C489>?đèéêëēęěĕėƒ<C497>?ğġģĥħìíîïīĩĭįıijĵķĸłľĺļŀñńňņʼnŋòóôõöø<C3B6><>?œŕřŗśšş<C5A1>?șťţŧțùúûüūůűŭũųŵýÿŷžżźÞþßſ<C39F>" + "àáâãäå<EFBFBD>?ąăæçć<C3A7>?ĉċ<C489>?đèéêëēęěĕėƒ<C497>?ğġģĥħìíîïīĩĭįıijĵķĸłľĺļŀñńňņʼnŋòóôõöø<C3B6><>?œŕřŗśšş<C5A1>?șťţŧțùúûüūůűŭũųŵýÿŷžżźÞþßſ<C39F>" +
"άέήίΰαβγδεζηθικλμνξοπ<EFBFBD>στυφχψωϊϋό<CF8B><>?" + "άέήίΰαβγδεζηθικλμνξοπ<EFBFBD>στυφχψωϊϋό<CF8B><>?" +
"абвгдежзийклмнопр<EFBFBD>уфхцчшщъыь<D18B><>?<3F>?ёђѓєѕіїјљћќ<D19B>?ўџѡѣѥѧѩѫѭѯѱѳѵѷѹѻѽѿ<D1BD><>?<3F>?ґғҕҗҙқ<D299>?ҟҡңҥҧҩҫҭүұҳҵҷҹһҽҿӀӂӄӆӈӊӌӎӑӓӕӗәӛ<D399>?ӟӡӣӥӧөӫӭӯӱӳӵӹ" + "абвгдежзийклмнопр<EFBFBD>уфхцчшщъыь<D18B><>?<3F>?ёђѓєѕіїјљћќ<D19B>?ўџѡѣѥѧѩѫѭѯѱѳѵѷѹѻѽѿ<D1BD><>?<3F>?ґғҕҗҙқ<D299>?ҟҡңҥҧҩҫҭүұҳҵҷҹһҽҿӀӂӄӆӈӊӌӎӑӓӕӗәӛ<D399>?ӟӡӣӥӧөӫӭӯӱӳӵӹ" +
"աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտր<EFBFBD>?ւփքօֆև" "աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտր<EFBFBD>?ւփքօֆև"
WIKI_WORD_PATTERN = '[A-Z' + I18N_HIGHER_CASE_LETTERS + '][a-z' + I18N_LOWER_CASE_LETTERS + ']+[A-Z' + I18N_HIGHER_CASE_LETTERS + ']\w+' 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 CAMEL_CASED_WORD_BORDER = /([a-z#{I18N_LOWER_CASE_LETTERS}])([A-Z#{I18N_HIGHER_CASE_LETTERS}])/u
def self.separate(wiki_word) def self.separate(wiki_word)
wiki_word.gsub(CAMEL_CASED_WORD_BORDER, '\1 \2') wiki_word.gsub(CAMEL_CASED_WORD_BORDER, '\1 \2')
end end
end end

View file

@ -1,22 +1,22 @@
<% <%
@title = "Upload #{@file_name}" @title = "Upload #{@file_name}"
@hide_navigatio = false @hide_navigatio = false
%> %>
<p> <p>
<%= form_tag({}, {:multipart => true}) %> <%= form_tag({}, {:multipart => true}) %>
<p> <p>
File to upload: File to upload:
<br/> <br/>
<input type="file" name="file" size="40" /> <input type="file" name="file" size="40" />
</p> </p>
<p> <p>
<input type="submit" value="Update" /> as <input type="submit" value="Update" /> as
<input type="text" name="author" id="authorName" value="<%= @author %>" <input type="text" name="author" id="authorName" value="<%= @author %>"
onClick="this.value == 'AnonymousCoward' ? this.value = '' : true" /> onClick="this.value == 'AnonymousCoward' ? this.value = '' : true" />
<% if @page %> <% if @page %>
| <a href="../file/">Cancel</a> <small>(unlocks page)</small> | <a href="../file/">Cancel</a> <small>(unlocks page)</small>
<% end %> <% end %>
</p> </p>
<%= end_form_tag %> <%= end_form_tag %>
</p> </p>

View file

@ -1,72 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<title> <title>
<% if @page and (@page.name == 'HomePage') and (%w( show published print ).include?(@action_name)) %> <% if @page and (@page.name == 'HomePage') and (%w( show published print ).include?(@action_name)) %>
<%= @web.name %> <%= @web.name %>
<% elsif @web %> <% elsif @web %>
<%= @title %> in <%= @web.name %> <%= @title %> in <%= @web.name %>
<% else %> <% else %>
<%= @title %> <%= @title %>
<% end %> <% end %>
</title> </title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style type="text/css"> <style type="text/css">
h1#pageName, .newWikiWord a, a.existingWikiWord, .newWikiWord a:hover, #TextileHelp h3 { h1#pageName, .newWikiWord a, a.existingWikiWord, .newWikiWord a:hover, #TextileHelp h3 {
color: #<%= @web ? @web.color : "393" %>; color: #<%= @web ? @web.color : "393" %>;
} }
#Container, #Content { #Container, #Content {
width: <%= @content_width || "600" %>px; width: <%= @content_width || "600" %>px;
} }
<%= File.read(RAILS_ROOT + '/public/stylesheets/instiki.css') if @inline_style %> <%= File.read(RAILS_ROOT + '/public/stylesheets/instiki.css') if @inline_style %>
</style> </style>
<link rel="Stylesheet" href="/stylesheets/instiki.css" type="text/css" media="screen" /> <link rel="Stylesheet" href="/stylesheets/instiki.css" type="text/css" media="screen" />
<style type="text/css"> <style type="text/css">
<%= @style_additions %> <%= @style_additions %>
<%= @web ? @web.additional_style : '' %> <%= @web ? @web.additional_style : '' %>
</style> </style>
</head> </head>
<body> <body>
<div id="Container"> <div id="Container">
<div id="Content"> <div id="Content">
<h1 id="pageName"> <h1 id="pageName">
<% if @page and (@page.name == 'HomePage') and %w( show published print ).include?(@action_name) %> <% if @page and (@page.name == 'HomePage') and %w( show published print ).include?(@action_name) %>
<%= @web.name %> <%= @web.name %>
<% elsif @web %> <% elsif @web %>
<small><%= @web.name %></small><br /> <small><%= @web.name %></small><br />
<%= @title %> <%= @title %>
<% else %> <% else %>
<%= @title %> <%= @title %>
<% end %> <% end %>
</h1> </h1>
<% if @flash[:error] %> <div id="error"> <% if @flash[:error] %> <div id="error">
<hr/><p><%= @flash[:error].to_s %></p><hr/></div> <hr/><p><%= @flash[:error].to_s %></p><hr/></div>
<% end %> <% end %>
<% if @flash[:info] %> <div id="info"> <% if @flash[:info] %> <div id="info">
<hr/><p><%= @flash[:info].to_s %></p><hr/></div> <hr/><p><%= @flash[:info].to_s %></p><hr/></div>
<% end %> <% end %>
<%= render 'navigation' unless @web.nil? || @hide_navigation %> <%= render 'navigation' unless @web.nil? || @hide_navigation %>
<%= @content_for_layout %> <%= @content_for_layout %>
<div id="footer"> <div id="footer">
<hr/> <hr/>
<p>This site is running on <a href="http://instiki.org/">Instiki</a></p> <p>This site is running on <a href="http://instiki.org/">Instiki</a></p>
<br/> <br/>
<p>Powered by <a href="http://rubyonrails.com/">Ruby on Rails</a></p> <p>Powered by <a href="http://rubyonrails.com/">Ruby on Rails</a></p>
</div> </div>
</div> <!-- Content --> </div> <!-- Content -->
</div> <!-- Container --> </div> <!-- Container -->
</body> </body>
</html> </html>

30
app/views/markdown_help.rhtml Executable file → Normal file
View file

@ -1,16 +1,16 @@
<div id="TextileHelp" style="float: right; width: 250px; margin-top: 5px"> <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> <h3>Markdown formatting tips (<a target="_new" href="http://daringfireball.net/projects/markdown/syntax">advanced</a>)</h3>
<table cellspacing="0" cellpadding="0"> <table cellspacing="0" cellpadding="0">
<tr><td>_your text_</td><td class="arrow">&rarr;</td><td><em>your text</em></td></tr> <tr><td>_your text_</td><td class="arrow">&rarr;</td><td><em>your text</em></td></tr>
<tr><td>**your text**</td><td class="arrow">&rarr;</td><td><strong>your text</strong></td></tr> <tr><td>**your text**</td><td class="arrow">&rarr;</td><td><strong>your text</strong></td></tr>
<tr><td>`my code`</td><td class="arrow">&rarr;</td><td><code>my code</code></td></tr> <tr><td>`my code`</td><td class="arrow">&rarr;</td><td><code>my code</code></td></tr>
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">&rarr;</td><td>&#8226; Bulleted list<br />&#8226; Second item</td></tr> <tr><td>* Bulleted list<br />* Second item</td><td class="arrow">&rarr;</td><td>&#8226; Bulleted list<br />&#8226; Second item</td></tr>
<tr><td>1. Numbered list<br />1. Second item</td><td class="arrow">&rarr;</td><td>1. Numbered list<br />2. Second item</td></tr> <tr><td>1. Numbered list<br />1. Second item</td><td class="arrow">&rarr;</td><td>1. Numbered list<br />2. Second item</td></tr>
<tr><td>[link name](URL)</td><td class="arrow">&rarr;</td><td><a href="URL">link name</a></td></tr> <tr><td>[link name](URL)</td><td class="arrow">&rarr;</td><td><a href="URL">link name</a></td></tr>
<tr><td>***</td><td class="arrow">&rarr;</td><td>Horizontal ruler</td></tr> <tr><td>***</td><td class="arrow">&rarr;</td><td>Horizontal ruler</td></tr>
<tr><td>&lt;http://url><br />&lt;email@add.com></td><td class="arrow">&rarr;</td><td>Auto-linked</td></tr> <tr><td>&lt;http://url><br />&lt;email@add.com></td><td class="arrow">&rarr;</td><td>Auto-linked</td></tr>
<tr><td>![Alt text](URL)</td><td class="arrow">&rarr;</td><td>Image</td></tr> <tr><td>![Alt text](URL)</td><td class="arrow">&rarr;</td><td>Image</td></tr>
</table> </table>
<%= render 'wiki_words_help' %> <%= render 'wiki_words_help' %>
</div> </div>

50
app/views/navigation.rhtml Executable file → Normal file
View file

@ -1,25 +1,25 @@
<% <%
def list_item(title, url, description, accesskey = nil) def list_item(title, url, description, accesskey = nil)
if @title == title if @title == title
"<b class=\"navOn\" title=\"#{description}\" accesskey=\"#{accesskey}\">#{title}</b>" "<b class=\"navOn\" title=\"#{description}\" accesskey=\"#{accesskey}\">#{title}</b>"
else else
"<a href=\"#{url}\" title=\"#{description}\" accesskey=\"#{accesskey}\">#{title}</a>" "<a href=\"#{url}\" title=\"#{description}\" accesskey=\"#{accesskey}\">#{title}</a>"
end end
end end
%> %>
<form id="navigationForm" class="navigation" action="../search/" method="get" style="font-size: 10px"> <form id="navigationForm" class="navigation" action="../search/" method="get" style="font-size: 10px">
<% if @action_name != "published" then %> <% if @action_name != "published" then %>
<%= list_item "Home Page", "../show/HomePage", "Home, Sweet Home", "H" %> | <%= list_item "Home Page", "../show/HomePage", "Home, Sweet Home", "H" %> |
<%= list_item "All Pages", "../list/", "Alphabetically sorted list of pages", "A" %> | <%= 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 "Recently Revised", "../recently_revised/", "Pages sorted by when they were last changed", "U" %> |
<%= list_item "Authors", "../authors/", "Who wrote what" %> | <%= list_item "Authors", "../authors/", "Who wrote what" %> |
<%= list_item "Feeds", "../feeds/", "Subscribe to changes by RSS" %> | <%= list_item "Feeds", "../feeds/", "Subscribe to changes by RSS" %> |
<%= list_item "Export", "../export/", "Download a zip with all the pages in this wiki", "X" %> | <%= 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" /> <input type="text" id="searchField" name="query" style="font-size: 10px" value="Search" onClick="this.value == 'Search' ? this.value = '' : true" />
<% else %> <% else %>
<%= list_item "Home Page", "../published/HomePage", "Home, Sweet Home", "H" %> | <%= list_item "Home Page", "../published/HomePage", "Home, Sweet Home", "H" %> |
<% end%> <% end%>
</form> </form>

30
app/views/rdoc_help.rhtml Executable file → Normal file
View file

@ -1,16 +1,16 @@
<div id="TextileHelp" style="float: right; width: 250px; margin-top: 5px"> <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> <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"> <table cellspacing="0" cellpadding="0">
<tr><td>_your text_</td><td class="arrow">&rarr;</td><td><em>your text</em></td></tr> <tr><td>_your text_</td><td class="arrow">&rarr;</td><td><em>your text</em></td></tr>
<tr><td>*your text*</td><td class="arrow">&rarr;</td><td><strong>your text</strong></td></tr> <tr><td>*your text*</td><td class="arrow">&rarr;</td><td><strong>your text</strong></td></tr>
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">&rarr;</td><td>&#8226; Bulleted list<br />&#8226; Second item</td></tr> <tr><td>* Bulleted list<br />* Second item</td><td class="arrow">&rarr;</td><td>&#8226; Bulleted list<br />&#8226; Second item</td></tr>
<tr><td>1. Numbered list<br />2. Second item</td><td class="arrow">&rarr;</td><td>1. Numbered list<br />2. Second item</td></tr> <tr><td>1. Numbered list<br />2. Second item</td><td class="arrow">&rarr;</td><td>1. Numbered list<br />2. Second item</td></tr>
<tr><td>+my_code+</td><td class="arrow">&rarr;</td><td><code>my_code</code></td></tr> <tr><td>+my_code+</td><td class="arrow">&rarr;</td><td><code>my_code</code></td></tr>
<tr><td>---</td><td class="arrow">&rarr;</td><td>Horizontal ruler</td></tr> <tr><td>---</td><td class="arrow">&rarr;</td><td>Horizontal ruler</td></tr>
<tr><td>[[URL linkname]]</td><td class="arrow">&rarr;</td><td><a href="URL">linkname</a></td></tr> <tr><td>[[URL linkname]]</td><td class="arrow">&rarr;</td><td><a href="URL">linkname</a></td></tr>
<tr><td>http://url<br />mailto:e@add.com</td><td class="arrow">&rarr;</td><td>Auto-linked</td></tr> <tr><td>http://url<br />mailto:e@add.com</td><td class="arrow">&rarr;</td><td>Auto-linked</td></tr>
<tr><td>imageURL</td><td class="arrow">&rarr;</td><td>Image</td></tr> <tr><td>imageURL</td><td class="arrow">&rarr;</td><td>Image</td></tr>
</table> </table>
<%= render 'wiki_words_help' %> <%= render 'wiki_words_help' %>
</div> </div>

54
app/views/textile_help.rhtml Executable file → Normal file
View file

@ -1,28 +1,28 @@
<div id="TextileHelp" style="float: right; width: 250px; margin-top: 5px"> <div id="TextileHelp" style="float: right; width: 250px; margin-top: 5px">
<h3>Textile formatting tips (<a href="#" onClick="quickRedReference(); return false;">advanced</a>)</h3> <h3>Textile formatting tips (<a href="#" onClick="quickRedReference(); return false;">advanced</a>)</h3>
<table cellspacing="0" cellpadding="0"> <table cellspacing="0" cellpadding="0">
<tr><td>_your text_</td><td class="arrow">&rarr;</td><td><em>your text</em></td></tr> <tr><td>_your text_</td><td class="arrow">&rarr;</td><td><em>your text</em></td></tr>
<tr><td>*your text*</td><td class="arrow">&rarr;</td><td><strong>your text</strong></td></tr> <tr><td>*your text*</td><td class="arrow">&rarr;</td><td><strong>your text</strong></td></tr>
<tr><td>%{color:red}hello%</td><td class="arrow">&rarr;</td><td><span style="color: red;">hello</span></td></tr> <tr><td>%{color:red}hello%</td><td class="arrow">&rarr;</td><td><span style="color: red;">hello</span></td></tr>
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">&rarr;</td><td>&#8226; Bulleted list<br />&#8226; Second item</td></tr> <tr><td>* Bulleted list<br />* Second item</td><td class="arrow">&rarr;</td><td>&#8226; Bulleted list<br />&#8226; Second item</td></tr>
<tr><td># Numbered list<br /># Second item</td><td class="arrow">&rarr;</td><td>1. Numbered list<br />2. Second item</td></tr> <tr><td># Numbered list<br /># Second item</td><td class="arrow">&rarr;</td><td>1. Numbered list<br />2. Second item</td></tr>
<tr><td>"linkname":URL</td><td class="arrow">&rarr;</td><td><a href="URL">linkname</a></td></tr> <tr><td>"linkname":URL</td><td class="arrow">&rarr;</td><td><a href="URL">linkname</a></td></tr>
<tr><td>|a|table|row|<br />|b|table|row|</td><td class="arrow">&rarr;</td><td>Table</td></tr> <tr><td>|a|table|row|<br />|b|table|row|</td><td class="arrow">&rarr;</td><td>Table</td></tr>
<tr><td>http://url<br />email@address.com</td><td class="arrow">&rarr;</td><td>Auto-linked</td></tr> <tr><td>http://url<br />email@address.com</td><td class="arrow">&rarr;</td><td>Auto-linked</td></tr>
<tr><td>!imageURL!</td><td class="arrow">&rarr;</td><td>Image</td></tr> <tr><td>!imageURL!</td><td class="arrow">&rarr;</td><td>Image</td></tr>
</table> </table>
<%= render 'wiki_words_help' %> <%= render 'wiki_words_help' %>
</div> </div>
<script language="JavaScript"> <script language="JavaScript">
function quickRedReference() { function quickRedReference() {
window.open( window.open(
"http://hobix.com/textile/quick.html", "http://hobix.com/textile/quick.html",
"redRef", "redRef",
"height=600,width=550,channelmode=0,dependent=0," + "height=600,width=550,channelmode=0,dependent=0," +
"directories=0,fullscreen=0,location=0,menubar=0," + "directories=0,fullscreen=0,location=0,menubar=0," +
"resizable=0,scrollbars=1,status=1,toolbar=0" "resizable=0,scrollbars=1,status=1,toolbar=0"
); );
} }
</script> </script>

22
app/views/wiki/authors.rhtml Executable file → Normal file
View file

@ -1,11 +1,11 @@
<% @title = 'Authors' %> <% @title = 'Authors' %>
<ul id="authorList"> <ul id="authorList">
<% for author in @authors %> <% for author in @authors %>
<li> <li>
<%= @web.make_link(author) %> <%= @web.make_link(author) %>
co- or authored: co- or authored:
<%= @web.select.pages_authored_by(author).collect { |page| page.link }.join ', ' %> <%= @web.select.pages_authored_by(author).collect { |page| page.link }.join ', ' %>
</li> </li>
<% end %> <% end %>
</ul> </ul>

58
app/views/wiki/edit.rhtml Executable file → Normal file
View file

@ -1,29 +1,29 @@
<% <%
@title = "Editing #{@page.name}" @title = "Editing #{@page.name}"
@content_width = 720 @content_width = 720
@hide_navigation = true @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"] %> <%= "<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 %> <%= render("#{@web.markup}_help") if @web %>
<form id="editForm" action="../save/<%= @page.name %>" method="post" onSubmit="cleanAuthorName();"> <form id="editForm" action="../save/<%= @page.name %>" method="post" onSubmit="cleanAuthorName();">
<p> <p>
<textarea name="content" style="width: 450px; height: 500px"><%= @page.content %></textarea> <textarea name="content" style="width: 450px; height: 500px"><%= @page.content %></textarea>
</p> </p>
<p> <p>
<input type="submit" value="Update" /> as <input type="submit" value="Update" /> as
<input type="text" name="author" id="authorName" value="<%= @author %>" <input type="text" name="author" id="authorName" value="<%= @author %>"
onClick="this.value == 'AnonymousCoward' ? this.value = '' : true" /> onClick="this.value == 'AnonymousCoward' ? this.value = '' : true" />
| <a href="../cancel_edit/<%= @page.name %>">Cancel</a> <small>(unlocks page)</small> | <a href="../cancel_edit/<%= @page.name %>">Cancel</a> <small>(unlocks page)</small>
</p> </p>
</form> </form>
<script language="JavaScript1.2"> <script language="JavaScript1.2">
function cleanAuthorName() { function cleanAuthorName() {
if (document.getElementById('authorName').value == "") { if (document.getElementById('authorName').value == "") {
document.getElementById('authorName').value = 'AnonymousCoward'; document.getElementById('authorName').value = 'AnonymousCoward';
} }
} }
</script> </script>

234
app/views/wiki/edit_web.rhtml Executable file → Normal file
View file

@ -1,117 +1,117 @@
<% @title = "Edit Web" %> <% @title = "Edit Web" %>
<form action="../update_web" id="setup" method="post" onSubmit="cleanAddress(); return validateSetup()"> <form action="../update_web" id="setup" method="post" onSubmit="cleanAddress(); return validateSetup()">
<h2 style="margin-bottom: 3px">Name and address</h2> <h2 style="margin-bottom: 3px">Name and address</h2>
<div class="help"> <div class="help">
The name of the web is included in the title on all pages. 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. 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>. Ex: the address "rails" gives URLs like <i>/rails/show/HomePage</i>.
</div> </div>
<div class="inputBox, disableAutoComplete"> <div class="inputBox, disableAutoComplete">
Name: <input type="text" id="name" name="name" value="<%= @web.name %>" Name: <input type="text" id="name" name="name" value="<%= @web.name %>"
onChange="proposeAddress();" /> &nbsp;&nbsp; onChange="proposeAddress();" /> &nbsp;&nbsp;
Address: <input type="text" id="address" name="address" value="<%= @web.address %>" Address: <input type="text" id="address" name="address" value="<%= @web.address %>"
onChange="cleanAddress();" /> onChange="cleanAddress();" />
<i>(Letters and digits only)</i> <i>(Letters and digits only)</i>
</div> </div>
<h2 style="margin-bottom: 3px">Specialize</h2> <h2 style="margin-bottom: 3px">Specialize</h2>
<div class="help"> <div class="help">
Turning safe mode on will strip HTML tags and stylesheet options from the content of all pages. 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 Turning on "brackets only" will require all wiki words to be as [[wiki word]] and WikiWord
won't work. won't work.
Turning "allow uploads" on will let wiki users to upload pictures and other files to the wiki Turning "allow uploads" on will let wiki users to upload pictures and other files to the wiki
and include them on wiki pages. and include them on wiki pages.
Additions to the stylesheet take precedence over the existing styles. 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. <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;"> <a href="#" onClick="document.getElementById('additionalStyle').style.display='block';return false;">
See styles &gt;&gt; See styles &gt;&gt;
</a> </a>
</div> </div>
<div class="inputBox, disableAutoComplete"> <div class="inputBox, disableAutoComplete">
Markup: Markup:
<select name="markup"> <select name="markup">
<%= html_options({"Textile" => :textile, "Markdown" => :markdown, "RDoc" => :rdoc }, <%= html_options({"Textile" => :textile, "Markdown" => :markdown, "RDoc" => :rdoc },
@web.markup) %> @web.markup) %>
</select> </select>
&nbsp;&nbsp; &nbsp;&nbsp;
Color: Color:
<select name="color"> <select name="color">
<%= html_options({ "Green" => "008B26", "Purple" => "504685", "Red" => "DA0006", <%= html_options({ "Green" => "008B26", "Purple" => "504685", "Red" => "DA0006",
"Orange" => "FA6F00", "Grey" => "8BA2B0" }, @web.color) %> "Orange" => "FA6F00", "Grey" => "8BA2B0" }, @web.color) %>
</select> </select>
&nbsp;&nbsp; &nbsp;&nbsp;
<small> <small>
<input type="checkbox" name="safe_mode" <%= 'checked="on"' if @web.safe_mode %> /> Safe mode <input type="checkbox" name="safe_mode" <%= 'checked="on"' if @web.safe_mode %> /> Safe mode
&nbsp;&nbsp; &nbsp;&nbsp;
<input type="checkbox" name="brackets_only" <%= 'checked="on"' if @web.brackets_only %> /> <input type="checkbox" name="brackets_only" <%= 'checked="on"' if @web.brackets_only %> />
Brackets only Brackets only
&nbsp;&nbsp; &nbsp;&nbsp;
<input type="checkbox" name="count_pages" <%= 'checked="on"' if @web.count_pages %> /> Count pages <input type="checkbox" name="count_pages" <%= 'checked="on"' if @web.count_pages %> /> Count pages
&nbsp;&nbsp; &nbsp;&nbsp;
<input type="checkbox" name="allow_uploads" <%= 'checked="on"' if @web.allow_uploads %> /> Allow uploads <input type="checkbox" name="allow_uploads" <%= 'checked="on"' if @web.allow_uploads %> /> Allow uploads
</small> </small>
<textarea id="additionalStyle" <textarea id="additionalStyle"
style="display: none; margin-top: 10px; margin-bottom: 5px; width: 560px; height: 200px" style="display: none; margin-top: 10px; margin-bottom: 5px; width: 560px; height: 200px"
name="additional_style"><%= @web.additional_style %> name="additional_style"><%= @web.additional_style %>
</textarea> </textarea>
</div> </div>
<h2 style="margin-bottom: 3px">Password protection for this web (<%= @web.name %>)</h2> <h2 style="margin-bottom: 3px">Password protection for this web (<%= @web.name %>)</h2>
<div class="help"> <div class="help">
This is the password that visitors need to view and edit this web. This is the password that visitors need to view and edit this web.
Setting the password to nothing will remove the password protection. Setting the password to nothing will remove the password protection.
</div> </div>
<div class="inputBox"> <div class="inputBox">
Password: <input class="disableAutoComplete" type="password" id="password" Password: <input class="disableAutoComplete" type="password" id="password"
name="password" value="<%= @web.password %>" /> name="password" value="<%= @web.password %>" />
&nbsp;&nbsp; &nbsp;&nbsp;
Verify: <input class="disableAutoComplete" type="password" id="password_check" Verify: <input class="disableAutoComplete" type="password" id="password_check"
value="<%= @web.password %>" name="password_check" /> value="<%= @web.password %>" name="password_check" />
</div> </div>
<h2 style="margin-bottom: 3px">Publish read-only version of this web (<%= @web.name %>)</h2> <h2 style="margin-bottom: 3px">Publish read-only version of this web (<%= @web.name %>)</h2>
<div class="help"> <div class="help">
You can turn on a read-only version of this web that's accessible even when the regular web You can turn on a read-only version of this web that's accessible even when the regular web
is password protected. is password protected.
The published version is accessible through URLs like /wiki/published/HomePage. The published version is accessible through URLs like /wiki/published/HomePage.
</div> </div>
<div class="inputBox"> <div class="inputBox">
<input type="checkbox" name="published" <%= 'checked="on"' if @web.published %> /> Publish this web <input type="checkbox" name="published" <%= 'checked="on"' if @web.published %> /> Publish this web
</div> </div>
<p align="right"> <p align="right">
<small> <small>
Enter system password Enter system password
<input type="password" class="disableAutoComplete" id="system_password" name="system_password" /> <input type="password" class="disableAutoComplete" id="system_password" name="system_password" />
and and
<input type="submit" value="Update Web" /> <input type="submit" value="Update Web" />
<br/><br/> <br/><br/>
...or forget changes and <a href="/new_web/">create a new web</a> ...or forget changes and <a href="/new_web/">create a new web</a>
</small> </small>
</p> </p>
</form> </form>
<br/> <br/>
<h1>Other administrative tasks</h1> <h1>Other administrative tasks</h1>
<form action="../remove_orphaned_pages" id="remove_orphaned_pages" method="post"> <form action="../remove_orphaned_pages" id="remove_orphaned_pages" method="post">
<p align="right"> <p align="right">
<small> <small>
Clean up by entering system password Clean up by entering system password
<input type="password" id="system_password" name="system_password" /> <input type="password" id="system_password" name="system_password" />
and and
<input type="submit" value="Delete Orphan Pages" /> <input type="submit" value="Delete Orphan Pages" />
</small> </small>
</p> </p>
</form> </form>
<script type="text/javascript" src="/javascripts/edit_web.js" /> <script type="text/javascript" src="/javascripts/edit_web.js" />
<script type="text/javascript">overrideAutocomplete()</script> <script type="text/javascript">overrideAutocomplete()</script>

24
app/views/wiki/export.rhtml Executable file → Normal file
View file

@ -1,12 +1,12 @@
<% @title = "Export" %> <% @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> <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"> <ul id="feedsList">
<li><a href="../export_html">HTML</a> <li><a href="../export_html">HTML</a>
<li><a href="../export_markup">Markup (<%= @web.markup %>)</a> <li><a href="../export_markup">Markup (<%= @web.markup %>)</a>
<% if OPTIONS[:pdflatex] && @web.markup == :textile %> <% if OPTIONS[:pdflatex] && @web.markup == :textile %>
<li><a href="../export_tex">TeX</a> <li><a href="../export_tex">TeX</a>
<li><a href="../export_pdf">PDF</a> <li><a href="../export_pdf">PDF</a>
<% end %> <% end %>
</ul> </ul>

16
app/views/wiki/feeds.rhtml Executable file → Normal file
View file

@ -1,8 +1,8 @@
<% @title = "Feeds" %> <% @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> <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"> <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_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> <li><a href="../rss_with_headlines<%= "?password=#{web.password}" if @web.password %>">Headlines (RSS 2.0)</a>
</ul> </ul>

114
app/views/wiki/list.rhtml Executable file → Normal file
View file

@ -1,57 +1,57 @@
<% @title = "All Pages" %> <% @title = "All Pages" %>
<% unless @categories.empty? %> <% unless @categories.empty? %>
<div id="categories"> <div id="categories">
<strong>Categories</strong>: <strong>Categories</strong>:
[<a href=".">Any</a>] [<a href=".">Any</a>]
<%= @category_links.join(', ') %> <%= @category_links.join(', ') %>
</div> </div>
<% end %> <% end %>
<div id="allPages" style="float: left; width: 280px; margin-right: 30px"> <div id="allPages" style="float: left; width: 280px; margin-right: 30px">
<% unless @pages_that_are_orphaned.empty? && @page_names_that_are_wanted.empty? %> <% unless @pages_that_are_orphaned.empty? && @page_names_that_are_wanted.empty? %>
<h2> <h2>
All Pages All Pages
<br/><small style="font-size: 12px"><i>All pages in <%= @set_name %> listed alphabetically</i></small> <br/><small style="font-size: 12px"><i>All pages in <%= @set_name %> listed alphabetically</i></small>
</h2> </h2>
<% end %> <% end %>
<ul><% for page in @pages_by_name %> <ul><% for page in @pages_by_name %>
<li><a href="../show/<%= page.name %>"><%= truncate(page.plain_name, 35) %></a></li> <li><a href="../show/<%= page.name %>"><%= truncate(page.plain_name, 35) %></a></li>
<% end %></ul> <% end %></ul>
<% if @web.count_pages %> <% if @web.count_pages %>
<% total_chars = @pages_in_category.characters %> <% total_chars = @pages_in_category.characters %>
<p><small>All content: <%= total_chars %> chars / <%= sprintf("%-.1f", (total_chars / 2275 )) %> pages</small></p> <p><small>All content: <%= total_chars %> chars / <%= sprintf("%-.1f", (total_chars / 2275 )) %> pages</small></p>
<% end %> <% end %>
</div> </div>
<div style="float: left; width: 280px"> <div style="float: left; width: 280px">
<% unless @page_names_that_are_wanted.empty? %> <% unless @page_names_that_are_wanted.empty? %>
<h2> <h2>
Wanted Pages Wanted Pages
<br/><small style="font-size: 12px"><i>Unexisting pages that other pages in <%= @set_name %> reference</i></small> <br/><small style="font-size: 12px"><i>Unexisting pages that other pages in <%= @set_name %> reference</i></small>
</h2> </h2>
<ul style="margin-bottom: 10px"> <ul style="margin-bottom: 10px">
<% for page_name in @page_names_that_are_wanted %> <% for page_name in @page_names_that_are_wanted %>
<li> <li>
<a href="../show/<%= page_name %>"><%= truncate(WikiWords.separate(page_name), 35) %></a> <a href="../show/<%= page_name %>"><%= truncate(WikiWords.separate(page_name), 35) %></a>
wanted by wanted by
<%= @web.select.pages_that_reference(page_name).collect { |page| page.link }.join(", ") %> <%= @web.select.pages_that_reference(page_name).collect { |page| page.link }.join(", ") %>
</li> </li>
<% end %> <% end %>
</ul> </ul>
<% end %> <% end %>
<% unless @pages_that_are_orphaned.empty? %> <% unless @pages_that_are_orphaned.empty? %>
<h2> <h2>
Orphaned Pages Orphaned Pages
<br/><small style="font-size: 12px"><i>Pages in <%= @set_name %> that no other page reference</i></small> <br/><small style="font-size: 12px"><i>Pages in <%= @set_name %> that no other page reference</i></small>
</h2> </h2>
<ul style="margin-bottom: 35px"> <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 %> <% for page in @pages_that_are_orphaned %><li><a href="../show/<%= page.name %>"><%= truncate(page.plain_name, 35) %></a></li><% end %>
</ul> </ul>
<% end %> <% end %>
</div> </div>

40
app/views/wiki/locked.rhtml Executable file → Normal file
View file

@ -1,20 +1,20 @@
<% @title = "#{@page.plain_name} is locked" %> <% @title = "#{@page.plain_name} is locked" %>
<% if @page.lock_duration(Time.now) == 0 %> <% if @page.lock_duration(Time.now) == 0 %>
<p><%= @page.locked_by_link %> just started editing this page.</p> <p><%= @page.locked_by_link %> just started editing this page.</p>
<% else %> <% else %>
<p><%= @page.locked_by_link %> has been editing this page for <%= @page.lock_duration(Time.now) %> minutes.</p> <p><%= @page.locked_by_link %> has been editing this page for <%= @page.lock_duration(Time.now) %> minutes.</p>
<% end %> <% end %>
<p> <p>
<%= link_to 'Edit the page anyway', <%= link_to 'Edit the page anyway',
{:web => @web_name, :action => 'edit', :id => @page.name, :params => {'break_lock' => '1'} }, {:web => @web_name, :action => 'edit', :id => @page.name, :params => {'break_lock' => '1'} },
{:accesskey => 'E'} {:accesskey => 'E'}
%> %>
<%= link_to 'Cancel', <%= link_to 'Cancel',
{:web => @web_name, :action => 'show', :id => @page.name}, {:web => @web_name, :action => 'show', :id => @page.name},
{:accesskey => 'C'} {:accesskey => 'C'}
%> %>
</p> </p>

16
app/views/wiki/login.rhtml Executable file → Normal file
View file

@ -1,8 +1,8 @@
<% @title = "#{@web_name} Login" %><% @hide_navigation = true %> <% @title = "#{@web_name} Login" %><% @hide_navigation = true %>
<form action="authenticate" method="post"> <form action="authenticate" method="post">
<p> <p>
<b>Password</b><br /> <b>Password</b><br />
<input type="password" name="password" /> <input type="password" name="password" />
</p> </p>
</form> </form>

50
app/views/wiki/new.rhtml Executable file → Normal file
View file

@ -1,25 +1,25 @@
<% <%
@title = "Creating #{WikiWords.separate(CGI.unescape(@page_name))}" @title = "Creating #{WikiWords.separate(CGI.unescape(@page_name))}"
@content_width = 720 @content_width = 720
@hide_navigation = true @hide_navigation = true
%> %>
<%= render("#{@web.markup}_help") if @web %> <%= render("#{@web.markup}_help") if @web %>
<form action="../save/<%= @page_name %>" method="post" onSubmit="cleanAuthorName();"> <form action="../save/<%= @page_name %>" method="post" onSubmit="cleanAuthorName();">
<p> <p>
<textarea name="content" style="width: 450px; height: 500px"></textarea> <textarea name="content" style="width: 450px; height: 500px"></textarea>
</p> </p>
<p> <p>
<input type="submit" value="Create" /> as <input type="submit" value="Create" /> as
<input type="text" name="author" id="authorName" value="<%= @author %>" onClick="this.value == 'AnonymousCoward' ? this.value = '' : true" /> <input type="text" name="author" id="authorName" value="<%= @author %>" onClick="this.value == 'AnonymousCoward' ? this.value = '' : true" />
</p> </p>
</form> </form>
<script language="JavaScript1.2"> <script language="JavaScript1.2">
function cleanAuthorName() { function cleanAuthorName() {
if (document.getElementById('authorName').value == "") { if (document.getElementById('authorName').value == "") {
document.getElementById('authorName').value = 'AnonymousCoward'; document.getElementById('authorName').value = 'AnonymousCoward';
} }
} }
</script> </script>

166
app/views/wiki/new_system.rhtml Executable file → Normal file
View file

@ -1,83 +1,83 @@
<% @title = "Instiki Setup"; @content_width = 500 %> <% @title = "Instiki Setup"; @content_width = 500 %>
<p> <p>
Congratulations on succesfully installing and starting Instiki. Congratulations on succesfully installing and starting Instiki.
Since this is the first time Instiki has been run on this port, Since this is the first time Instiki has been run on this port,
you'll need to do a brief one-time setup. you'll need to do a brief one-time setup.
</p> </p>
<form action="../create_system" id="setup" method="post" onSubmit="return validateSetup()"> <form action="../create_system" id="setup" method="post" onSubmit="return validateSetup()">
<ol class="setup"> <ol class="setup">
<li> <li>
<h2 style="margin-bottom: 3px">Name and address for your first web</h2> <h2 style="margin-bottom: 3px">Name and address for your first web</h2>
<div class="help"> <div class="help">
The name of the web is included in the title on all pages. 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. 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>. Ex: the address "rails" gives URLs like <i>/rails/show/HomePage</i>.
The address can only consist of letters and digits. The address can only consist of letters and digits.
</div> </div>
<div class="inputBox"> <div class="inputBox">
Name: <input type="text" id="web_name" name="web_name" value="Wiki" Name: <input type="text" id="web_name" name="web_name" value="Wiki"
onChange="proposeAddress();" onClick="this.value == 'Wiki' ? this.value = '' : true" /> onChange="proposeAddress();" onClick="this.value == 'Wiki' ? this.value = '' : true" />
&nbsp;&nbsp; &nbsp;&nbsp;
Address: <input type="text" id="web_address" name="web_address" onChange="cleanAddress();" Address: <input type="text" id="web_address" name="web_address" onChange="cleanAddress();"
value="wiki" /> value="wiki" />
</div> </div>
</li> </li>
<li> <li>
<h2 style="margin-bottom: 3px">Password for creating and changing webs</h2> <h2 style="margin-bottom: 3px">Password for creating and changing webs</h2>
<div class="help"> <div class="help">
Administrative access allows you to make new webs and change existing ones.<br/> 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. Everyone with this password will be able to do this, so pick it carefully.
</div> </div>
<div class="inputBox"> <div class="inputBox">
Password: <input type="password" id="password" name="password" /> Password: <input type="password" id="password" name="password" />
&nbsp;&nbsp; &nbsp;&nbsp;
Verify: <input type="password" id="password_check" name="password_check" /> Verify: <input type="password" id="password_check" name="password_check" />
</div> </div>
</li> </li>
</ol> </ol>
<p align="right"> <p align="right">
<input type="submit" value="Setup" style="margin-left: 40px" /> <input type="submit" value="Setup" style="margin-left: 40px" />
</p> </p>
</form> </form>
<script> <script>
function proposeAddress() { function proposeAddress() {
document.getElementById('web_address').value = document.getElementById('web_address').value =
document.getElementById('web_name').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase(); document.getElementById('web_name').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
} }
function cleanAddress() { function cleanAddress() {
document.getElementById('web_address').value = document.getElementById('web_address').value =
document.getElementById('web_address').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase(); document.getElementById('web_address').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
} }
function validateSetup() { function validateSetup() {
if (document.getElementById('web_name').value == "") { if (document.getElementById('web_name').value == "") {
alert("You must pick a name for the first web"); alert("You must pick a name for the first web");
return false; return false;
} }
if (document.getElementById('web_address').value == "") { if (document.getElementById('web_address').value == "") {
alert("You must pick an address for the first web"); alert("You must pick an address for the first web");
return false; return false;
} }
if (document.getElementById('password').value == "") { if (document.getElementById('password').value == "") {
alert("You must pick a system password"); alert("You must pick a system password");
return false; return false;
} }
if (document.getElementById('password_check').value == "" || if (document.getElementById('password_check').value == "" ||
document.getElementById('password').value != document.getElementById('password_check').value) { document.getElementById('password').value != document.getElementById('password_check').value) {
alert("The password and its verification doesn't match"); alert("The password and its verification doesn't match");
return false; return false;
} }
return true; return true;
} }
</script> </script>

138
app/views/wiki/new_web.rhtml Executable file → Normal file
View file

@ -1,69 +1,69 @@
<% @title = "New Wiki Web"; @content_width = 500 %> <% @title = "New Wiki Web"; @content_width = 500 %>
<p> <p>
Each web serves as an isolated name space for wiki pages, Each web serves as an isolated name space for wiki pages,
so different subjects or projects can write about different <i>MuppetShows</i>. so different subjects or projects can write about different <i>MuppetShows</i>.
</p> </p>
<form action="../create_web" id="setup" method="post" onSubmit="cleanAddress(); <form action="../create_web" id="setup" method="post" onSubmit="cleanAddress();
return validateSetup()"> return validateSetup()">
<ol class="setup"> <ol class="setup">
<li> <li>
<h2 style="margin-bottom: 3px">Name and address for your new web</h2> <h2 style="margin-bottom: 3px">Name and address for your new web</h2>
<div class="help"> <div class="help">
The name of the web is included in the title on all pages. 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. 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>. Ex: the address "rails" gives URLs like <i>/rails/show/HomePage</i>.
The address can only consist of letters and digits. The address can only consist of letters and digits.
</div> </div>
<div class="inputBox"> <div class="inputBox">
Name: <input type="text" id="web_name" name="name" onChange="proposeAddress();" /> Name: <input type="text" id="web_name" name="name" onChange="proposeAddress();" />
&nbsp;&nbsp; &nbsp;&nbsp;
Address: <input type="text" id="web_address" name="address" onChange="cleanAddress();" /> Address: <input type="text" id="web_address" name="address" onChange="cleanAddress();" />
</div> </div>
</li> </li>
</ol> </ol>
<p align="right"> <p align="right">
<small> <small>
Enter system password Enter system password
<input type="password" id="system_password" name="system_password" /> <input type="password" id="system_password" name="system_password" />
and and
<input type="submit" value="Create Web" /> <input type="submit" value="Create Web" />
</small> </small>
</p> </p>
</form> </form>
<script> <script>
function proposeAddress() { function proposeAddress() {
document.getElementById('web_address').value = document.getElementById('web_address').value =
document.getElementById('web_name').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase(); document.getElementById('web_name').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
} }
function cleanAddress() { function cleanAddress() {
document.getElementById('web_address').value = document.getElementById('web_address').value =
document.getElementById('web_address').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase(); document.getElementById('web_address').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
} }
function validateSetup() { function validateSetup() {
if (document.getElementById('web_name').value == "") { if (document.getElementById('web_name').value == "") {
alert("You must pick a name for the new web"); alert("You must pick a name for the new web");
return false; return false;
} }
if (document.getElementById('web_address').value == "") { if (document.getElementById('web_address').value == "") {
alert("You must pick an address for the new web"); alert("You must pick an address for the new web");
return false; return false;
} }
if (document.getElementById('system_password').value == "") { if (document.getElementById('system_password').value == "") {
alert("You must enter the system password"); alert("You must enter the system password");
return false; return false;
} }
return true; return true;
} }
</script> </script>

156
app/views/wiki/page.rhtml Executable file → Normal file
View file

@ -1,78 +1,78 @@
<% @title = @page.plain_name %> <% @title = @page.plain_name %>
<div id="revision"> <div id="revision">
<%= @page.display_content %> <%= @page.display_content %>
</div> </div>
<div id="changes" style="display: none"> <div id="changes" style="display: none">
<p style="background: #eee; padding: 3px; border: 1px solid silver"> <p style="background: #eee; padding: 3px; border: 1px solid silver">
<small> <small>
Showing changes from revision #<%= @page.number - 1 %> to #<%= @page.number %>: Showing changes from revision #<%= @page.number - 1 %> to #<%= @page.number %>:
<ins class="diffins">Added</ins> | <del class="diffdel">Removed</del> <ins class="diffins">Added</ins> | <del class="diffdel">Removed</del>
</small> </small>
</p> </p>
<%= @page.display_diff %> <%= @page.display_diff %>
</div> </div>
<div class="byline"> <div class="byline">
<%= @page.revisions? ? "Revised" : "Created" %> on <%= @page.pretty_created_at %> <%= @page.revisions? ? "Revised" : "Created" %> on <%= @page.pretty_created_at %>
by <%= @page.author_link %> by <%= @page.author_link %>
<%= "(#{@page.author.ip})" if @page.author.respond_to?(:ip) %> <%= "(#{@page.author.ip})" if @page.author.respond_to?(:ip) %>
<% if @web.count_pages %> <% if @web.count_pages %>
<% total_chars = @page.content.length %> <% total_chars = @page.content.length %>
(<%= total_chars %> characters / <%= sprintf("%-.1f", (total_chars / 2275 rescue 0)) %> pages) (<%= total_chars %> characters / <%= sprintf("%-.1f", (total_chars / 2275 rescue 0)) %> pages)
<% end %> <% end %>
</div> </div>
<div class="navigation"> <div class="navigation">
<% if @page.name == "HomePage" %> <% if @page.name == "HomePage" %>
<a href="../edit/<%= @page.name %>" class="navlink" accesskey="E">Edit Page</a> <a href="../edit/<%= @page.name %>" class="navlink" accesskey="E">Edit Page</a>
| <a href="../edit_web/" class="navlink">Edit Web</a> | <a href="../edit_web/" class="navlink">Edit Web</a>
<% else %> <% else %>
<a href="../edit/<%= @page.name %>" class="navlink" accesskey="E">Edit</a> <a href="../edit/<%= @page.name %>" class="navlink" accesskey="E">Edit</a>
<% end %> <% end %>
<% if @page.revisions.length > 1 %> <% if @page.revisions.length > 1 %>
| <a href="../revision/<%= @page.name %>?rev=<%= @page.revisions.length - 2 %>" class="navlink" accesskey="R">Back in time</a> | <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> <small>(<%= @page.revisions.length - 1 %> revisions)</small>
<% end %> <% end %>
<% if @page.revisions.length > 1 %> <% if @page.revisions.length > 1 %>
<span id="show_changes"> <span id="show_changes">
| <a href="#" onClick="toggleChanges(); return false;">See changes</a> | <a href="#" onClick="toggleChanges(); return false;">See changes</a>
</span> </span>
<span id="hide_changes" style="display: none"> <span id="hide_changes" style="display: none">
| <a href="#" onClick="toggleChanges(); return false;">Hide changes</a> | <a href="#" onClick="toggleChanges(); return false;">Hide changes</a>
</span> </span>
<% end %> <% end %>
<small> <small>
| Views: <a href="../print/<%= @page.name %>">Print</a> | Views: <a href="../print/<%= @page.name %>">Print</a>
<% if defined? RedClothForTex and RedClothForTex.available? and @web.markup == :textile %> <% if defined? RedClothForTex and RedClothForTex.available? and @web.markup == :textile %>
| <a href="../tex/<%= @page.name %>">TeX</a> | <a href="../pdf/<%= @page.name %>">PDF</a> | <a href="../tex/<%= @page.name %>">TeX</a> | <a href="../pdf/<%= @page.name %>">PDF</a>
<% end %> <% end %>
</small> </small>
<% if @page.references.length > 0 %> <% if @page.references.length > 0 %>
<small> <small>
| Linked from: <%= @page.references.collect { |ref| ref.link }.join(", ") %> | Linked from: <%= @page.references.collect { |ref| ref.link }.join(", ") %>
</small> </small>
<% end %> <% end %>
</div> </div>
<script language="Javascript"> <script language="Javascript">
function toggleChanges() { function toggleChanges() {
if (document.getElementById("changes").style.display == "none") { if (document.getElementById("changes").style.display == "none") {
document.getElementById("changes").style.display = "block"; document.getElementById("changes").style.display = "block";
document.getElementById("revision").style.display = "none"; document.getElementById("revision").style.display = "none";
document.getElementById("show_changes").style.display = "none"; document.getElementById("show_changes").style.display = "none";
document.getElementById("hide_changes").style.display = "inline"; document.getElementById("hide_changes").style.display = "inline";
} else { } else {
document.getElementById("changes").style.display = "none"; document.getElementById("changes").style.display = "none";
document.getElementById("revision").style.display = "block"; document.getElementById("revision").style.display = "block";
document.getElementById("show_changes").style.display = "inline"; document.getElementById("show_changes").style.display = "inline";
document.getElementById("hide_changes").style.display = "none"; document.getElementById("hide_changes").style.display = "none";
} }
} }
</script> </script>

28
app/views/wiki/print.rhtml Executable file → Normal file
View file

@ -1,14 +1,14 @@
<% <%
@title = @page.plain_name @title = @page.plain_name
@hide_navigation = true @hide_navigation = true
@style_additions = ".newWikiWord { background-color: white; font-style: italic; }" @style_additions = ".newWikiWord { background-color: white; font-style: italic; }"
@inline_style = true @inline_style = true
%> %>
<%= @page.display_content_for_export %> <%= @page.display_content_for_export %>
<div class="byline"> <div class="byline">
<%= @page.revisions? ? "Revised" : "Created" %> on <%= @page.pretty_created_at %> <%= @page.revisions? ? "Revised" : "Created" %> on <%= @page.pretty_created_at %>
by by
<%= @page.author_link({ :mode => :export }) %> <%= @page.author_link({ :mode => :export }) %>
</div> </div>

16
app/views/wiki/published.rhtml Executable file → Normal file
View file

@ -1,8 +1,8 @@
<% <%
@title = @page.plain_name @title = @page.plain_name
@hide_navigation = false @hide_navigation = false
@style_additions = ".newWikiWord { background-color: white; font-style: italic; }" @style_additions = ".newWikiWord { background-color: white; font-style: italic; }"
@inline_style = true @inline_style = true
%> %>
<%= @page.display_published %> <%= @page.display_published %>

64
app/views/wiki/recently_revised.rhtml Executable file → Normal file
View file

@ -1,32 +1,32 @@
<% @title = "Recently Revised" %> <% @title = "Recently Revised" %>
<% unless @categories.empty? %> <% unless @categories.empty? %>
<div id="categories"> <div id="categories">
<strong>Categories</strong>: <strong>Categories</strong>:
[<a href=".">Any</a>] [<a href=".">Any</a>]
<%= @category_links.join(', ') %> <%= @category_links.join(', ') %>
</div> </div>
<% end %> <% end %>
<% unless @pages_by_revision.empty? %> <% unless @pages_by_revision.empty? %>
<% revision_date = @pages_by_revision.first.revised_on %> <% revision_date = @pages_by_revision.first.revised_on %>
<h3><%= revision_date.strftime('%B %e, %Y') %></h3> <h3><%= revision_date.strftime('%B %e, %Y') %></h3>
<ul> <ul>
<% for page in @pages_by_revision %> <% for page in @pages_by_revision %>
<% if page.revised_on < revision_date %> <% if page.revised_on < revision_date %>
<% revision_date = page.revised_on %> <% revision_date = page.revised_on %>
</ul> </ul>
<h3><%= revision_date.strftime('%B %e, %Y') %></h3> <h3><%= revision_date.strftime('%B %e, %Y') %></h3>
<ul> <ul>
<% end %> <% end %>
<li> <li>
<a href="../show/<%= page.name %>"><%= page.plain_name %></a> <a href="../show/<%= page.name %>"><%= page.plain_name %></a>
<div class="byline" style="margin-bottom: 0px"> <div class="byline" style="margin-bottom: 0px">
by <%= page.author_link %> by <%= page.author_link %>
at <%= page.created_at.strftime "%H:%M" %> at <%= page.created_at.strftime "%H:%M" %>
<%= "from #{page.author.ip}" if page.author.respond_to?(:ip) %> <%= "from #{page.author.ip}" if page.author.respond_to?(:ip) %>
</div> </div>
</li> </li>
<% end %> <% end %>
</ul> </ul>
<% end %> <% end %>

158
app/views/wiki/revision.rhtml Executable file → Normal file
View file

@ -1,79 +1,79 @@
<% @title = "#{@page.plain_name} (Rev ##{@revision.number})" %> <% @title = "#{@page.plain_name} (Rev ##{@revision.number})" %>
<div id="revision"> <div id="revision">
<%= @revision.display_content %> <%= @revision.display_content %>
</div> </div>
<div id="changes" style="display: none"> <div id="changes" style="display: none">
<p style="background: #eee; padding: 3px; border: 1px solid silver"> <p style="background: #eee; padding: 3px; border: 1px solid silver">
<small> <small>
Showing changes from revision #<%= @revision.number - 1 %> to #<%= @revision.number %>: Showing changes from revision #<%= @revision.number - 1 %> to #<%= @revision.number %>:
<ins class="diffins">Added</ins> | <del class="diffdel">Removed</del> <ins class="diffins">Added</ins> | <del class="diffdel">Removed</del>
</small> </small>
</p> </p>
<%= @revision.display_diff %> <%= @revision.display_diff %>
</div> </div>
<div class="byline"> <div class="byline">
<%= "Revision from #{@revision.pretty_created_at} by" %> <%= "Revision from #{@revision.pretty_created_at} by" %>
<%= @page.web.make_link(@revision.author) %> <%= @page.web.make_link(@revision.author) %>
</div> </div>
<div class="navigation"> <div class="navigation">
<% if @revision.next_revision %> <% if @revision.next_revision %>
<% if @revision.next_revision.number < (@page.revisions.length - 1) %> <% if @revision.next_revision.number < (@page.revisions.length - 1) %>
<a href="../revision/<%= @page.name %>?rev=<%= @revision.next_revision.number %>" class="navlink"> <a href="../revision/<%= @page.name %>?rev=<%= @revision.next_revision.number %>" class="navlink">
<% else %> <% else %>
<a href="../show/<%= @page.name %>" class="navlink"> <a href="../show/<%= @page.name %>" class="navlink">
<% end %> <% end %>
Forward in time</a> Forward in time</a>
(<%= @revision.page.revisions.length - @revision.next_revision.number %> more) (<%= @revision.page.revisions.length - @revision.next_revision.number %> more)
<% end %> <% end %>
<% if @revision.next_revision && @revision.previous_revision %> <% if @revision.next_revision && @revision.previous_revision %>
| |
<% end %> <% end %>
<% if @revision.previous_revision %> <% if @revision.previous_revision %>
<a href="../revision/<%= @page.name %>?rev=<%= @revision.previous_revision.number %>" class="navlink">Back in time</a> <a href="../revision/<%= @page.name %>?rev=<%= @revision.previous_revision.number %>" class="navlink">Back in time</a>
(<%= @revision.previous_revision.number + 1 %> more) (<%= @revision.previous_revision.number + 1 %> more)
<% end %> <% end %>
| <a href="../show/<%= @page.name %>" class="navlink">See current</a> | <a href="../show/<%= @page.name %>" class="navlink">See current</a>
<% if @revision.previous_revision %> <% if @revision.previous_revision %>
<span id="show_changes"> <span id="show_changes">
| <a href="#" onClick="toggleChanges(); return false;">See changes</a> | <a href="#" onClick="toggleChanges(); return false;">See changes</a>
</span> </span>
<span id="hide_changes" style="display: none"> <span id="hide_changes" style="display: none">
| <a href="#" onClick="toggleChanges(); return false;">Hide changes</a> | <a href="#" onClick="toggleChanges(); return false;">Hide changes</a>
</span> </span>
<% end %> <% end %>
| <a href="../rollback/<%= @page.name %>?rev=<%= @revision.number %>" class="navlink">Rollback</a> | <a href="../rollback/<%= @page.name %>?rev=<%= @revision.number %>" class="navlink">Rollback</a>
<% if @page.references.length > 0 %> <% if @page.references.length > 0 %>
<small> <small>
| Linked from: <%= @page.references.collect { |ref| "<a href='#{ref.name}'>#{ref.name}</a>" }.join(", ") %> | Linked from: <%= @page.references.collect { |ref| "<a href='#{ref.name}'>#{ref.name}</a>" }.join(", ") %>
</small> </small>
<% end %> <% end %>
</div> </div>
<script language="Javascript"> <script language="Javascript">
function toggleChanges() { function toggleChanges() {
if (document.getElementById("changes").style.display == "none") { if (document.getElementById("changes").style.display == "none") {
document.getElementById("changes").style.display = "block"; document.getElementById("changes").style.display = "block";
document.getElementById("revision").style.display = "none"; document.getElementById("revision").style.display = "none";
document.getElementById("show_changes").style.display = "none"; document.getElementById("show_changes").style.display = "none";
document.getElementById("hide_changes").style.display = "inline"; document.getElementById("hide_changes").style.display = "inline";
} else { } else {
document.getElementById("changes").style.display = "none"; document.getElementById("changes").style.display = "none";
document.getElementById("revision").style.display = "block"; document.getElementById("revision").style.display = "block";
document.getElementById("show_changes").style.display = "inline"; document.getElementById("show_changes").style.display = "inline";
document.getElementById("hide_changes").style.display = "none"; document.getElementById("hide_changes").style.display = "none";
} }
} }
</script> </script>

0
app/views/wiki/rollback.rhtml Executable file → Normal file
View file

44
app/views/wiki/rss_feed.rhtml Executable file → Normal file
View file

@ -1,22 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"> <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel> <channel>
<title><%= @web.name %></title> <title><%= @web.name %></title>
<link><%= url_for :only_path => false, :web => @web_name, :action => 'show', :id => 'HomePage' %></link> <link><%= url_for :only_path => false, :web => @web_name, :action => 'show', :id => 'HomePage' %></link>
<description>An Instiki wiki</description> <description>An Instiki wiki</description>
<language>en-us</language> <language>en-us</language>
<ttl>40</ttl> <ttl>40</ttl>
<% for page in @pages_by_revision %> <% for page in @pages_by_revision %>
<item> <item>
<title><%= page.plain_name %></title> <title><%= page.plain_name %></title>
<% unless @hide_description %> <% unless @hide_description %>
<description><%= CGI.escapeHTML(page.display_content) %></description> <description><%= CGI.escapeHTML(page.display_content) %></description>
<% end %> <% end %>
<pubDate><%= page.created_at.strftime "%a, %e %b %Y %H:%M:%S %Z" %></pubDate> <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> <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> <link><%= url_for :only_path => false, :web => @web_name, :action => 'show', :id => page.name %></link>
<dc:creator><%= WikiWords.separate(page.author) %></dc:creator> <dc:creator><%= WikiWords.separate(page.author) %></dc:creator>
</item> </item>
<% end %> <% end %>
</channel> </channel>
</rss> </rss>

26
app/views/wiki/search.rhtml Executable file → Normal file
View file

@ -1,13 +1,13 @@
<% @title = @results.length > 0 ? "#{@results.length} pages contains \"#{@params["query"]}\"" : "No pages contains \"#{@query}\"" %> <% @title = @results.length > 0 ? "#{@results.length} pages contains \"#{@params["query"]}\"" : "No pages contains \"#{@query}\"" %>
<% if @results.length > 0 %> <% if @results.length > 0 %>
<ul> <ul>
<% for page in @results %> <% for page in @results %>
<li><a href="../show/<%= page.name %>"><%= page.plain_name %></a></li> <li><a href="../show/<%= page.name %>"><%= page.plain_name %></a></li>
<% end %> <% end %>
</ul> </ul>
<% else %> <% 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&mdash;only as a sentence fragment.</p> <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&mdash;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> <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 %> <% end %>

44
app/views/wiki/tex.rhtml Executable file → Normal file
View file

@ -1,23 +1,23 @@
\documentclass[12pt,titlepage]{article} \documentclass[12pt,titlepage]{article}
\usepackage[danish]{babel} %danske tekster \usepackage[danish]{babel} %danske tekster
\usepackage[OT1]{fontenc} %rigtige danske bogstaver... \usepackage[OT1]{fontenc} %rigtige danske bogstaver...
\usepackage{a4} \usepackage{a4}
\usepackage{graphicx} \usepackage{graphicx}
\usepackage{ucs} \usepackage{ucs}
\usepackage[utf8x]{inputenc} \usepackage[utf8x]{inputenc}
\input epsf \input epsf
%------------------------------------------------------------------- %-------------------------------------------------------------------
\begin{document} \begin{document}
\sloppy \sloppy
%------------------------------------------------------------------- %-------------------------------------------------------------------
\section*{<%= @page.name %>} \section*{<%= @page.name %>}
<%= @tex_content %> <%= @tex_content %>
\end{document} \end{document}

68
app/views/wiki/tex_web.rhtml Executable file → Normal file
View file

@ -1,35 +1,35 @@
\documentclass[12pt,titlepage]{article} \documentclass[12pt,titlepage]{article}
\usepackage{fancyhdr} \usepackage{fancyhdr}
\pagestyle{fancy} \pagestyle{fancy}
\fancyhead[LE,RO]{} \fancyhead[LE,RO]{}
\fancyhead[LO,RE]{\nouppercase{\bfseries \leftmark}} \fancyhead[LO,RE]{\nouppercase{\bfseries \leftmark}}
\fancyfoot[C]{\thepage} \fancyfoot[C]{\thepage}
\usepackage[danish]{babel} %danske tekster \usepackage[danish]{babel} %danske tekster
\usepackage{a4} \usepackage{a4}
\usepackage{graphicx} \usepackage{graphicx}
\usepackage{ucs} \usepackage{ucs}
\usepackage[utf8]{inputenc} \usepackage[utf8]{inputenc}
\input epsf \input epsf
%------------------------------------------------------------------- %-------------------------------------------------------------------
\title{<%= @web_name %>} \title{<%= @web_name %>}
\begin{document} \begin{document}
\maketitle \maketitle
\tableofcontents \tableofcontents
\pagebreak \pagebreak
\sloppy \sloppy
%------------------------------------------------------------------- %-------------------------------------------------------------------
<%= @tex_content %> <%= @tex_content %>
\end{document} \end{document}

36
app/views/wiki/web_list.rhtml Executable file → Normal file
View file

@ -1,18 +1,18 @@
<% @title = "Wiki webs" %> <% @title = "Wiki webs" %>
<ul> <ul>
<% for web in @webs %> <% for web in @webs %>
<li> <li>
<% if web.published %> <% if web.published %>
<%= web.make_link 'HomePage', web.name, :mode => :publish %> (read-only) / <%= web.make_link 'HomePage', web.name, :mode => :publish %> (read-only) /
<%= web.make_link 'HomePage', 'editable version', :mode => :edit %> (requires login) <%= web.make_link 'HomePage', 'editable version', :mode => :edit %> (requires login)
<% else %> <% else %>
<%= web.make_link 'HomePage', nil, :mode => :edit %> <%= web.make_link 'HomePage', nil, :mode => :edit %>
<% end %> <% end %>
<div class="byline" style="margin-bottom: 0px"> <div class="byline" style="margin-bottom: 0px">
<%= web.pages.length %> pages by <%= web.authors.length %> authors <%= web.pages.length %> pages by <%= web.authors.length %> authors
</div> </div>
</li> </li>
<% end %> <% end %>
</ul> </ul>

18
app/views/wiki_words_help.rhtml Executable file → Normal file
View file

@ -1,9 +1,9 @@
<h3>Wiki words</h3> <h3>Wiki words</h3>
<p style="border-top: 1px dotted #ccc; margin-top: 0px"> <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 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. brackets is a wiki word. A camel-case wiki word can be escaped by putting \ in front of it.
</p> </p>
<p> <p>
Wiki words: <i>HomePage, ThreeWordsTogether, [[C++]], [[Let's play again!]]</i><br/> Wiki words: <i>HomePage, ThreeWordsTogether, [[C++]], [[Let's play again!]]</i><br/>
Not wiki words: <i>IBM, School</i> Not wiki words: <i>IBM, School</i>
</p> </p>

8
config/environments/development.rb Executable file → Normal file
View file

@ -1,4 +1,4 @@
Dependencies.mechanism = :require Dependencies.mechanism = :require
ActionController::Base.consider_all_requests_local = true ActionController::Base.consider_all_requests_local = true
BREAKPOINT_SERVER_PORT = 42531 BREAKPOINT_SERVER_PORT = 42531
ActionController::Base.logger.level = Logger::DEBUG ActionController::Base.logger.level = Logger::DEBUG

44
libraries/active_record_stub.rb Executable file → Normal file
View file

@ -1,23 +1,23 @@
# This project uses Railties, which has an external dependency on ActiveRecord # This project uses Railties, which has an external dependency on ActiveRecord
# Since ActiveRecord may not be present in Instiki runtime environment, this # Since ActiveRecord may not be present in Instiki runtime environment, this
# file provides a stub replacement for it # file provides a stub replacement for it
unless defined? ActiveRecord::Base unless defined? ActiveRecord::Base
module ActiveRecord module ActiveRecord
class Base class Base
# dependency in railties/lib/dispatcher.rb # dependency in railties/lib/dispatcher.rb
def self.reset_column_information_and_inheritable_attributes_for_all_subclasses def self.reset_column_information_and_inheritable_attributes_for_all_subclasses
# noop # noop
end end
# dependency in actionpack/lib/action_controller/benchmarking.rb # dependency in actionpack/lib/action_controller/benchmarking.rb
def self.connected? def self.connected?
false false
end end
end end
end end
end end

948
libraries/diff.rb Executable file → Normal file
View file

@ -1,475 +1,475 @@
# heavily based off difflib.py - see that file for documentation # heavily based off difflib.py - see that file for documentation
# ported from Python by Bill Atkins # ported from Python by Bill Atkins
# This does not support all features offered by difflib; it # This does not support all features offered by difflib; it
# implements only the subset of features necessary # implements only the subset of features necessary
# to support a Ruby version of HTML Differ. You're welcome to finish this off. # 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 # 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- # for diff, so often a string will be split by // to get an array of one-
# character strings. # character strings.
# Some methods in Diff are untested and are not guaranteed to work. The # 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. # methods in HTMLDiff and any methods it calls should work quite well.
# changes by DenisMertz # changes by DenisMertz
# * main change: # * main change:
# ** get the tag soup away # ** get the tag soup away
# the tag soup problem was first reported with <p> tags, but it appeared also with # the tag soup problem was first reported with <p> tags, but it appeared also with
# <li>, <ul> etc... tags # <li>, <ul> etc... tags
# this version should mostly fix these problems # this version should mostly fix these problems
# ** added a Builder class to manage the creation of the final htmldiff # ** added a Builder class to manage the creation of the final htmldiff
# * minor changes: # * minor changes:
# ** use symbols instead of string to represent opcodes # ** use symbols instead of string to represent opcodes
# ** small fix to html2list # ** small fix to html2list
# #
module Enumerable module Enumerable
def reduce(init) def reduce(init)
result = init result = init
each { |item| result = yield(result, item) } each { |item| result = yield(result, item) }
result result
end end
end end
module Diff module Diff
class SequenceMatcher class SequenceMatcher
def initialize(a=[''], b=[''], isjunk=nil, byline=false) def initialize(a=[''], b=[''], isjunk=nil, byline=false)
a = (!byline and a.kind_of? String) ? a.split(//) : a a = (!byline and a.kind_of? String) ? a.split(//) : a
b = (!byline and b.kind_of? String) ? b.split(//) : b b = (!byline and b.kind_of? String) ? b.split(//) : b
@isjunk = isjunk || proc {} @isjunk = isjunk || proc {}
set_seqs a, b set_seqs a, b
end end
def set_seqs(a, b) def set_seqs(a, b)
set_seq_a a set_seq_a a
set_seq_b b set_seq_b b
end end
def set_seq_a(a) def set_seq_a(a)
@a = a @a = a
@matching_blocks = @opcodes = nil @matching_blocks = @opcodes = nil
end end
def set_seq_b(b) def set_seq_b(b)
@b = b @b = b
@matching_blocks = @opcodes = nil @matching_blocks = @opcodes = nil
chain_b chain_b
end end
def chain_b def chain_b
@fullbcount = nil @fullbcount = nil
@b2j = {} @b2j = {}
pophash = {} pophash = {}
junkdict = {} junkdict = {}
@b.each_with_index do |elt, i| @b.each_with_index do |elt, i|
if @b2j.has_key? elt if @b2j.has_key? elt
indices = @b2j[elt] indices = @b2j[elt]
if @b.length >= 200 and indices.length * 100 > @b.length if @b.length >= 200 and indices.length * 100 > @b.length
pophash[elt] = 1 pophash[elt] = 1
indices.clear indices.clear
else else
indices.push i indices.push i
end end
else else
@b2j[elt] = [i] @b2j[elt] = [i]
end end
end end
pophash.each_key { |elt| @b2j.delete elt } pophash.each_key { |elt| @b2j.delete elt }
junkdict = {} junkdict = {}
unless @isjunk.nil? unless @isjunk.nil?
[pophash, @b2j].each do |d| [pophash, @b2j].each do |d|
d.each_key do |elt| d.each_key do |elt|
if @isjunk.call(elt) if @isjunk.call(elt)
junkdict[elt] = 1 junkdict[elt] = 1
d.delete elt d.delete elt
end end
end end
end end
end end
@isbjunk = junkdict.method(:has_key?) @isbjunk = junkdict.method(:has_key?)
@isbpopular = junkdict.method(:has_key?) @isbpopular = junkdict.method(:has_key?)
end end
def find_longest_match(alo, ahi, blo, bhi) def find_longest_match(alo, ahi, blo, bhi)
besti, bestj, bestsize = alo, blo, 0 besti, bestj, bestsize = alo, blo, 0
j2len = {} j2len = {}
(alo..ahi).step do |i| (alo..ahi).step do |i|
newj2len = {} newj2len = {}
(@b2j[@a[i]] || []).each do |j| (@b2j[@a[i]] || []).each do |j|
if j < blo if j < blo
next next
end end
if j >= bhi if j >= bhi
break break
end end
k = newj2len[j] = (j2len[j - 1] || 0) + 1 k = newj2len[j] = (j2len[j - 1] || 0) + 1
if k > bestsize if k > bestsize
besti, bestj, bestsize = i - k + 1, j - k + 1, k besti, bestj, bestsize = i - k + 1, j - k + 1, k
end end
end end
j2len = newj2len j2len = newj2len
end end
while besti > alo and bestj > blo and while besti > alo and bestj > blo and
not @isbjunk.call(@b[bestj-1]) and not @isbjunk.call(@b[bestj-1]) and
@a[besti-1] == @b[bestj-1] @a[besti-1] == @b[bestj-1]
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
end end
while besti+bestsize < ahi and bestj+bestsize < bhi and while besti+bestsize < ahi and bestj+bestsize < bhi and
not @isbjunk.call(@b[bestj+bestsize]) and not @isbjunk.call(@b[bestj+bestsize]) and
@a[besti+bestsize] == @b[bestj+bestsize] @a[besti+bestsize] == @b[bestj+bestsize]
bestsize += 1 bestsize += 1
end end
while besti > alo and bestj > blo and while besti > alo and bestj > blo and
@isbjunk.call(@b[bestj-1]) and @isbjunk.call(@b[bestj-1]) and
@a[besti-1] == @b[bestj-1] @a[besti-1] == @b[bestj-1]
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
end end
while besti+bestsize < ahi and bestj+bestsize < bhi and while besti+bestsize < ahi and bestj+bestsize < bhi and
@isbjunk.call(@b[bestj+bestsize]) and @isbjunk.call(@b[bestj+bestsize]) and
@a[besti+bestsize] == @b[bestj+bestsize] @a[besti+bestsize] == @b[bestj+bestsize]
bestsize += 1 bestsize += 1
end end
[besti, bestj, bestsize] [besti, bestj, bestsize]
end end
def get_matching_blocks def get_matching_blocks
return @matching_blocks unless @matching_blocks.nil? or return @matching_blocks unless @matching_blocks.nil? or
@matching_blocks.empty? @matching_blocks.empty?
@matching_blocks = [] @matching_blocks = []
la, lb = @a.length, @b.length la, lb = @a.length, @b.length
match_block_helper(0, la, 0, lb, @matching_blocks) match_block_helper(0, la, 0, lb, @matching_blocks)
@matching_blocks.push [la, lb, 0] @matching_blocks.push [la, lb, 0]
end end
def match_block_helper(alo, ahi, blo, bhi, answer) def match_block_helper(alo, ahi, blo, bhi, answer)
i, j, k = x = find_longest_match(alo, ahi, blo, bhi) i, j, k = x = find_longest_match(alo, ahi, blo, bhi)
if not k.zero? if not k.zero?
if alo < i and blo < j if alo < i and blo < j
match_block_helper(alo, i, blo, j, answer) match_block_helper(alo, i, blo, j, answer)
end end
answer.push x answer.push x
if i + k < ahi and j + k < bhi if i + k < ahi and j + k < bhi
match_block_helper(i + k, ahi, j + k, bhi, answer) match_block_helper(i + k, ahi, j + k, bhi, answer)
end end
end end
end end
def get_opcodes def get_opcodes
unless @opcodes.nil? or @opcodes.empty? unless @opcodes.nil? or @opcodes.empty?
return @opcodes return @opcodes
end end
i = j = 0 i = j = 0
@opcodes = answer = [] @opcodes = answer = []
get_matching_blocks.each do |ai, bj, size| get_matching_blocks.each do |ai, bj, size|
tag = if i < ai and j < bj tag = if i < ai and j < bj
:replace :replace
elsif i < ai elsif i < ai
:delete :delete
elsif j < bj elsif j < bj
:insert :insert
end end
answer.push [tag, i, ai, j, bj] if tag answer.push [tag, i, ai, j, bj] if tag
i, j = ai + size, bj + size i, j = ai + size, bj + size
answer.push [:equal, ai, i, bj, j] unless size.zero? answer.push [:equal, ai, i, bj, j] unless size.zero?
end end
return answer return answer
end end
# XXX: untested # XXX: untested
def get_grouped_opcodes(n=3) def get_grouped_opcodes(n=3)
codes = get_opcodes codes = get_opcodes
if codes[0][0] == :equal if codes[0][0] == :equal
tag, i1, i2, j1, j2 = codes[0] tag, i1, i2, j1, j2 = codes[0]
codes[0] = tag, [i1, i2 - n].max, i2, [j1, j2-n].max, j2 codes[0] = tag, [i1, i2 - n].max, i2, [j1, j2-n].max, j2
end end
if codes[-1][0] == :equal if codes[-1][0] == :equal
tag, i1, i2, j1, j2 = codes[-1] tag, i1, i2, j1, j2 = codes[-1]
codes[-1] = tag, i1, min(i2, i1+n), j1, min(j2, j1+n) codes[-1] = tag, i1, min(i2, i1+n), j1, min(j2, j1+n)
end end
nn = n + n nn = n + n
group = [] group = []
codes.each do |tag, i1, i2, j1, j2| codes.each do |tag, i1, i2, j1, j2|
if tag == :equal and i2-i1 > nn if tag == :equal and i2-i1 > nn
group.push [tag, i1, [i2, i1 + n].min, j1, [j2, j1 + n].min] group.push [tag, i1, [i2, i1 + n].min, j1, [j2, j1 + n].min]
yield group yield group
group = [] group = []
i1, j1 = [i1, i2-n].max, [j1, j2-n].max i1, j1 = [i1, i2-n].max, [j1, j2-n].max
group.push [tag, i1, i2, j1 ,j2] group.push [tag, i1, i2, j1 ,j2]
end end
end end
if group and group.length != 1 and group[0][0] == :equal if group and group.length != 1 and group[0][0] == :equal
yield group yield group
end end
end end
def ratio def ratio
matches = get_matching_blocks.reduce(0) do |sum, triple| matches = get_matching_blocks.reduce(0) do |sum, triple|
sum + triple[-1] sum + triple[-1]
end end
Diff.calculate_ratio(matches, @a.length + @b.length) Diff.calculate_ratio(matches, @a.length + @b.length)
end end
def quick_ratio def quick_ratio
if @fullbcount.nil? or @fullbcount.empty? if @fullbcount.nil? or @fullbcount.empty?
@fullbcount = {} @fullbcount = {}
@b.each do |elt| @b.each do |elt|
@fullbcount[elt] = (@fullbcount[elt] || 0) + 1 @fullbcount[elt] = (@fullbcount[elt] || 0) + 1
end end
end end
avail = {} avail = {}
matches = 0 matches = 0
@a.each do |elt| @a.each do |elt|
if avail.has_key? elt if avail.has_key? elt
numb = avail[elt] numb = avail[elt]
else else
numb = @fullbcount[elt] || 0 numb = @fullbcount[elt] || 0
end end
avail[elt] = numb - 1 avail[elt] = numb - 1
if numb > 0 if numb > 0
matches += 1 matches += 1
end end
end end
Diff.calculate_ratio matches, @a.length + @b.length Diff.calculate_ratio matches, @a.length + @b.length
end end
def real_quick_ratio def real_quick_ratio
la, lb = @a.length, @b.length la, lb = @a.length, @b.length
Diff.calculate_ratio([la, lb].min, la + lb) Diff.calculate_ratio([la, lb].min, la + lb)
end end
protected :chain_b, :match_block_helper protected :chain_b, :match_block_helper
end # end class SequenceMatcher end # end class SequenceMatcher
def self.calculate_ratio(matches, length) def self.calculate_ratio(matches, length)
return 1.0 if length.zero? return 1.0 if length.zero?
2.0 * matches / length 2.0 * matches / length
end end
# XXX: untested # XXX: untested
def self.get_close_matches(word, possibilities, n=3, cutoff=0.6) def self.get_close_matches(word, possibilities, n=3, cutoff=0.6)
unless n > 0 unless n > 0
raise "n must be > 0: #{n}" raise "n must be > 0: #{n}"
end end
unless 0.0 <= cutoff and cutoff <= 1.0 unless 0.0 <= cutoff and cutoff <= 1.0
raise "cutoff must be in (0.0..1.0): #{cutoff}" raise "cutoff must be in (0.0..1.0): #{cutoff}"
end end
result = [] result = []
s = SequenceMatcher.new s = SequenceMatcher.new
s.set_seq_b word s.set_seq_b word
possibilities.each do |x| possibilities.each do |x|
s.set_seq_a x s.set_seq_a x
if s.real_quick_ratio >= cutoff and if s.real_quick_ratio >= cutoff and
s.quick_ratio >= cutoff and s.quick_ratio >= cutoff and
s.ratio >= cutoff s.ratio >= cutoff
result.push [s.ratio, x] result.push [s.ratio, x]
end end
end end
unless result.nil? or result.empty? unless result.nil? or result.empty?
result.sort result.sort
result.reverse! result.reverse!
result = result[-n..-1] result = result[-n..-1]
end end
result.collect { |score, x| x } result.collect { |score, x| x }
end end
def self.count_leading(line, ch) def self.count_leading(line, ch)
i, n = 0, line.length i, n = 0, line.length
while i < n and line[i].chr == ch while i < n and line[i].chr == ch
i += 1 i += 1
end end
i i
end end
end end
module HTMLDiff module HTMLDiff
include Diff include Diff
class Builder class Builder
VALID_METHODS = [:replace, :insert, :delete, :equal] VALID_METHODS = [:replace, :insert, :delete, :equal]
def initialize(a, b) def initialize(a, b)
@a = a @a = a
@b = b @b = b
@content = [] @content = []
end end
def do_op(opcode) def do_op(opcode)
@opcode = opcode @opcode = opcode
op = @opcode[0] op = @opcode[0]
VALID_METHODS.include?(op) or raise(NameError, "Invalid opcode #{op}") VALID_METHODS.include?(op) or raise(NameError, "Invalid opcode #{op}")
self.method(op).call self.method(op).call
end end
def result def result
@content.join('') @content.join('')
end end
#this methods have to be called via do_op(opcode) so that @opcode is set properly #this methods have to be called via do_op(opcode) so that @opcode is set properly
private private
def replace def replace
delete("diffmod") delete("diffmod")
insert("diffmod") insert("diffmod")
end end
def insert(tagclass="diffins") def insert(tagclass="diffins")
op_helper("ins", tagclass, @b[@opcode[3]...@opcode[4]]) op_helper("ins", tagclass, @b[@opcode[3]...@opcode[4]])
end end
def delete(tagclass="diffdel") def delete(tagclass="diffdel")
op_helper("del", tagclass, @a[@opcode[1]...@opcode[2]]) op_helper("del", tagclass, @a[@opcode[1]...@opcode[2]])
end end
def equal def equal
@content += @b[@opcode[3]...@opcode[4]] @content += @b[@opcode[3]...@opcode[4]]
end end
# using this as op_helper would be equivalent to the first version of diff.rb by Bill Atkins # 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) def op_helper_simple(tagname, tagclass, to_add)
@content << "<#{tagname} class=\"#{tagclass}\">" @content << "<#{tagname} class=\"#{tagclass}\">"
@content += to_add @content += to_add
@content << "</#{tagname}>" @content << "</#{tagname}>"
end end
# this tries to put <p> tags or newline chars before the opening diff tags (<ins> or <del>) # this tries to put <p> tags or newline chars before the opening diff tags (<ins> or <del>)
# or after the ending diff tags # or after the ending diff tags
# as a result the diff tags should be the "more inside" possible. # as a result the diff tags should be the "more inside" possible.
# this seems to work nice with html containing only paragraphs # this seems to work nice with html containing only paragraphs
# but not sure it works if there are other tags (div, span ... ? ) around # but not sure it works if there are other tags (div, span ... ? ) around
def op_helper(tagname, tagclass, to_add) def op_helper(tagname, tagclass, to_add)
@content << to_add.shift while ( HTMLDiff.is_newline(to_add.first) or @content << to_add.shift while ( HTMLDiff.is_newline(to_add.first) or
HTMLDiff.is_p_close_tag(to_add.first) or HTMLDiff.is_p_close_tag(to_add.first) or
HTMLDiff.is_p_open_tag(to_add.first) ) HTMLDiff.is_p_open_tag(to_add.first) )
@content << "<#{tagname} class=\"#{tagclass}\">" @content << "<#{tagname} class=\"#{tagclass}\">"
@content += to_add @content += to_add
last_tags = [] last_tags = []
last_tags.unshift(@content.pop) while ( HTMLDiff.is_newline(@content.last) or last_tags.unshift(@content.pop) while ( HTMLDiff.is_newline(@content.last) or
HTMLDiff.is_p_close_tag(@content.last) or HTMLDiff.is_p_close_tag(@content.last) or
HTMLDiff.is_p_open_tag(@content.last) ) HTMLDiff.is_p_open_tag(@content.last) )
last_tags.unshift "</#{tagname}>" last_tags.unshift "</#{tagname}>"
@content += last_tags @content += last_tags
remove_empty_diff(tagname, tagclass) remove_empty_diff(tagname, tagclass)
end end
def remove_empty_diff(tagname, tagclass) def remove_empty_diff(tagname, tagclass)
if @content[-2] == "<#{tagname} class=\"#{tagclass}\">" and @content[-1] == "</#{tagname}>" then if @content[-2] == "<#{tagname} class=\"#{tagclass}\">" and @content[-1] == "</#{tagname}>" then
@content.pop @content.pop
@content.pop @content.pop
end end
end end
end end
def self.is_newline(x) def self.is_newline(x)
(x == "\n") or (x == "\r") or (x == "\t") (x == "\n") or (x == "\r") or (x == "\t")
end end
def self.is_p_open_tag(x) def self.is_p_open_tag(x)
x =~ /\A<(p|li|ul|ol|dir|dt|dl)/ x =~ /\A<(p|li|ul|ol|dir|dt|dl)/
end end
def self.is_p_close_tag(x) def self.is_p_close_tag(x)
x =~ %r!\A</(p|li|ul|ol|dir|dt|dl)! x =~ %r!\A</(p|li|ul|ol|dir|dt|dl)!
end end
def self.diff(a, b) def self.diff(a, b)
a, b = a.split(//), b.split(//) if a.kind_of? String and b.kind_of? String a, b = a.split(//), b.split(//) if a.kind_of? String and b.kind_of? String
a, b = html2list(a), html2list(b) a, b = html2list(a), html2list(b)
out = Builder.new(a, b) out = Builder.new(a, b)
s = SequenceMatcher.new(a, b) s = SequenceMatcher.new(a, b)
s.get_opcodes.each do |opcode| s.get_opcodes.each do |opcode|
out.do_op(opcode) out.do_op(opcode)
end end
out.result out.result
end end
def self.html2list(x, b=false) def self.html2list(x, b=false)
mode = 'char' mode = 'char'
cur = '' cur = ''
out = [] out = []
x = x.split(//) if x.kind_of? String x = x.split(//) if x.kind_of? String
x.each do |c| x.each do |c|
if mode == 'tag' if mode == 'tag'
if c == '>' if c == '>'
if b if b
cur += ']' cur += ']'
else else
cur += c cur += c
end end
out.push(cur) out.push(cur)
cur = '' cur = ''
mode = 'char' mode = 'char'
else else
cur += c cur += c
end end
elsif mode == 'char' elsif mode == 'char'
if c == '<' if c == '<'
out.push cur out.push cur
if b if b
cur = '[' cur = '['
else else
cur = c cur = c
end end
mode = 'tag' mode = 'tag'
elsif /\s/.match c elsif /\s/.match c
out.push cur + c out.push cur + c
cur = '' cur = ''
else else
cur += c cur += c
end end
end end
end end
out.push cur out.push cur
# TODO: make something better here # TODO: make something better here
out.each{|x| x.chomp! unless is_newline(x)} out.each{|x| x.chomp! unless is_newline(x)}
out.find_all { |x| x != '' } out.find_all { |x| x != '' }
end end
end end
if __FILE__ == $0 if __FILE__ == $0
require 'pp' require 'pp'
# a = "<p>this is the original string</p>" # \n<p>but around the world</p>" # 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>" # 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>" 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>" b = "<ul>\n\t<li>one</li>\n\t<li>two\n\t<ul><li>abc</li></ul></li>\n</ul>"
puts a puts a
pp HTMLDiff.html2list(a) pp HTMLDiff.html2list(a)
puts puts
puts b puts b
pp HTMLDiff.html2list(b) pp HTMLDiff.html2list(b)
puts puts
puts HTMLDiff.diff(a, b) puts HTMLDiff.diff(a, b)
end end

View file

@ -1,15 +1,15 @@
# Model methods that want to rollback transactions gracefully # Model methods that want to rollback transactions gracefully
# (i.e, returning the user back to the form from which the request was posted) # (i.e, returning the user back to the form from which the request was posted)
# should raise Instiki::ValidationError. # should raise Instiki::ValidationError.
# #
# E.g. if a model object does # E.g. if a model object does
# raise "Foo: '#{foo}' is not equal to Bar: '#{bar}'" if (foo != bar) # 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 # 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 # where s/he was entering foo and bar, and the error message will be displayed
# on the page # on the page
module Instiki module Instiki
class ValidationError < StandardError class ValidationError < StandardError
end end
end end

302
libraries/rdocsupport.rb Executable file → Normal file
View file

@ -1,152 +1,152 @@
begin begin
require "rdoc/markup/simple_markup" require "rdoc/markup/simple_markup"
require 'rdoc/markup/simple_markup/to_html' require 'rdoc/markup/simple_markup/to_html'
rescue LoadError rescue LoadError
# use old version if available # use old version if available
require 'markup/simple_markup' require 'markup/simple_markup'
require 'markup/simple_markup/to_html' require 'markup/simple_markup/to_html'
end end
module RDocSupport module RDocSupport
# A simple +rdoc+ markup class which recognizes some additional # A simple +rdoc+ markup class which recognizes some additional
# formatting commands suitable for Wiki use. # formatting commands suitable for Wiki use.
class RDocMarkup < SM::SimpleMarkup class RDocMarkup < SM::SimpleMarkup
def initialize def initialize
super() super()
pre = '(?:\\s|^|\\\\)' pre = '(?:\\s|^|\\\\)'
# links of the form # links of the form
# [[<url> description with spaces]] # [[<url> description with spaces]]
add_special(/((\\)?\[\[\S+?\s+.+?\]\])/,:TIDYLINK) add_special(/((\\)?\[\[\S+?\s+.+?\]\])/,:TIDYLINK)
# and external references # and external references
add_special(/((\\)?(link:|anchor:|http:|mailto:|ftp:|img:|www\.)\S+\w\/?)/, add_special(/((\\)?(link:|anchor:|http:|mailto:|ftp:|img:|www\.)\S+\w\/?)/,
:HYPERLINK) :HYPERLINK)
# <br/> # <br/>
add_special(%r{(#{pre}<br/>)}, :BR) add_special(%r{(#{pre}<br/>)}, :BR)
# and <center> ... </center> # and <center> ... </center>
add_html("center", :CENTER) add_html("center", :CENTER)
end end
def convert(text, handler) def convert(text, handler)
super.sub(/^<p>\n/, '').sub(/<\/p>$/, '') super.sub(/^<p>\n/, '').sub(/<\/p>$/, '')
end end
end end
# Handle special hyperlinking requirments for RDoc formatted # Handle special hyperlinking requirments for RDoc formatted
# entries. Requires RDoc # entries. Requires RDoc
class HyperLinkHtml < SM::ToHtml class HyperLinkHtml < SM::ToHtml
# Initialize the HyperLinkHtml object. # Initialize the HyperLinkHtml object.
# [path] location of the node # [path] location of the node
# [site] object representing the whole site (typically of class # [site] object representing the whole site (typically of class
# +Site+) # +Site+)
def initialize def initialize
super() super()
add_tag(:CENTER, "<center>", "</center>") add_tag(:CENTER, "<center>", "</center>")
end end
# handle <br/> # handle <br/>
def handle_special_BR(special) def handle_special_BR(special)
return "&lt;br/&gt" if special.text[0,1] == '\\' return "&lt;br/&gt" if special.text[0,1] == '\\'
special.text special.text
end end
# We're invoked with a potential external hyperlink. # We're invoked with a potential external hyperlink.
# [mailto:] just gets inserted. # [mailto:] just gets inserted.
# [http:] links are checked to see if they # [http:] links are checked to see if they
# reference an image. If so, that image gets inserted # reference an image. If so, that image gets inserted
# using an <img> tag. Otherwise a conventional <a href> # using an <img> tag. Otherwise a conventional <a href>
# is used. # is used.
# [img:] insert a <tt><img></tt> tag # [img:] insert a <tt><img></tt> tag
# [link:] used to insert arbitrary <tt><a></tt> references # [link:] used to insert arbitrary <tt><a></tt> references
# [anchor:] used to create an anchor # [anchor:] used to create an anchor
def handle_special_HYPERLINK(special) def handle_special_HYPERLINK(special)
text = special.text.strip text = special.text.strip
return text[1..-1] if text[0,1] == '\\' return text[1..-1] if text[0,1] == '\\'
url = special.text.strip url = special.text.strip
if url =~ /([A-Za-z]+):(.*)/ if url =~ /([A-Za-z]+):(.*)/
type = $1 type = $1
path = $2 path = $2
else else
type = "http" type = "http"
path = url path = url
url = "http://#{url}" url = "http://#{url}"
end end
case type case type
when "http" when "http"
if url =~ /\.(gif|png|jpg|jpeg|bmp)$/ if url =~ /\.(gif|png|jpg|jpeg|bmp)$/
"<img src=\"#{url}\"/>" "<img src=\"#{url}\"/>"
else else
"<a href=\"#{url}\">#{url.sub(%r{^\w+:/*}, '')}</a>" "<a href=\"#{url}\">#{url.sub(%r{^\w+:/*}, '')}</a>"
end end
when "img" when "img"
"<img src=\"#{path}\"/>" "<img src=\"#{path}\"/>"
when "link" when "link"
"<a href=\"#{path}\">#{path}</a>" "<a href=\"#{path}\">#{path}</a>"
when "anchor" when "anchor"
"<a name=\"#{path}\"></a>" "<a name=\"#{path}\"></a>"
else else
"<a href=\"#{url}\">#{url.sub(%r{^\w+:/*}, '')}</a>" "<a href=\"#{url}\">#{url.sub(%r{^\w+:/*}, '')}</a>"
end end
end end
# Here's a hyperlink where the label is different to the URL # Here's a hyperlink where the label is different to the URL
# [[url label that may contain spaces]] # [[url label that may contain spaces]]
# #
def handle_special_TIDYLINK(special) def handle_special_TIDYLINK(special)
text = special.text.strip text = special.text.strip
return text[1..-1] if text[0,1] == '\\' return text[1..-1] if text[0,1] == '\\'
unless text =~ /\[\[(\S+?)\s+(.+?)\]\]/ unless text =~ /\[\[(\S+?)\s+(.+?)\]\]/
return text return text
end end
url = $1 url = $1
label = $2 label = $2
label = RDocFormatter.new(label).to_html label = RDocFormatter.new(label).to_html
label = label.split.select{|x| x =~ /\S/}. label = label.split.select{|x| x =~ /\S/}.
map{|x| x.chomp}.join(' ') map{|x| x.chomp}.join(' ')
case url case url
when /link:(\S+)/ when /link:(\S+)/
return %{<a href="#{$1}">#{label}</a>} return %{<a href="#{$1}">#{label}</a>}
when /img:(\S+)/ when /img:(\S+)/
return %{<img src="http://#{$1}" alt="#{label}" />} return %{<img src="http://#{$1}" alt="#{label}" />}
when /rubytalk:(\S+)/ when /rubytalk:(\S+)/
return %{<a href="http://ruby-talk.org/blade/#{$1}">#{label}</a>} return %{<a href="http://ruby-talk.org/blade/#{$1}">#{label}</a>}
when /rubygarden:(\S+)/ when /rubygarden:(\S+)/
return %{<a href="http://www.rubygarden.org/ruby?#{$1}">#{label}</a>} return %{<a href="http://www.rubygarden.org/ruby?#{$1}">#{label}</a>}
when /c2:(\S+)/ when /c2:(\S+)/
return %{<a href="http://c2.com/cgi/wiki?#{$1}">#{label}</a>} return %{<a href="http://c2.com/cgi/wiki?#{$1}">#{label}</a>}
when /isbn:(\S+)/ when /isbn:(\S+)/
return %{<a href="http://search.barnesandnoble.com/bookSearch/} + return %{<a href="http://search.barnesandnoble.com/bookSearch/} +
%{isbnInquiry.asp?isbn=#{$1}">#{label}</a>} %{isbnInquiry.asp?isbn=#{$1}">#{label}</a>}
end end
unless url =~ /\w+?:/ unless url =~ /\w+?:/
url = "http://#{url}" url = "http://#{url}"
end end
"<a href=\"#{url}\">#{label}</a>" "<a href=\"#{url}\">#{label}</a>"
end end
end end
class RDocFormatter class RDocFormatter
def initialize(text) def initialize(text)
@text = text @text = text
end end
def to_html def to_html
markup = RDocMarkup.new markup = RDocMarkup.new
h = HyperLinkHtml.new h = HyperLinkHtml.new
markup.convert(@text, h) markup.convert(@text, h)
end end
end end
end end

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
View file

@ -1,107 +1,107 @@
# Below are some hacks to Rails internal classes that implement Instiki URLs scheme. # 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. # 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 # When Rails implements some way to do it in the framework, this code should be replaced
# with something more legitimate. # with something more legitimate.
# In Instiki URLs are mapped to the ActionPack actions, possibly performed on a particular # In Instiki URLs are mapped to the ActionPack actions, possibly performed on a particular
# web (sub-wiki) and page within that web. # web (sub-wiki) and page within that web.
# #
# 1. Controller is determined by action name (default is 'wiki') # 1. Controller is determined by action name (default is 'wiki')
# 2. '/name1/' maps to action 'name1', unspecified web # 2. '/name1/' maps to action 'name1', unspecified web
# Example: http://localhost/new_system/ # Example: http://localhost/new_system/
# 3. Special case of above, URI '/wiki/' maps to action 'index', because Rails sets this address # 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 # when default controller name is specified as 'wiki', and an application root
# (http://localhost:2500/)is requested. # (http://localhost:2500/)is requested.
# 4. '/name1/name2/' maps to web 'name1', action 'name2' # 4. '/name1/name2/' maps to web 'name1', action 'name2'
# Example: http://localhost/mywiki/search/ # Example: http://localhost/mywiki/search/
# 5. '/name1/name2/name3/' maps to web 'name1', action 'name2', # 5. '/name1/name2/name3/' maps to web 'name1', action 'name2',
# Example: http://localhost/mywiki/show/HomePage # Example: http://localhost/mywiki/show/HomePage
require 'dispatcher' require 'dispatcher'
# Overrides Rails DispatchServlet.parse_uri # Overrides Rails DispatchServlet.parse_uri
class DispatchServlet class DispatchServlet
def self.parse_uri(path) def self.parse_uri(path)
result = parse_path(path) result = parse_path(path)
if result if result
result[:controller] = ActionMapper.map_to_controller(result[:action]) result[:controller] = ActionMapper.map_to_controller(result[:action])
result result
else else
false false
end end
end end
def self.parse_path(path) def self.parse_path(path)
ApplicationController.logger.debug "Parsing URI '#{path}'" ApplicationController.logger.debug "Parsing URI '#{path}'"
component = '([-_a-zA-Z0-9]+)' component = '([-_a-zA-Z0-9]+)'
page_name = '(.*)' page_name = '(.*)'
case path.sub(%r{^/(?:fcgi|mruby|cgi)/}, "/") case path.sub(%r{^/(?:fcgi|mruby|cgi)/}, "/")
when '/wiki/' when '/wiki/'
{ :web => nil, :controller => 'wiki', :action => 'index' } { :web => nil, :controller => 'wiki', :action => 'index' }
when %r{^/#{component}/?$} when %r{^/#{component}/?$}
{ :web => nil, :controller => 'wiki', :action => $1 } { :web => nil, :controller => 'wiki', :action => $1 }
when %r{^/#{component}/#{component}/?$} when %r{^/#{component}/#{component}/?$}
{ :web => $1, :controller => 'wiki', :action => $2 } { :web => $1, :controller => 'wiki', :action => $2 }
when %r{^/#{component}/#{component}/(.*)/?$} when %r{^/#{component}/#{component}/(.*)/?$}
{ :web => $1, :controller => 'wiki', :action => $2, :id => drop_trailing_slash($3) } { :web => $1, :controller => 'wiki', :action => $2, :id => drop_trailing_slash($3) }
else else
false false
end end
end end
def self.drop_trailing_slash(line) def self.drop_trailing_slash(line)
if line[-1] == ?/ if line[-1] == ?/
line.chop line.chop
else else
line line
end end
end end
class ActionMapper class ActionMapper
@@action_to_controller_map = { @@action_to_controller_map = {
'file' => 'file', 'file' => 'file',
'pic' => 'file' 'pic' => 'file'
} }
def self.map_to_controller(action) def self.map_to_controller(action)
@@action_to_controller_map[action] || 'wiki' @@action_to_controller_map[action] || 'wiki'
end end
end end
end end
require 'action_controller/url_rewriter.rb' require 'action_controller/url_rewriter.rb'
# Overrides parts of AP UrlRewriter to achieve the Instiki's legacy URL scheme # Overrides parts of AP UrlRewriter to achieve the Instiki's legacy URL scheme
module ActionController module ActionController
class UrlRewriter class UrlRewriter
VALID_OPTIONS << :web unless VALID_OPTIONS.include? :web VALID_OPTIONS << :web unless VALID_OPTIONS.include? :web
private private
def resolve_aliases(options) def resolve_aliases(options)
options[:controller_prefix] = options[:web] unless options[:web].nil? options[:controller_prefix] = options[:web] unless options[:web].nil?
options options
end end
def controller_name(options, controller_prefix) def controller_name(options, controller_prefix)
ensure_slash_suffix(options, :controller_prefix) ensure_slash_suffix(options, :controller_prefix)
controller_name = case options[:controller_prefix] controller_name = case options[:controller_prefix]
when String: options[:controller_prefix] when String: options[:controller_prefix]
when false : "" when false : ""
when nil : controller_prefix || "" when nil : controller_prefix || ""
end end
# In Instiki we don't need the controller name (there is only one comtroller, anyway) # In Instiki we don't need the controller name (there is only one comtroller, anyway)
# therefore the below line is commented out # therefore the below line is commented out
# controller_name << (options[:controller] + "/") if options[:controller] # controller_name << (options[:controller] + "/") if options[:controller]
return controller_name return controller_name
end end
end end
end end

36
natives/osx/desktop_launcher/AppDelegate.h Executable file → Normal file
View file

@ -1,18 +1,18 @@
/* AppDelegate */ /* AppDelegate */
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject @interface AppDelegate : NSObject
{ {
IBOutlet NSMenu* statusMenu; IBOutlet NSMenu* statusMenu;
NSTask* serverCommand; NSTask* serverCommand;
int processID; int processID;
BOOL shouldOpenUntitled; BOOL shouldOpenUntitled;
NSNetService* service; NSNetService* service;
} }
- (IBAction)about:(id)sender; - (IBAction)about:(id)sender;
- (IBAction)goToHomepage:(id)sender; - (IBAction)goToHomepage:(id)sender;
- (IBAction)goToInstikiOrg:(id)sender; - (IBAction)goToInstikiOrg:(id)sender;
- (IBAction)quit:(id)sender; - (IBAction)quit:(id)sender;
@end @end

218
natives/osx/desktop_launcher/AppDelegate.mm Executable file → Normal file
View file

@ -1,109 +1,109 @@
#include <unistd.h> #include <unistd.h>
#include <sys/wait.h> #include <sys/wait.h>
#import "AppDelegate.h" #import "AppDelegate.h"
int launch_ruby (char const* cmd) int launch_ruby (char const* cmd)
{ {
int pId, parentID = getpid(); int pId, parentID = getpid();
if((pId = fork()) == 0) // child if((pId = fork()) == 0) // child
{ {
NSLog(@"set child (%d) to pgrp %d", getpid(), parentID); NSLog(@"set child (%d) to pgrp %d", getpid(), parentID);
setpgrp(0, parentID); setpgrp(0, parentID);
system(cmd); system(cmd);
return 0; return 0;
} }
else // parent else // parent
{ {
NSLog(@"started child process: %d", pId); NSLog(@"started child process: %d", pId);
return pId; return pId;
} }
} }
@implementation AppDelegate @implementation AppDelegate
- (NSString*)storageDirectory - (NSString*)storageDirectory
{ {
NSString* dir = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/Instiki"]; NSString* dir = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/Instiki"];
[[NSFileManager defaultManager] createDirectoryAtPath:dir attributes:nil]; [[NSFileManager defaultManager] createDirectoryAtPath:dir attributes:nil];
return dir; return dir;
} }
- (void)awakeFromNib - (void)awakeFromNib
{ {
setpgrp(0, getpid()); setpgrp(0, getpid());
if([[[[NSBundle mainBundle] infoDictionary] objectForKey:@"LSUIElement"] isEqualToString:@"1"]) if([[[[NSBundle mainBundle] infoDictionary] objectForKey:@"LSUIElement"] isEqualToString:@"1"])
{ {
NSStatusBar* bar = [NSStatusBar systemStatusBar]; NSStatusBar* bar = [NSStatusBar systemStatusBar];
NSStatusItem* item = [[bar statusItemWithLength:NSVariableStatusItemLength] retain]; NSStatusItem* item = [[bar statusItemWithLength:NSVariableStatusItemLength] retain];
[item setTitle:@"Wiki"]; [item setTitle:@"Wiki"];
[item setHighlightMode:YES]; [item setHighlightMode:YES];
[item setMenu:statusMenu]; [item setMenu:statusMenu];
} }
NSBundle* bundle = [NSBundle bundleForClass:[self class]]; NSBundle* bundle = [NSBundle bundleForClass:[self class]];
NSString* ruby = [bundle pathForResource:@"ruby" ofType:nil]; NSString* ruby = [bundle pathForResource:@"ruby" ofType:nil];
NSString* script = [[bundle resourcePath] stringByAppendingPathComponent:@"rb_src/instiki.rb"]; NSString* script = [[bundle resourcePath] stringByAppendingPathComponent:@"rb_src/instiki.rb"];
if(ruby && script) if(ruby && script)
{ {
NSString* cmd = [NSString stringWithFormat: NSString* cmd = [NSString stringWithFormat:
@"%@ -I '%@' -I '%@' '%@' -s --storage='%@'", @"%@ -I '%@' -I '%@' '%@' -s --storage='%@'",
ruby, ruby,
[[bundle resourcePath] stringByAppendingPathComponent:@"lib/ruby/1.8"], [[bundle resourcePath] stringByAppendingPathComponent:@"lib/ruby/1.8"],
[[bundle resourcePath] stringByAppendingPathComponent:@"lib/ruby/1.8/powerpc-darwin"], [[bundle resourcePath] stringByAppendingPathComponent:@"lib/ruby/1.8/powerpc-darwin"],
script, script,
[self storageDirectory] [self storageDirectory]
]; ];
NSLog(@"starting %@", cmd); NSLog(@"starting %@", cmd);
processID = launch_ruby([cmd UTF8String]); processID = launch_ruby([cmd UTF8String]);
} }
/* public the service using rendezvous */ /* public the service using rendezvous */
service = [[NSNetService alloc] service = [[NSNetService alloc]
initWithDomain:@"" // default domain initWithDomain:@"" // default domain
type:@"_http._tcp." type:@"_http._tcp."
name:[NSString stringWithFormat:@"%@'s Instiki", NSFullUserName()] name:[NSString stringWithFormat:@"%@'s Instiki", NSFullUserName()]
port:2500]; port:2500];
[service publish]; [service publish];
} }
- (void)applicationWillTerminate:(NSNotification*)aNotification - (void)applicationWillTerminate:(NSNotification*)aNotification
{ {
[service stop]; [service stop];
[service release]; [service release];
kill(0, SIGTERM); kill(0, SIGTERM);
} }
- (IBAction)about:(id)sender - (IBAction)about:(id)sender
{ {
[NSApp activateIgnoringOtherApps:YES]; [NSApp activateIgnoringOtherApps:YES];
[NSApp orderFrontStandardAboutPanel:self]; [NSApp orderFrontStandardAboutPanel:self];
} }
- (IBAction)goToHomepage:(id)sender - (IBAction)goToHomepage:(id)sender
{ {
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://localhost:2500/"]]; [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://localhost:2500/"]];
} }
- (IBAction)goToInstikiOrg:(id)sender - (IBAction)goToInstikiOrg:(id)sender
{ {
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.instiki.org/"]]; [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.instiki.org/"]];
} }
- (BOOL)applicationShouldOpenUntitledFile:(NSApplication*)sender - (BOOL)applicationShouldOpenUntitledFile:(NSApplication*)sender
{ {
return shouldOpenUntitled ?: (shouldOpenUntitled = YES, NO); return shouldOpenUntitled ?: (shouldOpenUntitled = YES, NO);
} }
- (BOOL)applicationOpenUntitledFile:(NSApplication*)theApplication - (BOOL)applicationOpenUntitledFile:(NSApplication*)theApplication
{ {
return [self goToHomepage:self], YES; return [self goToHomepage:self], YES;
} }
- (IBAction)quit:(id)sender - (IBAction)quit:(id)sender
{ {
[NSApp terminate:self]; [NSApp terminate:self];
} }
@end @end

30
natives/osx/desktop_launcher/Credits.html Executable file → Normal file
View file

@ -1,16 +1,16 @@
<dl> <dl>
<dt>Engineering:</dt> <dt>Engineering:</dt>
<dd>Some people</dd> <dd>Some people</dd>
<dt>Human Interface Design:</dt> <dt>Human Interface Design:</dt>
<dd>Some other people</dd> <dd>Some other people</dd>
<dt>Testing:</dt> <dt>Testing:</dt>
<dd>Hopefully not nobody</dd> <dd>Hopefully not nobody</dd>
<dt>Documentation:</dt> <dt>Documentation:</dt>
<dd>Whoever</dd> <dd>Whoever</dd>
<dt>With special thanks to:</dt> <dt>With special thanks to:</dt>
<dd>Mom</dd> <dd>Mom</dd>
</dl> </dl>

View file

24
natives/osx/desktop_launcher/English.lproj/MainMenu.nib/classes.nib generated Executable file → Normal file
View file

@ -1,13 +1,13 @@
{ {
IBClasses = ( IBClasses = (
{ {
ACTIONS = {about = id; goToHomepage = id; goToInstikiOrg = id; quit = id; }; ACTIONS = {about = id; goToHomepage = id; goToInstikiOrg = id; quit = id; };
CLASS = AppDelegate; CLASS = AppDelegate;
LANGUAGE = ObjC; LANGUAGE = ObjC;
OUTLETS = {statusMenu = NSMenu; }; OUTLETS = {statusMenu = NSMenu; };
SUPERCLASS = NSObject; SUPERCLASS = NSObject;
}, },
{CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; } {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }
); );
IBVersion = 1; IBVersion = 1;
} }

48
natives/osx/desktop_launcher/English.lproj/MainMenu.nib/info.nib generated Executable file → Normal file
View file

@ -1,24 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>IBDocumentLocation</key> <key>IBDocumentLocation</key>
<string>109 6 356 240 0 0 1440 878 </string> <string>109 6 356 240 0 0 1440 878 </string>
<key>IBEditorPositions</key> <key>IBEditorPositions</key>
<dict> <dict>
<key>206</key> <key>206</key>
<string>112 300 116 87 0 0 1440 878 </string> <string>112 300 116 87 0 0 1440 878 </string>
<key>29</key> <key>29</key>
<string>241 316 70 44 0 0 1440 878 </string> <string>241 316 70 44 0 0 1440 878 </string>
</dict> </dict>
<key>IBFramework Version</key> <key>IBFramework Version</key>
<string>349.0</string> <string>349.0</string>
<key>IBOpenObjects</key> <key>IBOpenObjects</key>
<array> <array>
<integer>206</integer> <integer>206</integer>
<integer>29</integer> <integer>29</integer>
</array> </array>
<key>IBSystem Version</key> <key>IBSystem Version</key>
<string>7H63</string> <string>7H63</string>
</dict> </dict>
</plist> </plist>

0
natives/osx/desktop_launcher/English.lproj/MainMenu.nib/objects.nib generated Executable file → Normal file
View file

24
natives/osx/desktop_launcher/Info.plist Executable file → Normal file
View file

@ -1,13 +1,13 @@
{ {
CFBundleDevelopmentRegion = English; CFBundleDevelopmentRegion = English;
CFBundleExecutable = Instiki; CFBundleExecutable = Instiki;
CFBundleIconFile = ""; CFBundleIconFile = "";
CFBundleIdentifier = "com.nextangle.instiki"; CFBundleIdentifier = "com.nextangle.instiki";
CFBundleInfoDictionaryVersion = "6.0"; CFBundleInfoDictionaryVersion = "6.0";
CFBundlePackageType = APPL; CFBundlePackageType = APPL;
CFBundleSignature = WIKI; CFBundleSignature = WIKI;
CFBundleVersion = "0.9.0"; CFBundleVersion = "0.9.0";
LSUIElement = 1; LSUIElement = 1;
NSMainNibFile = MainMenu; NSMainNibFile = MainMenu;
NSPrincipalClass = NSApplication; NSPrincipalClass = NSApplication;
} }

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
View file

@ -1,7 +1,7 @@
// //
// Prefix header for all source files of the 'Instiki' target in the 'Instiki' project // Prefix header for all source files of the 'Instiki' target in the 'Instiki' project
// //
#ifdef __OBJC__ #ifdef __OBJC__
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#endif #endif

18
natives/osx/desktop_launcher/MakeDMG.sh Executable file → Normal file
View file

@ -1,9 +1,9 @@
#!/bin/sh #!/bin/sh
hdiutil create -size 12m -fs HFS+ -volname Instiki -ov /tmp/Instiki_12MB.dmg hdiutil create -size 12m -fs HFS+ -volname Instiki -ov /tmp/Instiki_12MB.dmg
hdiutil mount /tmp/Instiki_12MB.dmg hdiutil mount /tmp/Instiki_12MB.dmg
# strip ~/ruby/instiki/natives/osx/build/Instiki.app/Contents/MacOS/Instiki # 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 ditto ~/ruby/instiki/natives/osx/desktop_launcher/build/Instiki.app /Volumes/Instiki/Instiki.app
hdiutil unmount /Volumes/Instiki hdiutil unmount /Volumes/Instiki
hdiutil convert -format UDZO -o /tmp/Instiki.dmg /tmp/Instiki_12MB.dmg hdiutil convert -format UDZO -o /tmp/Instiki.dmg /tmp/Instiki_12MB.dmg
hdiutil internet-enable -yes /tmp/Instiki.dmg hdiutil internet-enable -yes /tmp/Instiki.dmg

28
natives/osx/desktop_launcher/main.mm Executable file → Normal file
View file

@ -1,14 +1,14 @@
// //
// main.mm // main.mm
// Instiki // Instiki
// //
// Created by Allan Odgaard on Thu May 20 2004. // Created by Allan Odgaard on Thu May 20 2004.
// Copyright (c) 2004 MacroMates. All rights reserved. // Copyright (c) 2004 MacroMates. All rights reserved.
// //
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
int main (int argc, char const* argv[]) int main (int argc, char const* argv[])
{ {
return NSApplicationMain(argc, argv); return NSApplicationMain(argc, argv);
} }

32
natives/osx/desktop_launcher/version.plist Executable file → Normal file
View file

@ -1,16 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>BuildVersion</key> <key>BuildVersion</key>
<string>17</string> <string>17</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>0.1</string> <string>0.1</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>0.1</string> <string>0.1</string>
<key>ProjectName</key> <key>ProjectName</key>
<string>NibPBTemplates</string> <string>NibPBTemplates</string>
<key>SourceVersion</key> <key>SourceVersion</key>
<string>1150000</string> <string>1150000</string>
</dict> </dict>
</plist> </plist>

0
public/images/.images_go_here Executable file → Normal file
View file

View file

@ -1,48 +1,48 @@
function proposeAddress() { function proposeAddress() {
document.getElementById('address').value = document.getElementById('address').value =
document.getElementById('name').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase(); document.getElementById('name').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
} }
function cleanAddress() { function cleanAddress() {
document.getElementById('address').value = document.getElementById('address').value =
document.getElementById('address').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase(); document.getElementById('address').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
} }
function validateSetup() { function validateSetup() {
if (document.getElementById('system_password').value == "") { if (document.getElementById('system_password').value == "") {
alert("You must enter the system password"); alert("You must enter the system password");
return false; return false;
} }
if (document.getElementById('name').value == "") { if (document.getElementById('name').value == "") {
alert("You must pick a name for the web"); alert("You must pick a name for the web");
return false; return false;
} }
if (document.getElementById('address').value == "") { if (document.getElementById('address').value == "") {
alert("You must pick an address for the web"); alert("You must pick an address for the web");
return false; return false;
} }
if (document.getElementById('password').value != "" && if (document.getElementById('password').value != "" &&
document.getElementById('password').value != document.getElementById('password_check').value) { document.getElementById('password').value != document.getElementById('password_check').value) {
alert("The password and its verification doesn't match"); alert("The password and its verification doesn't match");
return false; return false;
} }
return true; return true;
} }
// overriding auto-complete by form managers // overriding auto-complete by form managers
// code by Chris Holland, lifted from // code by Chris Holland, lifted from
// http://chrisholland.blogspot.com/2004/11/banks-protect-privacy-disable.html // http://chrisholland.blogspot.com/2004/11/banks-protect-privacy-disable.html
function overrideAutocomplete() { function overrideAutocomplete() {
if (document.getElementsByTagName) { if (document.getElementsByTagName) {
var inputElements = document.getElementsByTagName("input"); var inputElements = document.getElementsByTagName("input");
for (i=0; inputElements[i]; i++) { for (i=0; inputElements[i]; i++) {
if (inputElements[i].className && (inputElements[i].className.indexOf("disableAutoComplete") != -1)) { if (inputElements[i].className && (inputElements[i].className.indexOf("disableAutoComplete") != -1)) {
inputElements[i].setAttribute("autocomplete","off"); inputElements[i].setAttribute("autocomplete","off");
}//if current input element has the disableAutoComplete class set. }//if current input element has the disableAutoComplete class set.
}//loop thru input elements }//loop thru input elements
} }
} }

442
public/stylesheets/instiki.css Executable file → Normal file
View file

@ -1,222 +1,222 @@
#Container { #Container {
float: none; float: none;
margin: 0 auto; margin: 0 auto;
text-align: center; text-align: center;
} }
#Content { #Content {
margin: 0; margin: 0;
padding: 5px; padding: 5px;
text-align: left; text-align: left;
border-top: none; border-top: none;
float: left; float: left;
} }
body { background-color: #fff; color: #333; } body { background-color: #fff; color: #333; }
body, p, ol, ul, td { body, p, ol, ul, td {
font-family: verdana, arial, helvetica, sans-serif; font-family: verdana, arial, helvetica, sans-serif;
font-size: 13px; font-size: 13px;
line-height: 18px; line-height: 18px;
} }
a { color: #000; } a { color: #000; }
.newWikiWord { background-color: #eee; } .newWikiWord { background-color: #eee; }
.newWikiWord a:hover { background-color: white; } .newWikiWord a:hover { background-color: white; }
a:visited { color: #666; } a:visited { color: #666; }
a:hover { color: #fff; background-color:#000; } a:hover { color: #fff; background-color:#000; }
h1, h2, h3 { color: #333; font-family: georgia, verdana; } h1, h2, h3 { color: #333; font-family: georgia, verdana; }
h1 { font-size: 28px } h1 { font-size: 28px }
h2 { font-size: 19px } h2 { font-size: 19px }
h3 { font-size: 16px } h3 { font-size: 16px }
h1#pageName { h1#pageName {
margin: 5px 0px 0px 0px; margin: 5px 0px 0px 0px;
padding: 0px 0px 0px 0px; padding: 0px 0px 0px 0px;
line-height: 28px; line-height: 28px;
} }
h1#pageName small { h1#pageName small {
color: grey; color: grey;
line-height: 10px; line-height: 10px;
font-size: 10px; font-size: 10px;
padding: 0px; padding: 0px;
} }
a.nav, a.nav:link, a.nav:visited { color: #000; } a.nav, a.nav:link, a.nav:visited { color: #000; }
a.nav:hover { color: #fff; background-color:#000; } a.nav:hover { color: #fff; background-color:#000; }
li { margin-bottom: 7px } li { margin-bottom: 7px }
.navigation { .navigation {
margin-top: 5px; margin-top: 5px;
font-size : 12px; font-size : 12px;
color: #999; color: #999;
} }
.navigation a:hover { color: #fff; background-color:#000; } .navigation a:hover { color: #fff; background-color:#000; }
.navigation a { .navigation a {
font-size: 11px; font-size: 11px;
color: black; color: black;
font-weight: bold; font-weight: bold;
} }
.navigation small a { .navigation small a {
font-weight: normal; font-weight: normal;
font-size: 11px; font-size: 11px;
} }
.navOn{ .navOn{
font-size: 11px; font-size: 11px;
color: grey; color: grey;
font-weight: bold; font-weight: bold;
text-decoration: none; text-decoration: none;
} }
.help { .help {
font-family: verdana, arial, helvetica, sans-serif; font-family: verdana, arial, helvetica, sans-serif;
font-size: 11px; font-size: 11px;
} }
.inputBox { .inputBox {
font-family: verdana, arial, helvetica, sans-serif; font-family: verdana, arial, helvetica, sans-serif;
font-size: 11px; font-size: 11px;
background-color: #eee; background-color: #eee;
padding: 5px; padding: 5px;
margin-bottom: 20px; margin-bottom: 20px;
} }
blockquote { blockquote {
display: block; display: block;
margin: 0px 0px 20px 0px; margin: 0px 0px 20px 0px;
padding: 0px 30px; padding: 0px 30px;
font-size:11px; font-size:11px;
line-height:17px; line-height:17px;
font-style: italic; font-style: italic;
} }
pre { pre {
background-color: #eee; background-color: #eee;
padding: 10px; padding: 10px;
font-size: 11px; font-size: 11px;
} }
ol.setup { ol.setup {
font-size: 19px; font-size: 19px;
font-family: georgia, verdana; font-family: georgia, verdana;
padding-left: 25px; padding-left: 25px;
} }
ol.setup li { ol.setup li {
margin-bottom: 20px margin-bottom: 20px
} }
.byline { .byline {
font-size: 10px; font-size: 10px;
font-style: italic; font-style: italic;
margin-bottom: 10px; margin-bottom: 10px;
color: #999; color: #999;
} }
.references { .references {
font-size: 10px; font-size: 10px;
} }
.diffdel, del.diffmod { .diffdel, del.diffmod {
background: pink; background: pink;
} }
.diffins, ins.diffmod { .diffins, ins.diffmod {
background: lightgreen; background: lightgreen;
} }
#footer { #footer {
height: 14px; height: 14px;
padding: .25em 0; padding: .25em 0;
} }
#footer p { #footer p {
font-size: 10px; font-size: 10px;
color: gray; color: gray;
font-style: italic; font-style: italic;
margin: 0; margin: 0;
float: right; float: right;
text-align: right; text-align: right;
} }
#error { #error {
color: darkred; color: darkred;
font-style: italic; font-style: italic;
width: 450px; width: 450px;
} }
#info { #info {
color: darkgreen; color: darkgreen;
font-style: italic; font-style: italic;
width: 450px; width: 450px;
} }
#TextileHelp table { #TextileHelp table {
margin-bottom: 0; margin-bottom: 0;
} }
#TextileHelp table+h3 { #TextileHelp table+h3 {
margin-top: 11px; margin-top: 11px;
} }
#TextileHelp table td { #TextileHelp table td {
font-size: 11px; font-size: 11px;
padding: 3px; padding: 3px;
vertical-align: top; vertical-align: top;
border-top: 1px dotted #ccc; border-top: 1px dotted #ccc;
} }
#TextileHelp table td.arrow { #TextileHelp table td.arrow {
padding-right: 5px; padding-right: 5px;
padding-left: 10px; padding-left: 10px;
color: #999; color: #999;
} }
#TextileHelp table td.label { #TextileHelp table td.label {
font-weight: bold; font-weight: bold;
white-space: nowrap; white-space: nowrap;
font-size: 10px; font-size: 10px;
padding-right: 15px; padding-right: 15px;
color: #000; color: #000;
} }
#TextileHelp h3 { #TextileHelp h3 {
font-size: 11px; font-size: 11px;
font-weight: bold; font-weight: bold;
font-weight: normal; font-weight: normal;
margin: 0 0 5px 0; margin: 0 0 5px 0;
padding: 5px 0 0 0; padding: 5px 0 0 0;
} }
#TextileHelp p { #TextileHelp p {
font-size: 10px; font-size: 10px;
} }
.rightHandSide { .rightHandSide {
float: right; float: right;
width: 147px; width: 147px;
margin-left: 10px; margin-left: 10px;
padding-left: 20px; padding-left: 20px;
border-left: 1px dotted #ccc; border-left: 1px dotted #ccc;
} }
.rightHandSide p { .rightHandSide p {
font-size: 10px; font-size: 10px;
} }
.newsList { .newsList {
margin-top: 20px; margin-top: 20px;
} }
.newsList p { .newsList p {
margin-bottom:30px margin-bottom:30px
} }

View file

@ -1,83 +1,83 @@
#!/usr/bin/ruby #!/usr/bin/ruby
require 'webrick' require 'webrick'
require 'optparse' require 'optparse'
require 'fileutils' require 'fileutils'
pwd = File.expand_path(File.dirname(__FILE__) + "/..") pwd = File.expand_path(File.dirname(__FILE__) + "/..")
OPTIONS = { OPTIONS = {
# Overridable options # Overridable options
:port => 2500, :port => 2500,
:ip => '127.0.0.1', :ip => '127.0.0.1',
:environment => 'production', :environment => 'production',
:server_root => File.expand_path(File.dirname(__FILE__) + '/../public/'), :server_root => File.expand_path(File.dirname(__FILE__) + '/../public/'),
:server_type => WEBrick::SimpleServer, :server_type => WEBrick::SimpleServer,
:storage => "#{File.expand_path(FileUtils.pwd)}/storage", :storage => "#{File.expand_path(FileUtils.pwd)}/storage",
} }
ARGV.options do |opts| ARGV.options do |opts|
script_name = File.basename($0) script_name = File.basename($0)
opts.banner = "Usage: ruby #{script_name} [options]" opts.banner = "Usage: ruby #{script_name} [options]"
opts.separator '' opts.separator ''
opts.on('-p', '--port=port', Integer, opts.on('-p', '--port=port', Integer,
'Runs Instiki on the specified port.', 'Runs Instiki on the specified port.',
'Default: 2500') { |OPTIONS[:port]| } 'Default: 2500') { |OPTIONS[:port]| }
opts.on('-b', '--binding=ip', String, opts.on('-b', '--binding=ip', String,
'Binds Rails to the specified ip.', 'Binds Rails to the specified ip.',
'Default: 127.0.0.1') { |OPTIONS[:ip]| } 'Default: 127.0.0.1') { |OPTIONS[:ip]| }
opts.on('-i', '--index=controller', String, opts.on('-i', '--index=controller', String,
'Specifies an index controller that requests for root will go to (instead of congratulations screen).' 'Specifies an index controller that requests for root will go to (instead of congratulations screen).'
) { |OPTIONS[:index_controller]| } ) { |OPTIONS[:index_controller]| }
opts.on('-e', '--environment=name', String, opts.on('-e', '--environment=name', String,
'Specifies the environment to run this server under (test/development/production).', 'Specifies the environment to run this server under (test/development/production).',
'Default: production') { |OPTIONS[:environment]| } 'Default: production') { |OPTIONS[:environment]| }
opts.on('-d', '--daemon', opts.on('-d', '--daemon',
'Make Rails run as a Daemon (only works if fork is available -- meaning on *nix).' 'Make Rails run as a Daemon (only works if fork is available -- meaning on *nix).'
) { OPTIONS[:server_type] = WEBrick::Daemon } ) { OPTIONS[:server_type] = WEBrick::Daemon }
opts.on('-s', '--simple', '--simple-server', opts.on('-s', '--simple', '--simple-server',
'[deprecated] Forces Instiki not to run as a Daemon if fork is available.', '[deprecated] Forces Instiki not to run as a Daemon if fork is available.',
'Since version 0.10.0 this option is ignored.' 'Since version 0.10.0 this option is ignored.'
) { puts "Warning: -s (--simple) option is deprecated. See instiki --help for details." } ) { puts "Warning: -s (--simple) option is deprecated. See instiki --help for details." }
opts.on('-t', '--storage=storage', String, opts.on('-t', '--storage=storage', String,
'Makes Instiki use the specified directory for storage.', 'Makes Instiki use the specified directory for storage.',
'Default: ./storage/[port]') { |OPTIONS[:storage]| } 'Default: ./storage/[port]') { |OPTIONS[:storage]| }
opts.on('-v', '--verbose', opts.on('-v', '--verbose',
'Enable debug-level logging' 'Enable debug-level logging'
) { OPTIONS[:verbose] = true } ) { OPTIONS[:verbose] = true }
opts.separator '' opts.separator ''
opts.on('-h', '--help', opts.on('-h', '--help',
'Show this help message.') { puts opts; exit } 'Show this help message.') { puts opts; exit }
opts.parse! opts.parse!
end end
ENV['RAILS_ENV'] = OPTIONS[:environment] ENV['RAILS_ENV'] = OPTIONS[:environment]
require File.expand_path(File.dirname(__FILE__) + '/../config/environment') require File.expand_path(File.dirname(__FILE__) + '/../config/environment')
if OPTIONS[:verbose] if OPTIONS[:verbose]
ActionController::Base.logger.level = Logger::DEBUG ActionController::Base.logger.level = Logger::DEBUG
end end
OPTIONS[:index_controller] = 'wiki' OPTIONS[:index_controller] = 'wiki'
require 'webrick_server' require 'webrick_server'
if OPTIONS[:environment] == 'production' if OPTIONS[:environment] == 'production'
storage_path = OPTIONS[:storage] + "/" + OPTIONS[:port].to_s storage_path = OPTIONS[:storage] + "/" + OPTIONS[:port].to_s
else else
storage_path = OPTIONS[:storage] + "/" + OPTIONS[:environment] + "/" + OPTIONS[:port].to_s storage_path = OPTIONS[:storage] + "/" + OPTIONS[:environment] + "/" + OPTIONS[:port].to_s
end end
FileUtils.mkdir_p(storage_path) FileUtils.mkdir_p(storage_path)
puts "=> Starting Instiki on http://#{OPTIONS[:ip]}:#{OPTIONS[:port]}" puts "=> Starting Instiki on http://#{OPTIONS[:ip]}:#{OPTIONS[:port]}"
puts "=> Data files are stored in #{storage_path}" puts "=> Data files are stored in #{storage_path}"
require 'application' require 'application'
WikiService.storage_path = storage_path WikiService.storage_path = storage_path
ApplicationController.wiki = WikiService.instance ApplicationController.wiki = WikiService.instance
DispatchServlet.dispatch(OPTIONS) DispatchServlet.dispatch(OPTIONS)

18
test/all_tests.rb Normal file → Executable file
View file

@ -1,9 +1,9 @@
require 'test_helper' require 'test_helper'
require 'find' require 'find'
test_root = File.dirname(__FILE__) test_root = File.dirname(__FILE__)
Find.find(test_root) { |path| Find.find(test_root) { |path|
if File.file?(path) and path =~ /.*_test\.rb$/ if File.file?(path) and path =~ /.*_test\.rb$/
require path[(test_root.size + 1)..-4] require path[(test_root.size + 1)..-4]
end end
} }

0
test/fixtures/rails.gif vendored Normal file → Executable file
View 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
View file

@ -1,26 +1,26 @@
# Unit tests for ApplicationController (the abstract controller class) # Unit tests for ApplicationController (the abstract controller class)
require File.dirname(__FILE__) + '/../test_helper' require File.dirname(__FILE__) + '/../test_helper'
require 'wiki_controller' require 'wiki_controller'
require 'rexml/document' require 'rexml/document'
# Need some concrete class to test the abstract class features # Need some concrete class to test the abstract class features
class WikiController; def rescue_action(e) logger.error(e); raise e end; end class WikiController; def rescue_action(e) logger.error(e); raise e end; end
class ApplicationTest < Test::Unit::TestCase class ApplicationTest < Test::Unit::TestCase
def setup def setup
setup_test_wiki setup_test_wiki
setup_controller_test(WikiController) setup_controller_test(WikiController)
end end
def tear_down def tear_down
tear_down_wiki tear_down_wiki
end end
def test_utf8_header def test_utf8_header
r = process('show', 'web' => 'wiki1', 'id' => 'HomePage') r = process('show', 'web' => 'wiki1', 'id' => 'HomePage')
assert_equal 'text/html; charset=UTF-8', r.headers['Content-Type'] assert_equal 'text/html; charset=UTF-8', r.headers['Content-Type']
end end
end end

254
test/functional/file_controller_test.rb Normal file → Executable file
View file

@ -1,127 +1,127 @@
#!/bin/env ruby -w #!/bin/env ruby -w
require File.dirname(__FILE__) + '/../test_helper' require File.dirname(__FILE__) + '/../test_helper'
require 'file_controller' require 'file_controller'
require 'fileutils' require 'fileutils'
# Raise errors beyond the default web-based presentation # Raise errors beyond the default web-based presentation
class FileController; def rescue_action(e) logger.error(e); raise e end; end class FileController; def rescue_action(e) logger.error(e); raise e end; end
class FileControllerTest < Test::Unit::TestCase class FileControllerTest < Test::Unit::TestCase
FILE_AREA = RAILS_ROOT + '/storage/test/wiki1' FILE_AREA = RAILS_ROOT + '/storage/test/wiki1'
FileUtils.mkdir_p(FILE_AREA) unless File.directory?(FILE_AREA) FileUtils.mkdir_p(FILE_AREA) unless File.directory?(FILE_AREA)
FileUtils.rm(Dir["#{FILE_AREA}/*"]) FileUtils.rm(Dir["#{FILE_AREA}/*"])
def setup def setup
setup_test_wiki setup_test_wiki
setup_controller_test setup_controller_test
end end
def tear_down def tear_down
tear_down_wiki tear_down_wiki
end end
def test_file def test_file
process 'file', 'web' => 'wiki1', 'id' => 'foo.tgz' process 'file', 'web' => 'wiki1', 'id' => 'foo.tgz'
assert_success assert_success
assert_rendered_file 'file/file' assert_rendered_file 'file/file'
end end
def test_file_download_text_file def test_file_download_text_file
File.open("#{FILE_AREA}/foo.txt", 'wb') { |f| f.write "aaa\nbbb\n" } File.open("#{FILE_AREA}/foo.txt", 'wb') { |f| f.write "aaa\nbbb\n" }
r = process 'file', 'web' => 'wiki1', 'id' => 'foo.txt' r = process 'file', 'web' => 'wiki1', 'id' => 'foo.txt'
assert_success assert_success
assert_equal "aaa\nbbb\n", r.binary_content assert_equal "aaa\nbbb\n", r.binary_content
assert_equal 'text/plain', r.headers['Content-Type'] assert_equal 'text/plain', r.headers['Content-Type']
end end
def test_file_download_pdf_file def test_file_download_pdf_file
File.open("#{FILE_AREA}/foo.pdf", 'wb') { |f| f.write "aaa\nbbb\n" } File.open("#{FILE_AREA}/foo.pdf", 'wb') { |f| f.write "aaa\nbbb\n" }
r = process 'file', 'web' => 'wiki1', 'id' => 'foo.pdf' r = process 'file', 'web' => 'wiki1', 'id' => 'foo.pdf'
assert_success assert_success
assert_equal "aaa\nbbb\n", r.binary_content assert_equal "aaa\nbbb\n", r.binary_content
assert_equal 'application/pdf', r.headers['Content-Type'] assert_equal 'application/pdf', r.headers['Content-Type']
end end
def test_pic_download_gif def test_pic_download_gif
FileUtils.cp("#{RAILS_ROOT}/test/fixtures/rails.gif", FILE_AREA) FileUtils.cp("#{RAILS_ROOT}/test/fixtures/rails.gif", FILE_AREA)
r = process 'pic', 'web' => 'wiki1', 'id' => 'rails.gif' r = process 'pic', 'web' => 'wiki1', 'id' => 'rails.gif'
assert_success assert_success
assert_equal File.size("#{FILE_AREA}/rails.gif"), r.binary_content.size assert_equal File.size("#{FILE_AREA}/rails.gif"), r.binary_content.size
end end
def test_pic_unknown_pic def test_pic_unknown_pic
r = process 'pic', 'web' => 'wiki1', 'id' => 'non-existant.gif' r = process 'pic', 'web' => 'wiki1', 'id' => 'non-existant.gif'
assert_success assert_success
assert_rendered_file 'file/file' assert_rendered_file 'file/file'
end end
def test_pic_upload_end_to_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' # 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') @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\">" + assert_equal "<p><span class=\"newWikiWord\">rails-e2e.gif<a href=\"../pic/rails-e2e.gif\">" +
"?</a></span></p>", "?</a></span></p>",
@home.display_content @home.display_content
# rails-e2e.gif is unknown to the system, so pic action goes to the file [upload] form # 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' r = process 'pic', 'web' => 'wiki1', 'id' => 'rails-e2e.gif'
assert_success assert_success
assert_rendered_file 'file/file' assert_rendered_file 'file/file'
# User uploads the picture # User uploads the picture
picture = File.read("#{RAILS_ROOT}/test/fixtures/rails.gif") picture = File.read("#{RAILS_ROOT}/test/fixtures/rails.gif")
r = process 'pic', 'web' => 'wiki1', 'id' => 'rails-e2e.gif', 'file' => StringIO.new(picture) r = process 'pic', 'web' => 'wiki1', 'id' => 'rails-e2e.gif', 'file' => StringIO.new(picture)
assert_redirect_url '/' assert_redirect_url '/'
assert @wiki.file_yard(@web).has_file?('rails-e2e.gif') assert @wiki.file_yard(@web).has_file?('rails-e2e.gif')
assert_equal(picture, File.read("#{RAILS_ROOT}/storage/test/wiki1/rails-e2e.gif")) assert_equal(picture, File.read("#{RAILS_ROOT}/storage/test/wiki1/rails-e2e.gif"))
# this should refresh the page display content (cached) # this should refresh the page display content (cached)
assert_equal "<p><img alt=\"rails-e2e.gif\" src=\"../pic/rails-e2e.gif\" /></p>", assert_equal "<p><img alt=\"rails-e2e.gif\" src=\"../pic/rails-e2e.gif\" /></p>",
@home.display_content @home.display_content
end end
def test_pic_upload_end_to_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' # 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') @wiki.revise_page('wiki1', 'HomePage', '[[instiki-e2e.txt:file]]', Time.now, 'AnonymousBrave')
assert_equal "<p><span class=\"newWikiWord\">instiki-e2e.txt" + assert_equal "<p><span class=\"newWikiWord\">instiki-e2e.txt" +
"<a href=\"../file/instiki-e2e.txt\">?</a></span></p>", "<a href=\"../file/instiki-e2e.txt\">?</a></span></p>",
@home.display_content @home.display_content
# rails-e2e.gif is unknown to the system, so pic action goes to the file [upload] form # 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' r = process 'file', 'web' => 'wiki1', 'id' => 'instiki-e2e.txt'
assert_success assert_success
assert_rendered_file 'file/file' assert_rendered_file 'file/file'
# User uploads the picture # User uploads the picture
file = "abcdefgh\n123" file = "abcdefgh\n123"
r = process 'file', 'web' => 'wiki1', 'id' => 'instiki-e2e.txt', 'file' => StringIO.new(file) r = process 'file', 'web' => 'wiki1', 'id' => 'instiki-e2e.txt', 'file' => StringIO.new(file)
assert_redirect_url '/' assert_redirect_url '/'
assert @wiki.file_yard(@web).has_file?('instiki-e2e.txt') assert @wiki.file_yard(@web).has_file?('instiki-e2e.txt')
assert_equal(file, File.read("#{RAILS_ROOT}/storage/test/wiki1/instiki-e2e.txt")) assert_equal(file, File.read("#{RAILS_ROOT}/storage/test/wiki1/instiki-e2e.txt"))
# this should refresh the page display content (cached) # this should refresh the page display content (cached)
assert_equal "<p><a class=\"existingWikiWord\" href=\"../file/instiki-e2e.txt\">" + assert_equal "<p><a class=\"existingWikiWord\" href=\"../file/instiki-e2e.txt\">" +
"instiki-e2e.txt</a></p>", "instiki-e2e.txt</a></p>",
@home.display_content @home.display_content
end end
def test_uploads_blocking def test_uploads_blocking
@web.allow_uploads = true @web.allow_uploads = true
r = process 'file', 'web' => 'wiki1', 'id' => 'filename' r = process 'file', 'web' => 'wiki1', 'id' => 'filename'
assert_success assert_success
@web.allow_uploads = false @web.allow_uploads = false
r = process 'file', 'web' => 'wiki1', 'id' => 'filename' r = process 'file', 'web' => 'wiki1', 'id' => 'filename'
assert_equal '403 Forbidden', r.headers['Status'] assert_equal '403 Forbidden', r.headers['Status']
end end
end end

File diff suppressed because it is too large Load diff

226
test/test_helper.rb Executable file → Normal file
View file

@ -1,113 +1,113 @@
ENV['RAILS_ENV'] ||= 'test' ENV['RAILS_ENV'] ||= 'test'
require File.dirname(__FILE__) + '/../config/environment' require File.dirname(__FILE__) + '/../config/environment'
require 'application' require 'application'
require 'test/unit' require 'test/unit'
require 'action_controller/test_process' require 'action_controller/test_process'
# Uncomment this variable to have assert_success check that response bodies are valid XML # Uncomment this variable to have assert_success check that response bodies are valid XML
$validate_xml_in_assert_success = true $validate_xml_in_assert_success = true
# Convenient setup method for Test::Unit::TestCase # Convenient setup method for Test::Unit::TestCase
class Test::Unit::TestCase class Test::Unit::TestCase
private private
def setup_controller_test(controller_class = nil, host = nil) def setup_controller_test(controller_class = nil, host = nil)
if controller_class if controller_class
@controller = controller_class @controller = controller_class
elsif self.class.to_s =~ /^(\w+Controller)Test$/ elsif self.class.to_s =~ /^(\w+Controller)Test$/
@controller = Object::const_get($1) @controller = Object::const_get($1)
else else
raise "Cannot derive the name of controller under test from class name #{self.class}" raise "Cannot derive the name of controller under test from class name #{self.class}"
end end
@request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new @request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
@request.host = host || 'localhost' @request.host = host || 'localhost'
return @request, @response return @request, @response
end end
# Wiki fixture for tests # Wiki fixture for tests
def setup_test_wiki def setup_test_wiki
@wiki = ApplicationController.wiki = WikiServiceWithNoPersistence.new @wiki = ApplicationController.wiki = WikiServiceWithNoPersistence.new
@web = @wiki.create_web('Test Wiki 1', 'wiki1') @web = @wiki.create_web('Test Wiki 1', 'wiki1')
@home = @wiki.write_page('wiki1', 'HomePage', 'First revision of the HomePage end', Time.now, @home = @wiki.write_page('wiki1', 'HomePage', 'First revision of the HomePage end', Time.now,
Author.new('AnAuthor', '127.0.0.1')) Author.new('AnAuthor', '127.0.0.1'))
end end
def setup_wiki_with_three_pages def setup_wiki_with_three_pages
@oak = @wiki.write_page('wiki1', 'Oak', @oak = @wiki.write_page('wiki1', 'Oak',
"All about oak.\n" + "All about oak.\n" +
"category: trees", "category: trees",
5.minutes.ago, Author.new('TreeHugger', '127.0.0.2')) 5.minutes.ago, Author.new('TreeHugger', '127.0.0.2'))
@elephant = @wiki.write_page('wiki1', 'Elephant', @elephant = @wiki.write_page('wiki1', 'Elephant',
"All about elephants.\n" + "All about elephants.\n" +
"category: animals", "category: animals",
10.minutes.ago, Author.new('Guest', '127.0.0.2')) 10.minutes.ago, Author.new('Guest', '127.0.0.2'))
end end
def tear_down_wiki def tear_down_wiki
ApplicationController.wiki = nil ApplicationController.wiki = nil
end end
end end
class WikiServiceWithNoPersistence class WikiServiceWithNoPersistence
include AbstractWikiService include AbstractWikiService
def initialize def initialize
init_wiki_service init_wiki_service
end end
def storage_path def storage_path
RAILS_ROOT + '/storage/test/' RAILS_ROOT + '/storage/test/'
end end
end end
# This module is to be included in unit tests that involve matching chunks. # 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 # 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. # and any the values of any fields that should be set after a match.
class ContentStub < String class ContentStub < String
attr_reader :chunks, :content attr_reader :chunks, :content
def initialize(str) def initialize(str)
super super
@chunks = [] @chunks = []
end end
end end
module ChunkMatch module ChunkMatch
# Asserts a number of tests for the given type and text. # Asserts a number of tests for the given type and text.
def match(chunk_type, test_text, expected_chunk_state) def match(chunk_type, test_text, expected_chunk_state)
if chunk_type.respond_to? :pattern if chunk_type.respond_to? :pattern
assert_match(chunk_type.pattern, test_text) assert_match(chunk_type.pattern, test_text)
end end
content = ContentStub.new(test_text) content = ContentStub.new(test_text)
chunk_type.apply_to(content) chunk_type.apply_to(content)
# Test if requested parts are correct. # Test if requested parts are correct.
expected_chunk_state.each_pair do |a_method, expected_value| expected_chunk_state.each_pair do |a_method, expected_value|
assert content.chunks.last.kind_of?(chunk_type) assert content.chunks.last.kind_of?(chunk_type)
assert_respond_to(content.chunks.last, a_method) assert_respond_to(content.chunks.last, a_method)
assert_equal(expected_value, content.chunks.last.send(a_method.to_sym), assert_equal(expected_value, content.chunks.last.send(a_method.to_sym),
"Wrong #{a_method} value") "Wrong #{a_method} value")
end end
end end
end end
if defined? $validate_xml_in_assert_success and $validate_xml_in_assert_success == true if defined? $validate_xml_in_assert_success and $validate_xml_in_assert_success == true
module Test module Test
module Unit module Unit
module Assertions module Assertions
unless method_defined? :__assert_success_before_ovverride_by_instiki unless method_defined? :__assert_success_before_ovverride_by_instiki
alias :__assert_success_before_ovverride_by_instiki :assert_success alias :__assert_success_before_ovverride_by_instiki :assert_success
end end
def assert_success def assert_success
__assert_success_before_ovverride_by_instiki __assert_success_before_ovverride_by_instiki
if @response.body.kind_of?(Proc) then # it's a file download, not an HTML content 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 else assert_nothing_raised(@response.body) { REXML::Document.new(@response.body) } end
end end
end end
end end
end end
end end

View file

@ -1,23 +1,23 @@
#!/bin/env ruby #!/bin/env ruby
require File.dirname(__FILE__) + '/../../test_helper' require File.dirname(__FILE__) + '/../../test_helper'
require 'chunks/category' require 'chunks/category'
require 'chunks/match' require 'chunks/match'
class CategoryTest < Test::Unit::TestCase class CategoryTest < Test::Unit::TestCase
include ChunkMatch include ChunkMatch
def test_single_category def test_single_category
match(Category, 'category: test', :list => ['test'], :hidden => nil) match(Category, 'category: test', :list => ['test'], :hidden => nil)
match(Category, 'category : chunk test ', :list => ['chunk test'], :hidden => nil) match(Category, 'category : chunk test ', :list => ['chunk test'], :hidden => nil)
match(Category, ':category: test', :list => ['test'], :hidden => ':') match(Category, ':category: test', :list => ['test'], :hidden => ':')
end end
def test_multiple_categories def test_multiple_categories
match(Category, 'category: test, multiple', :list => ['test', 'multiple'], :hidden => nil) match(Category, 'category: test, multiple', :list => ['test', 'multiple'], :hidden => nil)
match(Category, 'category : chunk test , multi category,regression test case ', match(Category, 'category : chunk test , multi category,regression test case ',
:list => ['chunk test','multi category','regression test case'], :hidden => nil :list => ['chunk test','multi category','regression test case'], :hidden => nil
) )
end end
end end

View file

@ -1,16 +1,16 @@
#!/bin/env ruby #!/bin/env ruby
require File.dirname(__FILE__) + '/../../test_helper' require File.dirname(__FILE__) + '/../../test_helper'
require 'chunks/nowiki' require 'chunks/nowiki'
require 'chunks/match' require 'chunks/match'
class NoWikiTest < Test::Unit::TestCase class NoWikiTest < Test::Unit::TestCase
include ChunkMatch include ChunkMatch
def test_simple_nowiki def test_simple_nowiki
match(NoWiki, 'This sentence contains <nowiki>[[raw text]]</nowiki>. Do not touch!', match(NoWiki, 'This sentence contains <nowiki>[[raw text]]</nowiki>. Do not touch!',
:plain_text => '[[raw text]]' :plain_text => '[[raw text]]'
) )
end end
end end

View file

@ -1,81 +1,81 @@
#!/bin/env ruby #!/bin/env ruby
require File.dirname(__FILE__) + '/../../test_helper' require File.dirname(__FILE__) + '/../../test_helper'
require 'chunks/wiki' require 'chunks/wiki'
class WikiTest < Test::Unit::TestCase class WikiTest < Test::Unit::TestCase
class ContentStub < String class ContentStub < String
def chunks def chunks
@chunks ||= [] @chunks ||= []
end end
end end
include ChunkMatch include ChunkMatch
def test_simple def test_simple
match(WikiChunk::Word, 'This is a WikiWord okay?', :page_name => 'WikiWord') match(WikiChunk::Word, 'This is a WikiWord okay?', :page_name => 'WikiWord')
end end
def test_escaped def test_escaped
match(WikiChunk::Word, 'Do not link to an \EscapedWord', match(WikiChunk::Word, 'Do not link to an \EscapedWord',
:page_name => 'EscapedWord', :escaped_text => 'EscapedWord' :page_name => 'EscapedWord', :escaped_text => 'EscapedWord'
) )
end end
def test_simple_brackets def test_simple_brackets
match(WikiChunk::Link, 'This is a [[bracketted link]]', match(WikiChunk::Link, 'This is a [[bracketted link]]',
:page_name => 'bracketted link', :escaped_text => nil :page_name => 'bracketted link', :escaped_text => nil
) )
end end
def test_complex_brackets def test_complex_brackets
match(WikiChunk::Link, 'This is a tricky link [[Sperberg-McQueen]]', match(WikiChunk::Link, 'This is a tricky link [[Sperberg-McQueen]]',
:page_name => 'Sperberg-McQueen', :escaped_text => nil :page_name => 'Sperberg-McQueen', :escaped_text => nil
) )
end end
def test_textile_link def test_textile_link
textile_link = ContentStub.new('"Here is a special link":SpecialLink') textile_link = ContentStub.new('"Here is a special link":SpecialLink')
WikiChunk::Word.apply_to(textile_link) WikiChunk::Word.apply_to(textile_link)
assert_equal '"Here is a special link":SpecialLink', textile_link assert_equal '"Here is a special link":SpecialLink', textile_link
assert textile_link.chunks.empty? assert textile_link.chunks.empty?
end end
def test_file_types def test_file_types
# only link # only link
assert_link_parsed_as 'only text', 'only text', 'show', '[[only text]]' assert_link_parsed_as 'only text', 'only text', 'show', '[[only text]]'
# link and text # link and text
assert_link_parsed_as 'page name', 'link text', 'show', '[[page name|link text]]' assert_link_parsed_as 'page name', 'link text', 'show', '[[page name|link text]]'
# link and type (file) # link and type (file)
assert_link_parsed_as 'foo.tar.gz', 'foo.tar.gz', 'file', '[[foo.tar.gz:file]]' assert_link_parsed_as 'foo.tar.gz', 'foo.tar.gz', 'file', '[[foo.tar.gz:file]]'
# link and type (pic) # link and type (pic)
assert_link_parsed_as 'foo.tar.gz', 'foo.tar.gz', 'pic', '[[foo.tar.gz:pic]]' assert_link_parsed_as 'foo.tar.gz', 'foo.tar.gz', 'pic', '[[foo.tar.gz:pic]]'
# link, text and type # link, text and type
assert_link_parsed_as 'foo.tar.gz', 'FooTar', 'file', '[[foo.tar.gz|FooTar:file]]' assert_link_parsed_as 'foo.tar.gz', 'FooTar', 'file', '[[foo.tar.gz|FooTar:file]]'
# NEGATIVE TEST CASES # NEGATIVE TEST CASES
# empty page name # empty page name
assert_link_parsed_as '|link text?', '|link text?', 'file', '[[|link text?:file]]' assert_link_parsed_as '|link text?', '|link text?', 'file', '[[|link text?:file]]'
# empty link text # empty link text
assert_link_parsed_as 'page name?|', 'page name?|', 'file', '[[page name?|:file]]' assert_link_parsed_as 'page name?|', 'page name?|', 'file', '[[page name?|:file]]'
# empty link type # empty link type
assert_link_parsed_as 'page name', 'link?:', 'show', '[[page name|link?:]]' assert_link_parsed_as 'page name', 'link?:', 'show', '[[page name|link?:]]'
# unknown link type # unknown link type
assert_link_parsed_as 'page name:create_system', 'page name:create_system', 'show', assert_link_parsed_as 'page name:create_system', 'page name:create_system', 'show',
'[[page name:create_system]]' '[[page name:create_system]]'
end end
def assert_link_parsed_as(expected_page_name, expected_link_text, expected_link_type, link) def assert_link_parsed_as(expected_page_name, expected_link_text, expected_link_type, link)
link_to_file = ContentStub.new(link) link_to_file = ContentStub.new(link)
WikiChunk::Link.apply_to(link_to_file) WikiChunk::Link.apply_to(link_to_file)
chunk = link_to_file.chunks.last chunk = link_to_file.chunks.last
assert chunk assert chunk
assert_equal expected_page_name, chunk.page_name assert_equal expected_page_name, chunk.page_name
assert_equal expected_link_text, chunk.link_text assert_equal expected_link_text, chunk.link_text
assert_equal expected_link_type, chunk.link_type assert_equal expected_link_type, chunk.link_type
end end
end end

View file

@ -1,82 +1,82 @@
#!/bin/env ruby -w #!/bin/env ruby -w
require File.dirname(__FILE__) + '/../test_helper' require File.dirname(__FILE__) + '/../test_helper'
require 'diff' require 'diff'
include Diff include Diff
class DiffTest < Test::Unit::TestCase class DiffTest < Test::Unit::TestCase
def test_init def test_init
assert(1 == 1, "tests working") assert(1 == 1, "tests working")
assert_nothing_raised("object created") do assert_nothing_raised("object created") do
s = SequenceMatcher.new "private Thread currentThread;", s = SequenceMatcher.new "private Thread currentThread;",
"private volatile Thread currentThread;", "private volatile Thread currentThread;",
proc { |x| x == ' ' } proc { |x| x == ' ' }
end end
end end
def test_matching_blocks def test_matching_blocks
s = SequenceMatcher.new "abxcd", "abcd" s = SequenceMatcher.new "abxcd", "abcd"
assert(s.get_matching_blocks == [[0, 0, 2], [3, 2, 2], [5, 4, 0]], assert(s.get_matching_blocks == [[0, 0, 2], [3, 2, 2], [5, 4, 0]],
"get_matching_blocks works") "get_matching_blocks works")
end end
def test_ratio def test_ratio
s = SequenceMatcher.new "abcd", "bcde" s = SequenceMatcher.new "abcd", "bcde"
assert(s.ratio == 0.75, "ratio works") assert(s.ratio == 0.75, "ratio works")
assert(s.quick_ratio == 0.75, "quick_ratio works") assert(s.quick_ratio == 0.75, "quick_ratio works")
assert(s.real_quick_ratio == 1.0, "real_quick_ratio works") assert(s.real_quick_ratio == 1.0, "real_quick_ratio works")
end end
def test_longest_match def test_longest_match
s = SequenceMatcher.new(" abcd", "abcd abcd") s = SequenceMatcher.new(" abcd", "abcd abcd")
assert(s.find_longest_match(0, 5, 0, 9) == [0, 4, 5], assert(s.find_longest_match(0, 5, 0, 9) == [0, 4, 5],
"find_longest_match works") "find_longest_match works")
s = SequenceMatcher.new() s = SequenceMatcher.new()
end end
def test_opcodes def test_opcodes
s = SequenceMatcher.new("qabxcd", "abycdf") s = SequenceMatcher.new("qabxcd", "abycdf")
assert(s.get_opcodes == [ assert(s.get_opcodes == [
[:delete, 0, 1, 0, 0], [:delete, 0, 1, 0, 0],
[:equal, 1, 3, 0, 2], [:equal, 1, 3, 0, 2],
[:replace, 3, 4, 2, 3], [:replace, 3, 4, 2, 3],
[:equal, 4, 6, 3, 5], [:equal, 4, 6, 3, 5],
[:insert, 6, 6, 5, 6]], "get_opcodes works") [:insert, 6, 6, 5, 6]], "get_opcodes works")
end end
def test_count_leading def test_count_leading
assert(Diff.count_leading(' abc', ' ') == 3, assert(Diff.count_leading(' abc', ' ') == 3,
"count_leading works") "count_leading works")
end end
def test_html2list def test_html2list
a = "here is the original text" a = "here is the original text"
#p HTMLDiff.html2list(a) #p HTMLDiff.html2list(a)
end end
def test_html_diff def test_html_diff
a = "this was the original string" a = "this was the original string"
b = "this is the super string" b = "this is the super string"
assert_equal 'this <del class="diffmod">was </del>' + assert_equal 'this <del class="diffmod">was </del>' +
'<ins class="diffmod">is </ins>the ' + '<ins class="diffmod">is </ins>the ' +
'<del class="diffmod">original </del>' + '<del class="diffmod">original </del>' +
'<ins class="diffmod">super </ins>string', '<ins class="diffmod">super </ins>string',
HTMLDiff.diff(a, b) HTMLDiff.diff(a, b)
end end
def test_html_diff_with_multiple_paragraphs def test_html_diff_with_multiple_paragraphs
a = "<p>this was the original string</p>" 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>" b = "<p>this is</p>\r\n<p>the super string</p>\r\n<p>around the world</p>"
assert_equal( assert_equal(
"<p>this <del class=\"diffmod\">was </del>" + "<p>this <del class=\"diffmod\">was </del>" +
"<ins class=\"diffmod\">is</ins></p>\r\n<p>the " + "<ins class=\"diffmod\">is</ins></p>\r\n<p>the " +
"<del class=\"diffmod\">original </del>" + "<del class=\"diffmod\">original </del>" +
"<ins class=\"diffmod\">super </ins>string</p>\r\n" + "<ins class=\"diffmod\">super </ins>string</p>\r\n" +
"<p><ins class=\"diffins\">around the world</ins></p>", "<p><ins class=\"diffins\">around the world</ins></p>",
HTMLDiff.diff(a, b) HTMLDiff.diff(a, b)
) )
end end
end end

68
test/unit/file_yard_test.rb Normal file → Executable file
View file

@ -1,35 +1,35 @@
#!/bin/env ruby -w #!/bin/env ruby -w
require File.dirname(__FILE__) + '/../test_helper' require File.dirname(__FILE__) + '/../test_helper'
require 'fileutils' require 'fileutils'
require 'file_yard' require 'file_yard'
require 'stringio' require 'stringio'
class FileYardTest < Test::Unit::TestCase class FileYardTest < Test::Unit::TestCase
def setup def setup
FileUtils.mkdir_p(file_path) FileUtils.mkdir_p(file_path)
FileUtils.rm(Dir["#{file_path}/*"]) FileUtils.rm(Dir["#{file_path}/*"])
@yard = FileYard.new(file_path) @yard = FileYard.new(file_path)
end end
def test_files def test_files
assert_equal [], @yard.files assert_equal [], @yard.files
# FileYard gets the list of files from directory in the constructor # FileYard gets the list of files from directory in the constructor
@yard.upload_file('aaa', StringIO.new('file contents')) @yard.upload_file('aaa', StringIO.new('file contents'))
assert_equal ["#{file_path}/aaa"], Dir["#{file_path}/*"] assert_equal ["#{file_path}/aaa"], Dir["#{file_path}/*"]
assert_equal ['aaa'], @yard.files assert_equal ['aaa'], @yard.files
assert @yard.has_file?('aaa') assert @yard.has_file?('aaa')
assert_equal 'file contents', File.read(@yard.file_path('aaa')) assert_equal 'file contents', File.read(@yard.file_path('aaa'))
end end
def test_file_path def test_file_path
assert_equal "#{file_path}/abcd", @yard.file_path('abcd') assert_equal "#{file_path}/abcd", @yard.file_path('abcd')
end end
def file_path def file_path
"#{RAILS_ROOT}/storage/test/instiki" "#{RAILS_ROOT}/storage/test/instiki"
end end
end end

View file

@ -1,91 +1,91 @@
#!/bin/env ruby -w #!/bin/env ruby -w
require File.dirname(__FILE__) + '/../test_helper' require File.dirname(__FILE__) + '/../test_helper'
require 'web' require 'web'
require 'page' require 'page'
class PageTest < Test::Unit::TestCase class PageTest < Test::Unit::TestCase
class MockWeb < Web class MockWeb < Web
def initialize() super(nil, 'test','test') end def initialize() super(nil, 'test','test') end
def [](wiki_word) %w( MyWay ThatWay SmartEngine ).include?(wiki_word) end def [](wiki_word) %w( MyWay ThatWay SmartEngine ).include?(wiki_word) end
def refresh_pages_with_references(name) end def refresh_pages_with_references(name) end
end end
def setup def setup
@page = Page.new( @page = Page.new(
MockWeb.new, MockWeb.new,
"FirstPage", "FirstPage",
"HisWay would be MyWay in kinda ThatWay in HisWay though MyWay \\OverThere -- see SmartEngine in that SmartEngineGUI", "HisWay would be MyWay in kinda ThatWay in HisWay though MyWay \\OverThere -- see SmartEngine in that SmartEngineGUI",
Time.local(2004, 4, 4, 16, 50), Time.local(2004, 4, 4, 16, 50),
"DavidHeinemeierHansson") "DavidHeinemeierHansson")
end end
def test_lock def test_lock
assert !@page.locked?(Time.local(2004, 4, 4, 16, 50)) assert !@page.locked?(Time.local(2004, 4, 4, 16, 50))
@page.lock(Time.local(2004, 4, 4, 16, 30), "DavidHeinemeierHansson") @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, 16, 50))
assert !@page.locked?(Time.local(2004, 4, 4, 17, 1)) assert !@page.locked?(Time.local(2004, 4, 4, 17, 1))
@page.unlock @page.unlock
assert !@page.locked?(Time.local(2004, 4, 4, 16, 50)) assert !@page.locked?(Time.local(2004, 4, 4, 16, 50))
end end
def test_lock_duration def test_lock_duration
@page.lock(Time.local(2004, 4, 4, 16, 30), "DavidHeinemeierHansson") @page.lock(Time.local(2004, 4, 4, 16, 30), "DavidHeinemeierHansson")
assert_equal 15, @page.lock_duration(Time.local(2004, 4, 4, 16, 45)) assert_equal 15, @page.lock_duration(Time.local(2004, 4, 4, 16, 45))
end end
def test_plain_name def test_plain_name
assert_equal "First Page", @page.plain_name assert_equal "First Page", @page.plain_name
end end
def test_revise def test_revise
@page.revise('HisWay would be MyWay in kinda lame', Time.local(2004, 4, 4, 16, 55), 'MarianneSyhler') @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 2, @page.revisions.length, 'Should have two revisions'
assert_equal 'MarianneSyhler', @page.author, 'Mary should be the author now' assert_equal 'MarianneSyhler', @page.author, 'Mary should be the author now'
assert_equal 'DavidHeinemeierHansson', @page.revisions.first.author, 'David was the first author' assert_equal 'DavidHeinemeierHansson', @page.revisions.first.author, 'David was the first author'
end end
def test_revise_continous_revision def test_revise_continous_revision
@page.revise('HisWay would be MyWay in kinda lame', Time.local(2004, 4, 4, 16, 55), 'MarianneSyhler') @page.revise('HisWay would be MyWay in kinda lame', Time.local(2004, 4, 4, 16, 55), 'MarianneSyhler')
assert_equal 2, @page.revisions.length assert_equal 2, @page.revisions.length
@page.revise('HisWay would be MyWay in kinda update', Time.local(2004, 4, 4, 16, 57), 'MarianneSyhler') @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 2, @page.revisions.length
assert_equal 'HisWay would be MyWay in kinda update', @page.revisions.last.content 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 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') @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 3, @page.revisions.length
assert_equal 'HisWay would be MyWay in the house', @page.revisions.last.content 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') @page.revise('HisWay would be MyWay in my way', Time.local(2004, 4, 4, 17, 30), 'DavidHeinemeierHansson')
assert_equal 4, @page.revisions.length assert_equal 4, @page.revisions.length
end end
def test_revise_content_unchanged def test_revise_content_unchanged
last_revision_before = @page.revisions.last last_revision_before = @page.revisions.last
revisions_number_before = @page.revisions.size revisions_number_before = @page.revisions.size
assert_raises(Instiki::ValidationError) { assert_raises(Instiki::ValidationError) {
@page.revise(@page.revisions.last.content.dup, Time.now, 'AlexeyVerkhovsky') @page.revise(@page.revisions.last.content.dup, Time.now, 'AlexeyVerkhovsky')
} }
assert_same last_revision_before, @page.revisions.last assert_same last_revision_before, @page.revisions.last
assert_equal revisions_number_before, @page.revisions.size assert_equal revisions_number_before, @page.revisions.size
end end
def test_rollback def test_rollback
@page.revise("spot two", Time.now, "David") @page.revise("spot two", Time.now, "David")
@page.revise("spot three", Time.now + 2000, "David") @page.revise("spot three", Time.now + 2000, "David")
assert_equal 3, @page.revisions.length, "Should have three revisions" assert_equal 3, @page.revisions.length, "Should have three revisions"
@page.rollback(1, Time.now) @page.rollback(1, Time.now)
assert_equal "spot two", @page.content assert_equal "spot two", @page.content
end end
end end

View file

@ -1,69 +1,69 @@
#!/bin/env ruby -w #!/bin/env ruby -w
require File.dirname(__FILE__) + '/../test_helper' require File.dirname(__FILE__) + '/../test_helper'
require 'redcloth_for_tex' require 'redcloth_for_tex'
class RedClothForTexTest < Test::Unit::TestCase class RedClothForTexTest < Test::Unit::TestCase
def test_basics def test_basics
assert_equal '{\bf First Page}', RedClothForTex.new("*First Page*").to_tex assert_equal '{\bf First Page}', RedClothForTex.new("*First Page*").to_tex
assert_equal '{\em 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 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 end
def test_blocks def test_blocks
assert_equal '\section*{hello}', RedClothForTex.new("h1. hello").to_tex assert_equal '\section*{hello}', RedClothForTex.new("h1. hello").to_tex
assert_equal '\subsection*{hello}', RedClothForTex.new("h2. hello").to_tex assert_equal '\subsection*{hello}', RedClothForTex.new("h2. hello").to_tex
end end
def test_table_of_contents def test_table_of_contents
source = <<EOL source = <<EOL
* [[A]] * [[A]]
** [[B]] ** [[B]]
** [[C]] ** [[C]]
* D * D
** [[E]] ** [[E]]
*** F *** F
EOL EOL
expected_result = <<EOL expected_result = <<EOL
\\pagebreak \\pagebreak
\\section{A} \\section{A}
Abe Abe
\\subsection{B} \\subsection{B}
Babe Babe
\\subsection{C} \\subsection{C}
\\pagebreak \\pagebreak
\\section{D} \\section{D}
\\subsection{E} \\subsection{E}
\\subsubsection{F} \\subsubsection{F}
EOL EOL
expected_result.chop! expected_result.chop!
assert_equal(expected_result, table_of_contents(source, 'A' => 'Abe', 'B' => 'Babe')) assert_equal(expected_result, table_of_contents(source, 'A' => 'Abe', 'B' => 'Babe'))
end end
def test_entities def test_entities
assert_equal "Beck \\& Fowler are 100\\% cool", RedClothForTex.new("Beck & Fowler are 100% cool").to_tex assert_equal "Beck \\& Fowler are 100\\% cool", RedClothForTex.new("Beck & Fowler are 100% cool").to_tex
end end
def test_bracket_links 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 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 end
def test_footnotes_on_abbreviations def test_footnotes_on_abbreviations
assert_equal( assert_equal(
"such a Horrible Day\\footnote{1}, but I won't be Made Useless", "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 RedClothForTex.new("such a [[Horrible Day]][1], but I won't be [[Made Useless]]").to_tex
) )
end end
def test_subsection_depth def test_subsection_depth
assert_equal "\\subsubsection*{Hello}", RedClothForTex.new("h4. Hello").to_tex assert_equal "\\subsubsection*{Hello}", RedClothForTex.new("h4. Hello").to_tex
end end
end end

View file

@ -1,255 +1,255 @@
#!/bin/env ruby -w #!/bin/env ruby -w
require File.dirname(__FILE__) + '/../test_helper' require File.dirname(__FILE__) + '/../test_helper'
require 'web' require 'web'
require 'revision' require 'revision'
require 'fileutils' require 'fileutils'
class RevisionTest < Test::Unit::TestCase class RevisionTest < Test::Unit::TestCase
def setup def setup
setup_test_wiki setup_test_wiki
@web.markup = :textile @web.markup = :textile
@page = @wiki.read_page('wiki1', 'HomePage') @page = @wiki.read_page('wiki1', 'HomePage')
['MyWay', 'SmartEngine', 'ThatWay'].each do |page| ['MyWay', 'SmartEngine', 'ThatWay'].each do |page|
@wiki.write_page('wiki1', page, page, Time.now, 'Me') @wiki.write_page('wiki1', page, page, Time.now, 'Me')
end end
@revision = Revision.new(@page, 1, @revision = Revision.new(@page, 1,
'HisWay would be MyWay in kinda ThatWay in HisWay though MyWay \OverThere -- ' + 'HisWay would be MyWay in kinda ThatWay in HisWay though MyWay \OverThere -- ' +
'see SmartEngine in that SmartEngineGUI', 'see SmartEngine in that SmartEngineGUI',
Time.local(2004, 4, 4, 16, 50), 'DavidHeinemeierHansson') Time.local(2004, 4, 4, 16, 50), 'DavidHeinemeierHansson')
end end
def test_wiki_words def test_wiki_words
assert_equal %w( HisWay MyWay SmartEngine SmartEngineGUI ThatWay ), @revision.wiki_words.sort assert_equal %w( HisWay MyWay SmartEngine SmartEngineGUI ThatWay ), @revision.wiki_words.sort
end end
def test_existing_pages def test_existing_pages
assert_equal %w( MyWay SmartEngine ThatWay ), @revision.existing_pages.sort assert_equal %w( MyWay SmartEngine ThatWay ), @revision.existing_pages.sort
end end
def test_unexisting_pages def test_unexisting_pages
assert_equal %w( HisWay SmartEngineGUI ), @revision.unexisting_pages.sort assert_equal %w( HisWay SmartEngineGUI ), @revision.unexisting_pages.sort
end end
def test_content_with_wiki_links def test_content_with_wiki_links
assert_equal '<p><span class="newWikiWord">His Way<a href="../show/HisWay">?</a></span> ' + 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 ' + 'would be <a class="existingWikiWord" href="../show/MyWay">My Way</a> in kinda ' +
'<a class="existingWikiWord" href="../show/ThatWay">That Way</a> in ' + '<a class="existingWikiWord" href="../show/ThatWay">That Way</a> in ' +
'<span class="newWikiWord">His Way<a href="../show/HisWay">?</a></span> ' + '<span class="newWikiWord">His Way<a href="../show/HisWay">?</a></span> ' +
'though <a class="existingWikiWord" href="../show/MyWay">My Way</a> OverThere&#8212;see ' + 'though <a class="existingWikiWord" href="../show/MyWay">My Way</a> OverThere&#8212;see ' +
'<a class="existingWikiWord" href="../show/SmartEngine">Smart Engine</a> in that ' + '<a class="existingWikiWord" href="../show/SmartEngine">Smart Engine</a> in that ' +
'<span class="newWikiWord">Smart Engine GUI' + '<span class="newWikiWord">Smart Engine GUI' +
'<a href="../show/SmartEngineGUI">?</a></span></p>', '<a href="../show/SmartEngineGUI">?</a></span></p>',
@revision.display_content @revision.display_content
end end
def test_bluecloth def test_bluecloth
@web.markup = :markdown @web.markup = :markdown
assert_markup_parsed_as( assert_markup_parsed_as(
%{<h1>My Headline</h1>\n\n<p>that <span class="newWikiWord">} + %{<h1>My Headline</h1>\n\n<p>that <span class="newWikiWord">} +
%{Smart Engine GUI<a href="../show/SmartEngineGUI">?</a></span></p>}, %{Smart Engine GUI<a href="../show/SmartEngineGUI">?</a></span></p>},
"My Headline\n===========\n\n that SmartEngineGUI") "My Headline\n===========\n\n that SmartEngineGUI")
code_block = [ code_block = [
'This is a code block:', 'This is a code block:',
'', '',
' def a_method(arg)', ' def a_method(arg)',
' return ThatWay', ' return ThatWay',
'', '',
'Nice!' 'Nice!'
].join("\n") ].join("\n")
assert_markup_parsed_as( assert_markup_parsed_as(
%{<p>This is a code block:</p>\n\n<pre><code>def a_method(arg)\n} + %{<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>}, %{return ThatWay\n</code></pre>\n\n<p>Nice!</p>},
code_block) code_block)
end end
def test_rdoc def test_rdoc
@web.markup = :rdoc @web.markup = :rdoc
@revision = Revision.new(@page, 1, '+hello+ that SmartEngineGUI', @revision = Revision.new(@page, 1, '+hello+ that SmartEngineGUI',
Time.local(2004, 4, 4, 16, 50), 'DavidHeinemeierHansson') Time.local(2004, 4, 4, 16, 50), 'DavidHeinemeierHansson')
assert_equal "<tt>hello</tt> that <span class=\"newWikiWord\">Smart Engine GUI" + assert_equal "<tt>hello</tt> that <span class=\"newWikiWord\">Smart Engine GUI" +
"<a href=\"../show/SmartEngineGUI\">?</a></span>\n\n", @revision.display_content "<a href=\"../show/SmartEngineGUI\">?</a></span>\n\n", @revision.display_content
end end
def test_content_with_auto_links def test_content_with_auto_links
assert_markup_parsed_as( assert_markup_parsed_as(
'<p><a href="http://www.loudthinking.com/">http://www.loudthinking.com/</a> ' + '<p><a href="http://www.loudthinking.com/">http://www.loudthinking.com/</a> ' +
'points to <a class="existingWikiWord" href="../show/ThatWay">That Way</a> from ' + 'points to <a class="existingWikiWord" href="../show/ThatWay">That Way</a> from ' +
'<a href="mailto:david@loudthinking.com">david@loudthinking.com</a></p>', '<a href="mailto:david@loudthinking.com">david@loudthinking.com</a></p>',
'http://www.loudthinking.com/ points to ThatWay from david@loudthinking.com') 'http://www.loudthinking.com/ points to ThatWay from david@loudthinking.com')
end end
def test_content_with_aliased_links def test_content_with_aliased_links
assert_markup_parsed_as( assert_markup_parsed_as(
'<p>Would a <a class="existingWikiWord" href="../show/SmartEngine">clever motor' + '<p>Would a <a class="existingWikiWord" href="../show/SmartEngine">clever motor' +
'</a> go by any other name?</p>', '</a> go by any other name?</p>',
'Would a [[SmartEngine|clever motor]] go by any other name?') 'Would a [[SmartEngine|clever motor]] go by any other name?')
end end
def test_content_with_wikiword_in_em def test_content_with_wikiword_in_em
assert_markup_parsed_as( assert_markup_parsed_as(
'<p><em>should we go <a class="existingWikiWord" href="../show/ThatWay">' + '<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>' + 'That Way</a> or <span class="newWikiWord">This Way<a href="../show/ThisWay">?</a>' +
'</span> </em></p>', '</span> </em></p>',
'_should we go ThatWay or ThisWay _') '_should we go ThatWay or ThisWay _')
end end
def test_content_with_wikiword_in_tag def test_content_with_wikiword_in_tag
assert_markup_parsed_as( assert_markup_parsed_as(
'<p>That is some <em style="WikiWord">Stylish Emphasis</em></p>', '<p>That is some <em style="WikiWord">Stylish Emphasis</em></p>',
'That is some <em style="WikiWord">Stylish Emphasis</em>') 'That is some <em style="WikiWord">Stylish Emphasis</em>')
end end
def test_content_with_escaped_wikiword def test_content_with_escaped_wikiword
# there should be no wiki link # there should be no wiki link
assert_markup_parsed_as('<p>WikiWord</p>', '\WikiWord') assert_markup_parsed_as('<p>WikiWord</p>', '\WikiWord')
end end
def test_content_with_pre_blocks def test_content_with_pre_blocks
assert_markup_parsed_as( 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>',
'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 end
def test_content_with_autolink_in_parentheses def test_content_with_autolink_in_parentheses
assert_markup_parsed_as( assert_markup_parsed_as(
'<p>The <span class="caps">W3C</span> body (<a href="http://www.w3c.org">' + '<p>The <span class="caps">W3C</span> body (<a href="http://www.w3c.org">' +
'http://www.w3c.org</a>) sets web standards</p>', 'http://www.w3c.org</a>) sets web standards</p>',
'The W3C body (http://www.w3c.org) sets web standards') 'The W3C body (http://www.w3c.org) sets web standards')
end end
def test_content_with_link_in_parentheses def test_content_with_link_in_parentheses
assert_markup_parsed_as( assert_markup_parsed_as(
'<p>(<a href="http://wiki.org/wiki.cgi?WhatIsWiki">What is a wiki?</a>)</p>', '<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)') '("What is a wiki?":http://wiki.org/wiki.cgi?WhatIsWiki)')
end end
def test_content_with_image_link def test_content_with_image_link
assert_markup_parsed_as( assert_markup_parsed_as(
'<p>This <img src="http://hobix.com/sample.jpg" alt="" /> is a Textile image link.</p>', '<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.') 'This !http://hobix.com/sample.jpg! is a Textile image link.')
end end
def test_content_with_nowiki_text def test_content_with_nowiki_text
assert_markup_parsed_as( assert_markup_parsed_as(
'<p>Do not mark up [[this text]] or http://www.thislink.com.</p>', '<p>Do not mark up [[this text]] or http://www.thislink.com.</p>',
'Do not mark up <nowiki>[[this text]]</nowiki> ' + 'Do not mark up <nowiki>[[this text]]</nowiki> ' +
'or <nowiki>http://www.thislink.com</nowiki>.') 'or <nowiki>http://www.thislink.com</nowiki>.')
end end
def test_content_with_bracketted_wiki_word def test_content_with_bracketted_wiki_word
@web.brackets_only = true @web.brackets_only = true
assert_markup_parsed_as( assert_markup_parsed_as(
'<p>This is a WikiWord and a tricky name <span class="newWikiWord">' + '<p>This is a WikiWord and a tricky name <span class="newWikiWord">' +
'Sperberg-McQueen<a href="../show/Sperberg-McQueen">?</a></span>.</p>', 'Sperberg-McQueen<a href="../show/Sperberg-McQueen">?</a></span>.</p>',
'This is a WikiWord and a tricky name [[Sperberg-McQueen]].') 'This is a WikiWord and a tricky name [[Sperberg-McQueen]].')
end end
def test_content_for_export def test_content_for_export
assert_equal '<p><span class="newWikiWord">His Way</span> would be ' + 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="MyWay.html">My Way</a> in kinda ' +
'<a class="existingWikiWord" href="ThatWay.html">That Way</a> in ' + '<a class="existingWikiWord" href="ThatWay.html">That Way</a> in ' +
'<span class="newWikiWord">His Way</span> though ' + '<span class="newWikiWord">His Way</span> though ' +
'<a class="existingWikiWord" href="MyWay.html">My Way</a> OverThere&#8212;see ' + '<a class="existingWikiWord" href="MyWay.html">My Way</a> OverThere&#8212;see ' +
'<a class="existingWikiWord" href="SmartEngine.html">Smart Engine</a> in that ' + '<a class="existingWikiWord" href="SmartEngine.html">Smart Engine</a> in that ' +
'<span class="newWikiWord">Smart Engine GUI</span></p>', '<span class="newWikiWord">Smart Engine GUI</span></p>',
@revision.display_content_for_export @revision.display_content_for_export
end end
def test_double_replacing def test_double_replacing
@revision.content = "VersionHistory\r\n\r\ncry VersionHistory" @revision.content = "VersionHistory\r\n\r\ncry VersionHistory"
assert_equal '<p><span class="newWikiWord">Version History' + assert_equal '<p><span class="newWikiWord">Version History' +
"<a href=\"../show/VersionHistory\">?</a></span></p>\n\n\t<p>cry " + "<a href=\"../show/VersionHistory\">?</a></span></p>\n\n\t<p>cry " +
'<span class="newWikiWord">Version History<a href="../show/VersionHistory">?</a>' + '<span class="newWikiWord">Version History<a href="../show/VersionHistory">?</a>' +
'</span></p>', '</span></p>',
@revision.display_content @revision.display_content
@revision.clear_display_cache @revision.clear_display_cache
@revision.content = "f\r\nVersionHistory\r\n\r\ncry VersionHistory" @revision.content = "f\r\nVersionHistory\r\n\r\ncry VersionHistory"
assert_equal "<p>f<br />\n<span class=\"newWikiWord\">Version History" + assert_equal "<p>f<br />\n<span class=\"newWikiWord\">Version History" +
"<a href=\"../show/VersionHistory\">?</a></span></p>\n\n\t<p>cry " + "<a href=\"../show/VersionHistory\">?</a></span></p>\n\n\t<p>cry " +
"<span class=\"newWikiWord\">Version History<a href=\"../show/VersionHistory\">?</a>" + "<span class=\"newWikiWord\">Version History<a href=\"../show/VersionHistory\">?</a>" +
"</span></p>", "</span></p>",
@revision.display_content @revision.display_content
end end
def test_difficult_wiki_words def test_difficult_wiki_words
@revision.content = "[[It's just awesome GUI!]]" @revision.content = "[[It's just awesome GUI!]]"
assert_equal "<p><span class=\"newWikiWord\">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>", "<a href=\"../show/It%27s+just+awesome+GUI%21\">?</a></span></p>",
@revision.display_content @revision.display_content
end end
def test_revisions_diff def test_revisions_diff
@page.revisions = [ @page.revisions = [
Revision.new(@page, 0, 'What a blue and lovely morning', Revision.new(@page, 0, 'What a blue and lovely morning',
Time.local(2004, 4, 4, 16, 50), 'DavidHeinemeierHansson'), Time.local(2004, 4, 4, 16, 50), 'DavidHeinemeierHansson'),
Revision.new(@page, 1, 'What a red and lovely morning today', Revision.new(@page, 1, 'What a red and lovely morning today',
Time.local(2004, 4, 4, 16, 50), 'DavidHeinemeierHansson') Time.local(2004, 4, 4, 16, 50), 'DavidHeinemeierHansson')
] ]
assert_equal "<p>What a <del class=\"diffmod\">blue </del><ins class=\"diffmod\">red " + 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 " + "</ins>and lovely <del class=\"diffmod\">morning</del><ins class=\"diffmod\">morning " +
"today</ins></p>", @page.revisions.last.display_diff "today</ins></p>", @page.revisions.last.display_diff
end end
def test_link_to_file def test_link_to_file
assert_markup_parsed_as( assert_markup_parsed_as(
'<p><span class="newWikiWord">doc.pdf<a href="../file/doc.pdf">?</a></span></p>', '<p><span class="newWikiWord">doc.pdf<a href="../file/doc.pdf">?</a></span></p>',
'[[doc.pdf:file]]') '[[doc.pdf:file]]')
end end
def test_link_to_pic def test_link_to_pic
@wiki.file_yard(@web).upload_file('square.jpg', StringIO.new('')) @wiki.file_yard(@web).upload_file('square.jpg', StringIO.new(''))
assert_markup_parsed_as( assert_markup_parsed_as(
'<p><img alt="Square" src="../pic/square.jpg" /></p>', '<p><img alt="Square" src="../pic/square.jpg" /></p>',
'[[square.jpg|Square:pic]]') '[[square.jpg|Square:pic]]')
assert_markup_parsed_as( assert_markup_parsed_as(
'<p><img alt="square.jpg" src="../pic/square.jpg" /></p>', '<p><img alt="square.jpg" src="../pic/square.jpg" /></p>',
'[[square.jpg:pic]]') '[[square.jpg:pic]]')
end end
def test_link_to_non_existant_pic def test_link_to_non_existant_pic
assert_markup_parsed_as( assert_markup_parsed_as(
'<p><span class="newWikiWord">NonExistant<a href="../pic/NonExistant.jpg">?</a>' + '<p><span class="newWikiWord">NonExistant<a href="../pic/NonExistant.jpg">?</a>' +
'</span></p>', '</span></p>',
'[[NonExistant.jpg|NonExistant:pic]]') '[[NonExistant.jpg|NonExistant:pic]]')
assert_markup_parsed_as( assert_markup_parsed_as(
'<p><span class="newWikiWord">NonExistant.jpg<a href="../pic/NonExistant.jpg">?</a>' + '<p><span class="newWikiWord">NonExistant.jpg<a href="../pic/NonExistant.jpg">?</a>' +
'</span></p>', '</span></p>',
'[[NonExistant.jpg:pic]]') '[[NonExistant.jpg:pic]]')
end end
# TODO Remove the leading underscores from this test when upgrading to RedCloth 3.0.1; # 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) # also add a test for the "Unhappy Face" problem (another interesting RedCloth bug)
def __test_list_with_tildas def __test_list_with_tildas
list_with_tildas = <<-EOL list_with_tildas = <<-EOL
* "a":~b * "a":~b
* c~ d * c~ d
EOL EOL
assert_markup_parsed_as( assert_markup_parsed_as(
"<li><a href=\"~b\">a</a></li>\n" + "<li><a href=\"~b\">a</a></li>\n" +
"<li>c~ d</li>\n", "<li>c~ d</li>\n",
list_with_tildas) list_with_tildas)
end end
def assert_markup_parsed_as(expected_output, input) def assert_markup_parsed_as(expected_output, input)
revision = Revision.new(@page, 1, input, Time.local(2004, 4, 4, 16, 50), 'AnAuthor') 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' assert_equal expected_output, revision.display_content, 'Textile output not as expected'
end end
end end

View file

@ -1,179 +1,179 @@
#!/bin/env ruby -w #!/bin/env ruby -w
require File.dirname(__FILE__) + '/../test_helper' require File.dirname(__FILE__) + '/../test_helper'
require 'chunks/uri' require 'chunks/uri'
class URITest < Test::Unit::TestCase class URITest < Test::Unit::TestCase
include ChunkMatch include ChunkMatch
def test_non_matches def test_non_matches
assert_conversion_does_not_apply(URIChunk, 'There is no URI here') assert_conversion_does_not_apply(URIChunk, 'There is no URI here')
assert_conversion_does_not_apply(URIChunk, assert_conversion_does_not_apply(URIChunk,
'One gemstone is the garnet:reddish in colour, like ruby') 'One gemstone is the garnet:reddish in colour, like ruby')
end end
def test_simple_uri def test_simple_uri
# Simplest case # Simplest case
match(URIChunk, 'http://www.example.com', match(URIChunk, 'http://www.example.com',
:scheme =>'http', :host =>'www.example.com', :path => nil, :scheme =>'http', :host =>'www.example.com', :path => nil,
:link_text => 'http://www.example.com' :link_text => 'http://www.example.com'
) )
# With trailing slash # With trailing slash
match(URIChunk, 'http://www.example.com/', match(URIChunk, 'http://www.example.com/',
:scheme =>'http', :host =>'www.example.com', :path => '/', :scheme =>'http', :host =>'www.example.com', :path => '/',
:link_text => 'http://www.example.com/' :link_text => 'http://www.example.com/'
) )
# Without http:// # Without http://
match(URIChunk, 'www.example.com', match(URIChunk, 'www.example.com',
:scheme =>'http', :host =>'www.example.com', :link_text => 'www.example.com' :scheme =>'http', :host =>'www.example.com', :link_text => 'www.example.com'
) )
# two parts # two parts
match(URIChunk, 'example.com', match(URIChunk, 'example.com',
:scheme =>'http',:host =>'example.com', :link_text => 'example.com' :scheme =>'http',:host =>'example.com', :link_text => 'example.com'
) )
# "unusual" base domain (was a bug in an early version) # "unusual" base domain (was a bug in an early version)
match(URIChunk, 'http://example.com.au/', match(URIChunk, 'http://example.com.au/',
:scheme =>'http', :host =>'example.com.au', :link_text => 'http://example.com.au/' :scheme =>'http', :host =>'example.com.au', :link_text => 'http://example.com.au/'
) )
# "unusual" base domain without http:// # "unusual" base domain without http://
match(URIChunk, 'example.com.au', match(URIChunk, 'example.com.au',
:scheme =>'http', :host =>'example.com.au', :link_text => 'example.com.au' :scheme =>'http', :host =>'example.com.au', :link_text => 'example.com.au'
) )
# Another "unusual" base domain # Another "unusual" base domain
match(URIChunk, 'http://www.example.co.uk/', match(URIChunk, 'http://www.example.co.uk/',
:scheme =>'http', :host =>'www.example.co.uk', :scheme =>'http', :host =>'www.example.co.uk',
:link_text => 'http://www.example.co.uk/' :link_text => 'http://www.example.co.uk/'
) )
match(URIChunk, 'example.co.uk', match(URIChunk, 'example.co.uk',
:scheme =>'http', :host =>'example.co.uk', :link_text => 'example.co.uk' :scheme =>'http', :host =>'example.co.uk', :link_text => 'example.co.uk'
) )
# With some path at the end # With some path at the end
match(URIChunk, 'http://moinmoin.wikiwikiweb.de/HelpOnNavigation', match(URIChunk, 'http://moinmoin.wikiwikiweb.de/HelpOnNavigation',
:scheme => 'http', :host => 'moinmoin.wikiwikiweb.de', :path => '/HelpOnNavigation', :scheme => 'http', :host => 'moinmoin.wikiwikiweb.de', :path => '/HelpOnNavigation',
:link_text => 'http://moinmoin.wikiwikiweb.de/HelpOnNavigation' :link_text => 'http://moinmoin.wikiwikiweb.de/HelpOnNavigation'
) )
# With some path at the end, and withot http:// prefix # With some path at the end, and withot http:// prefix
match(URIChunk, 'moinmoin.wikiwikiweb.de/HelpOnNavigation', match(URIChunk, 'moinmoin.wikiwikiweb.de/HelpOnNavigation',
:scheme => 'http', :host => 'moinmoin.wikiwikiweb.de', :path => '/HelpOnNavigation', :scheme => 'http', :host => 'moinmoin.wikiwikiweb.de', :path => '/HelpOnNavigation',
:link_text => 'moinmoin.wikiwikiweb.de/HelpOnNavigation' :link_text => 'moinmoin.wikiwikiweb.de/HelpOnNavigation'
) )
# With a port number # With a port number
match(URIChunk, 'http://www.example.com:80', match(URIChunk, 'http://www.example.com:80',
:scheme =>'http', :host =>'www.example.com', :port => '80', :path => nil, :scheme =>'http', :host =>'www.example.com', :port => '80', :path => nil,
:link_text => 'http://www.example.com:80') :link_text => 'http://www.example.com:80')
# With a port number and a path # With a port number and a path
match(URIChunk, 'http://www.example.com.tw:80/HelpOnNavigation', match(URIChunk, 'http://www.example.com.tw:80/HelpOnNavigation',
:scheme =>'http', :host =>'www.example.com.tw', :port => '80', :path => '/HelpOnNavigation', :scheme =>'http', :host =>'www.example.com.tw', :port => '80', :path => '/HelpOnNavigation',
:link_text => 'http://www.example.com.tw:80/HelpOnNavigation') :link_text => 'http://www.example.com.tw:80/HelpOnNavigation')
# With a query # With a query
match(URIChunk, 'http://www.example.com.tw:80/HelpOnNavigation?arg=val', match(URIChunk, 'http://www.example.com.tw:80/HelpOnNavigation?arg=val',
:scheme =>'http', :host =>'www.example.com.tw', :port => '80', :path => '/HelpOnNavigation', :scheme =>'http', :host =>'www.example.com.tw', :port => '80', :path => '/HelpOnNavigation',
:query => 'arg=val', :query => 'arg=val',
:link_text => 'http://www.example.com.tw:80/HelpOnNavigation?arg=val') :link_text => 'http://www.example.com.tw:80/HelpOnNavigation?arg=val')
# Query with two arguments # Query with two arguments
match(URIChunk, 'http://www.example.com.tw:80/HelpOnNavigation?arg=val&arg2=val2', match(URIChunk, 'http://www.example.com.tw:80/HelpOnNavigation?arg=val&arg2=val2',
:scheme =>'http', :host =>'www.example.com.tw', :port => '80', :path => '/HelpOnNavigation', :scheme =>'http', :host =>'www.example.com.tw', :port => '80', :path => '/HelpOnNavigation',
:query => 'arg=val&arg2=val2', :query => 'arg=val&arg2=val2',
:link_text => 'http://www.example.com.tw:80/HelpOnNavigation?arg=val&arg2=val2') :link_text => 'http://www.example.com.tw:80/HelpOnNavigation?arg=val&arg2=val2')
# HTTPS # HTTPS
match(URIChunk, 'https://www.example.com', match(URIChunk, 'https://www.example.com',
:scheme =>'https', :host =>'www.example.com', :port => nil, :path => nil, :query => nil, :scheme =>'https', :host =>'www.example.com', :port => nil, :path => nil, :query => nil,
:link_text => 'https://www.example.com') :link_text => 'https://www.example.com')
# FTP # FTP
match(URIChunk, 'ftp://www.example.com', match(URIChunk, 'ftp://www.example.com',
:scheme =>'ftp', :host =>'www.example.com', :port => nil, :path => nil, :query => nil, :scheme =>'ftp', :host =>'www.example.com', :port => nil, :path => nil, :query => nil,
:link_text => 'ftp://www.example.com') :link_text => 'ftp://www.example.com')
# mailto # mailto
match(URIChunk, 'mailto:jdoe123@example.com', match(URIChunk, 'mailto:jdoe123@example.com',
:scheme =>'mailto', :host =>'example.com', :port => nil, :path => nil, :query => nil, :scheme =>'mailto', :host =>'example.com', :port => nil, :path => nil, :query => nil,
:user => 'jdoe123', :link_text => 'mailto:jdoe123@example.com') :user => 'jdoe123', :link_text => 'mailto:jdoe123@example.com')
# something nonexistant # something nonexistant
match(URIChunk, 'foobar://www.example.com', match(URIChunk, 'foobar://www.example.com',
:scheme =>'foobar', :host =>'www.example.com', :port => nil, :path => nil, :query => nil, :scheme =>'foobar', :host =>'www.example.com', :port => nil, :path => nil, :query => nil,
:link_text => 'foobar://www.example.com') :link_text => 'foobar://www.example.com')
# Soap opera (the most complex case imaginable... well, not really, there should be more evil) # 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', 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', :scheme =>'http', :host =>'www.example.com.tw', :port => '80',
:path => '/~jdoe123/Help%20Me%20', :query => 'arg=val&arg2=val2', :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') :link_text => 'http://www.example.com.tw:80/~jdoe123/Help%20Me%20?arg=val&arg2=val2')
end end
def test_email_uri def test_email_uri
match(URIChunk, 'mail@example.com', match(URIChunk, 'mail@example.com',
:user => 'mail', :host => 'example.com', :link_text => 'mail@example.com' :user => 'mail', :host => 'example.com', :link_text => 'mail@example.com'
) )
end end
def test_non_email def test_non_email
# The @ is part of the normal text, but 'example.com' is marked up. # 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') match(URIChunk, 'Not an email: @example.com', :user => nil, :uri => 'http://example.com')
end end
def test_non_uri def test_non_uri
assert_conversion_does_not_apply URIChunk, 'httpd.conf' assert_conversion_does_not_apply URIChunk, 'httpd.conf'
assert_conversion_does_not_apply URIChunk, 'libproxy.so' assert_conversion_does_not_apply URIChunk, 'libproxy.so'
assert_conversion_does_not_apply URIChunk, 'ld.so.conf' assert_conversion_does_not_apply URIChunk, 'ld.so.conf'
end end
def test_uri_in_text def test_uri_in_text
match(URIChunk, 'Go to: http://www.example.com/', :host => 'www.example.com', :path =>'/') 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, 'http://www.example.com/ is a link.', :host => 'www.example.com')
match(URIChunk, match(URIChunk,
'Email david@loudthinking.com', 'Email david@loudthinking.com',
:scheme =>'mailto', :user =>'david', :host =>'loudthinking.com') :scheme =>'mailto', :user =>'david', :host =>'loudthinking.com')
# check that trailing punctuation is not included in the hostname # check that trailing punctuation is not included in the hostname
match(URIChunk, '"link":http://fake.link.com.', :scheme => 'http', :host => 'fake.link.com') match(URIChunk, '"link":http://fake.link.com.', :scheme => 'http', :host => 'fake.link.com')
end end
def test_uri_in_parentheses def test_uri_in_parentheses
match(URIChunk, 'URI (http://brackets.com.de) in brackets', :host => 'brackets.com.de') 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, 'because (as shown at research.net) the results', :host => 'research.net')
match(URIChunk, match(URIChunk,
'A wiki (http://wiki.org/wiki.cgi?WhatIsWiki) page', 'A wiki (http://wiki.org/wiki.cgi?WhatIsWiki) page',
:scheme => 'http', :host => 'wiki.org', :path => '/wiki.cgi', :query => 'WhatIsWiki' :scheme => 'http', :host => 'wiki.org', :path => '/wiki.cgi', :query => 'WhatIsWiki'
) )
end end
def test_uri_list_item def test_uri_list_item
match( match(
URIChunk, URIChunk,
'* http://www.btinternet.com/~mail2minh/SonyEricssonP80xPlatform.sis', '* http://www.btinternet.com/~mail2minh/SonyEricssonP80xPlatform.sis',
:path => '/~mail2minh/SonyEricssonP80xPlatform.sis' :path => '/~mail2minh/SonyEricssonP80xPlatform.sis'
) )
end end
def test_interesting_uri_with__comma def test_interesting_uri_with__comma
# Counter-intuitively, this URL matches, but the query part includes the trailing 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. # It has no way to know that the query does not include the comma.
match( match(
URIChunk, URIChunk,
"This text contains a URL http://someplace.org:8080/~person/stuff.cgi?arg=val, doesn't it?", "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', :scheme => 'http', :host => 'someplace.org', :port => '8080', :path => '/~person/stuff.cgi',
:query => 'arg=val,') :query => 'arg=val,')
end end
def test_local_urls def test_local_urls
# normal # normal
match(LocalURIChunk, 'http://perforce:8001/toto.html', match(LocalURIChunk, 'http://perforce:8001/toto.html',
:scheme => 'http', :host => 'perforce', :scheme => 'http', :host => 'perforce',
:port => '8001', :link_text => 'http://perforce:8001/toto.html') :port => '8001', :link_text => 'http://perforce:8001/toto.html')
# in parentheses # in parentheses
match(LocalURIChunk, 'URI (http://localhost:2500) in brackets', match(LocalURIChunk, 'URI (http://localhost:2500) in brackets',
:host => 'localhost', :port => '2500') :host => 'localhost', :port => '2500')
match(LocalURIChunk, 'because (as shown at http://perforce:8001) the results', match(LocalURIChunk, 'because (as shown at http://perforce:8001) the results',
:host => 'perforce', :port => '8001') :host => 'perforce', :port => '8001')
match(LocalURIChunk, match(LocalURIChunk,
'A wiki (http://localhost:2500/wiki.cgi?WhatIsWiki) page', 'A wiki (http://localhost:2500/wiki.cgi?WhatIsWiki) page',
:scheme => 'http', :host => 'localhost', :path => '/wiki.cgi', :scheme => 'http', :host => 'localhost', :path => '/wiki.cgi',
:port => '2500', :query => 'WhatIsWiki') :port => '2500', :query => 'WhatIsWiki')
end end
def assert_conversion_does_not_apply(chunk_type, str) def assert_conversion_does_not_apply(chunk_type, str)
processed_str = str.dup processed_str = str.dup
URIChunk.apply_to(processed_str) URIChunk.apply_to(processed_str)
assert_equal(str, processed_str) assert_equal(str, processed_str)
end end
end end

View file

@ -1,92 +1,92 @@
#!/bin/env ruby -w #!/bin/env ruby -w
require File.dirname(__FILE__) + '/../test_helper' require File.dirname(__FILE__) + '/../test_helper'
require 'url_rewriting_hack' require 'url_rewriting_hack'
class UrlRewritingHackTest < Test::Unit::TestCase class UrlRewritingHackTest < Test::Unit::TestCase
def test_parse_uri def test_parse_uri
assert_equal({:controller => 'wiki', :action => 'x', :web => nil}, assert_equal({:controller => 'wiki', :action => 'x', :web => nil},
DispatchServlet.parse_uri('/x/')) DispatchServlet.parse_uri('/x/'))
assert_equal({:web => 'x', :controller => 'wiki', :action => 'y'}, assert_equal({:web => 'x', :controller => 'wiki', :action => 'y'},
DispatchServlet.parse_uri('/x/y')) DispatchServlet.parse_uri('/x/y'))
assert_equal({:web => 'x', :controller => 'wiki', :action => 'y'}, assert_equal({:web => 'x', :controller => 'wiki', :action => 'y'},
DispatchServlet.parse_uri('/x/y/')) DispatchServlet.parse_uri('/x/y/'))
assert_equal({:web => 'x', :controller => 'wiki', :action => 'y', :id => 'z'}, assert_equal({:web => 'x', :controller => 'wiki', :action => 'y', :id => 'z'},
DispatchServlet.parse_uri('/x/y/z')) DispatchServlet.parse_uri('/x/y/z'))
assert_equal({:web => 'x', :controller => 'wiki', :action => 'y', :id => 'z'}, assert_equal({:web => 'x', :controller => 'wiki', :action => 'y', :id => 'z'},
DispatchServlet.parse_uri('/x/y/z/')) DispatchServlet.parse_uri('/x/y/z/'))
end end
def test_parse_uri_approot def test_parse_uri_approot
assert_equal({:controller => 'wiki', :action => 'index', :web => nil}, assert_equal({:controller => 'wiki', :action => 'index', :web => nil},
DispatchServlet.parse_uri('/wiki/')) DispatchServlet.parse_uri('/wiki/'))
end end
def test_parse_uri_interestng_cases def test_parse_uri_interestng_cases
assert_equal({:web => '_veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeery-long_web_', assert_equal({:web => '_veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeery-long_web_',
:controller => 'wiki', :controller => 'wiki',
:action => 'an_action', :id => 'HomePage' :action => 'an_action', :id => 'HomePage'
}, },
DispatchServlet.parse_uri( DispatchServlet.parse_uri(
'/_veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeery-long_web_/an_action/HomePage') '/_veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeery-long_web_/an_action/HomePage')
) )
assert_equal false, DispatchServlet.parse_uri('') assert_equal false, DispatchServlet.parse_uri('')
assert_equal false, DispatchServlet.parse_uri('//') assert_equal false, DispatchServlet.parse_uri('//')
assert_equal false, DispatchServlet.parse_uri('web') assert_equal false, DispatchServlet.parse_uri('web')
end end
def test_parse_uri_liberal_with_pagenames def test_parse_uri_liberal_with_pagenames
assert_equal({:controller => 'wiki', :web => 'web', :action => 'show', :id => '$HOME_PAGE'}, assert_equal({:controller => 'wiki', :web => 'web', :action => 'show', :id => '$HOME_PAGE'},
DispatchServlet.parse_uri('/web/show/$HOME_PAGE')) DispatchServlet.parse_uri('/web/show/$HOME_PAGE'))
assert_equal({:controller => 'wiki', :web => 'web', :action => 'show', assert_equal({:controller => 'wiki', :web => 'web', :action => 'show',
:id => 'HomePage/something_else'}, :id => 'HomePage/something_else'},
DispatchServlet.parse_uri('/web/show/HomePage/something_else')) DispatchServlet.parse_uri('/web/show/HomePage/something_else'))
assert_equal({:controller => 'wiki', :web => 'web', :action => 'show', assert_equal({:controller => 'wiki', :web => 'web', :action => 'show',
:id => 'HomePage?arg1=value1&arg2=value2'}, :id => 'HomePage?arg1=value1&arg2=value2'},
DispatchServlet.parse_uri('/web/show/HomePage?arg1=value1&arg2=value2')) DispatchServlet.parse_uri('/web/show/HomePage?arg1=value1&arg2=value2'))
assert_equal({:controller => 'wiki', :web => 'web', :action => 'show', assert_equal({:controller => 'wiki', :web => 'web', :action => 'show',
:id => 'Page+With+Spaces'}, :id => 'Page+With+Spaces'},
DispatchServlet.parse_uri('/web/show/Page+With+Spaces')) DispatchServlet.parse_uri('/web/show/Page+With+Spaces'))
end end
def test_url_rewriting def test_url_rewriting
request = ActionController::TestRequest.new request = ActionController::TestRequest.new
ur = ActionController::UrlRewriter.new(request, 'wiki', 'show') ur = ActionController::UrlRewriter.new(request, 'wiki', 'show')
assert_equal 'http://test.host/myweb/myaction', assert_equal 'http://test.host/myweb/myaction',
ur.rewrite(:web => 'myweb', :controller => 'wiki', :action => 'myaction') ur.rewrite(:web => 'myweb', :controller => 'wiki', :action => 'myaction')
assert_equal 'http://test.host/myOtherWeb/', assert_equal 'http://test.host/myOtherWeb/',
ur.rewrite(:web => 'myOtherWeb', :controller => 'wiki') ur.rewrite(:web => 'myOtherWeb', :controller => 'wiki')
assert_equal 'http://test.host/myaction', assert_equal 'http://test.host/myaction',
ur.rewrite(:controller => 'wiki', :action => 'myaction') ur.rewrite(:controller => 'wiki', :action => 'myaction')
assert_equal 'http://test.host/', assert_equal 'http://test.host/',
ur.rewrite(:controller => 'wiki') ur.rewrite(:controller => 'wiki')
end end
def test_controller_mapping def test_controller_mapping
request = ActionController::TestRequest.new request = ActionController::TestRequest.new
ur = ActionController::UrlRewriter.new(request, 'wiki', 'show') ur = ActionController::UrlRewriter.new(request, 'wiki', 'show')
assert_equal 'http://test.host/file', assert_equal 'http://test.host/file',
ur.rewrite(:controller => 'file', :action => 'file') ur.rewrite(:controller => 'file', :action => 'file')
assert_equal 'http://test.host/pic/abc.jpg', assert_equal 'http://test.host/pic/abc.jpg',
ur.rewrite(:controller => 'file', :action => 'pic', :id => 'abc.jpg') ur.rewrite(:controller => 'file', :action => 'pic', :id => 'abc.jpg')
assert_equal 'http://test.host/web/pic/abc.jpg', assert_equal 'http://test.host/web/pic/abc.jpg',
ur.rewrite(:web => 'web', :controller => 'file', :action => 'pic', :id => 'abc.jpg') ur.rewrite(:web => 'web', :controller => 'file', :action => 'pic', :id => 'abc.jpg')
# default option is wiki # default option is wiki
assert_equal 'http://test.host/unknown_action', assert_equal 'http://test.host/unknown_action',
ur.rewrite(:controller => 'wiki', :action => 'unknown_action') ur.rewrite(:controller => 'wiki', :action => 'unknown_action')
end end
end end

View file

@ -1,130 +1,130 @@
#!/bin/env ruby -w #!/bin/env ruby -w
require File.dirname(__FILE__) + '/../test_helper' require File.dirname(__FILE__) + '/../test_helper'
require 'wiki_service' require 'wiki_service'
class WebTest < Test::Unit::TestCase class WebTest < Test::Unit::TestCase
def setup def setup
@web = Web.new nil, 'Instiki', 'instiki' @web = Web.new nil, 'Instiki', 'instiki'
end end
def test_wiki_word_linking def test_wiki_word_linking
@web.add_page(Page.new(@web, 'SecondPage', 'Yo, yo. Have you EverBeenHated', Time.now, @web.add_page(Page.new(@web, 'SecondPage', 'Yo, yo. Have you EverBeenHated', Time.now,
'DavidHeinemeierHansson')) 'DavidHeinemeierHansson'))
assert_equal('<p>Yo, yo. Have you <span class="newWikiWord">Ever Been Hated' + assert_equal('<p>Yo, yo. Have you <span class="newWikiWord">Ever Been Hated' +
'<a href="../show/EverBeenHated">?</a></span></p>', '<a href="../show/EverBeenHated">?</a></span></p>',
@web.pages["SecondPage"].display_content) @web.pages["SecondPage"].display_content)
@web.add_page(Page.new(@web, 'EverBeenHated', 'Yo, yo. Have you EverBeenHated', Time.now, @web.add_page(Page.new(@web, 'EverBeenHated', 'Yo, yo. Have you EverBeenHated', Time.now,
'DavidHeinemeierHansson')) 'DavidHeinemeierHansson'))
assert_equal('<p>Yo, yo. Have you <a class="existingWikiWord" ' + assert_equal('<p>Yo, yo. Have you <a class="existingWikiWord" ' +
'href="../show/EverBeenHated">Ever Been Hated</a></p>', 'href="../show/EverBeenHated">Ever Been Hated</a></p>',
@web.pages['SecondPage'].display_content) @web.pages['SecondPage'].display_content)
end end
def test_pages_by_revision def test_pages_by_revision
add_sample_pages add_sample_pages
assert_equal 'EverBeenHated', @web.select.by_revision.first.name assert_equal 'EverBeenHated', @web.select.by_revision.first.name
end end
def test_pages_by_match def test_pages_by_match
add_sample_pages add_sample_pages
assert_equal 2, @web.select { |page| page.content =~ /me/i }.length assert_equal 2, @web.select { |page| page.content =~ /me/i }.length
assert_equal 1, @web.select { |page| page.content =~ /Who/i }.length assert_equal 1, @web.select { |page| page.content =~ /Who/i }.length
assert_equal 0, @web.select { |page| page.content =~ /none/i }.length assert_equal 0, @web.select { |page| page.content =~ /none/i }.length
end end
def test_references def test_references
add_sample_pages add_sample_pages
assert_equal 1, @web.select.pages_that_reference('EverBeenHated').length assert_equal 1, @web.select.pages_that_reference('EverBeenHated').length
assert_equal 0, @web.select.pages_that_reference('EverBeenInLove').length assert_equal 0, @web.select.pages_that_reference('EverBeenInLove').length
end end
def test_delete def test_delete
add_sample_pages add_sample_pages
assert_equal 2, @web.pages.length assert_equal 2, @web.pages.length
@web.remove_pages([ @web.pages['EverBeenInLove'] ]) @web.remove_pages([ @web.pages['EverBeenInLove'] ])
assert_equal 1, @web.pages.length assert_equal 1, @web.pages.length
end end
def test_make_link def test_make_link
add_sample_pages add_sample_pages
existing_page_wiki_url = existing_page_wiki_url =
'<a class="existingWikiWord" href="../show/EverBeenInLove">Ever Been In Love</a>' '<a class="existingWikiWord" href="../show/EverBeenInLove">Ever Been In Love</a>'
existing_page_published_url = existing_page_published_url =
'<a class="existingWikiWord" href="../published/EverBeenInLove">Ever Been In Love</a>' '<a class="existingWikiWord" href="../published/EverBeenInLove">Ever Been In Love</a>'
existing_page_static_url = existing_page_static_url =
'<a class="existingWikiWord" href="EverBeenInLove.html">Ever Been In Love</a>' '<a class="existingWikiWord" href="EverBeenInLove.html">Ever Been In Love</a>'
new_page_wiki_url = new_page_wiki_url =
'<span class="newWikiWord">Unknown Word<a href="../show/UnknownWord">?</a></span>' '<span class="newWikiWord">Unknown Word<a href="../show/UnknownWord">?</a></span>'
new_page_published_url = new_page_published_url =
new_page_static_url = new_page_static_url =
'<span class="newWikiWord">Unknown Word</span>' '<span class="newWikiWord">Unknown Word</span>'
# no options # no options
assert_equal existing_page_wiki_url, @web.make_link('EverBeenInLove') assert_equal existing_page_wiki_url, @web.make_link('EverBeenInLove')
# :mode => :export # :mode => :export
assert_equal existing_page_static_url, @web.make_link('EverBeenInLove', nil, :mode => :export) assert_equal existing_page_static_url, @web.make_link('EverBeenInLove', nil, :mode => :export)
# :mode => :publish # :mode => :publish
assert_equal existing_page_published_url, assert_equal existing_page_published_url,
@web.make_link('EverBeenInLove', nil, :mode => :publish) @web.make_link('EverBeenInLove', nil, :mode => :publish)
# new page, no options # new page, no options
assert_equal new_page_wiki_url, @web.make_link('UnknownWord') assert_equal new_page_wiki_url, @web.make_link('UnknownWord')
# new page, :mode => :export # new page, :mode => :export
assert_equal new_page_static_url, @web.make_link('UnknownWord', nil, :mode => :export) assert_equal new_page_static_url, @web.make_link('UnknownWord', nil, :mode => :export)
# new page, :mode => :publish # new page, :mode => :publish
assert_equal new_page_published_url, @web.make_link('UnknownWord', nil, :mode => :publish) assert_equal new_page_published_url, @web.make_link('UnknownWord', nil, :mode => :publish)
# Escaping special characters in the name # Escaping special characters in the name
assert_equal( assert_equal(
'<span class="newWikiWord">Smith &amp; Wesson<a href="../show/Smith+%26+Wesson">?</a></span>', '<span class="newWikiWord">Smith &amp; Wesson<a href="../show/Smith+%26+Wesson">?</a></span>',
@web.make_link('Smith & Wesson')) @web.make_link('Smith & Wesson'))
# optionally using text as the link text # optionally using text as the link text
assert_equal( assert_equal(
existing_page_published_url.sub(/>Ever Been In Love</, ">Haven't you ever been in love?<"), 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)) @web.make_link('EverBeenInLove', "Haven't you ever been in love?", :mode => :publish))
end end
def test_initialize def test_initialize
wiki_stub = Object.new wiki_stub = Object.new
web = Web.new(wiki_stub, 'Wiki2', 'wiki2', '123') web = Web.new(wiki_stub, 'Wiki2', 'wiki2', '123')
assert_equal wiki_stub, web.wiki assert_equal wiki_stub, web.wiki
assert_equal 'Wiki2', web.name assert_equal 'Wiki2', web.name
assert_equal 'wiki2', web.address assert_equal 'wiki2', web.address
assert_equal '123', web.password assert_equal '123', web.password
# new web should be set for maximum features enabled # new web should be set for maximum features enabled
assert_equal :textile, web.markup assert_equal :textile, web.markup
assert_equal '008B26', web.color assert_equal '008B26', web.color
assert !web.safe_mode assert !web.safe_mode
assert_equal {}, web.pages assert_equal {}, web.pages
assert web.allow_uploads assert web.allow_uploads
assert_equal @wiki, web.parent_wiki assert_equal @wiki, web.parent_wiki
assert_nil web.additional_style assert_nil web.additional_style
assert !web.published assert !web.published
assert !web.brackets_only assert !web.brackets_only
assert !web.count_pages assert !web.count_pages
assert web.allow_uploads assert web.allow_uploads
end end
private private
def add_sample_pages def add_sample_pages
@web.add_page(Page.new(@web, 'EverBeenInLove', 'Who am I me', @web.add_page(Page.new(@web, 'EverBeenInLove', 'Who am I me',
Time.local(2004, 4, 4, 16, 50), 'DavidHeinemeierHansson')) Time.local(2004, 4, 4, 16, 50), 'DavidHeinemeierHansson'))
@web.add_page(Page.new(@web, 'EverBeenHated', 'I am me EverBeenHated', @web.add_page(Page.new(@web, 'EverBeenHated', 'I am me EverBeenHated',
Time.local(2004, 4, 4, 16, 51), 'DavidHeinemeierHansson')) Time.local(2004, 4, 4, 16, 51), 'DavidHeinemeierHansson'))
end end
end end

View file

@ -1,116 +1,116 @@
#!/bin/env ruby -w #!/bin/env ruby -w
require File.dirname(__FILE__) + '/../test_helper' require File.dirname(__FILE__) + '/../test_helper'
require 'wiki_service' require 'wiki_service'
require 'fileutils' require 'fileutils'
class WikiServiceTest < Test::Unit::TestCase class WikiServiceTest < Test::Unit::TestCase
# Clean the test storage directory before the run # Clean the test storage directory before the run
unless defined? @@storage_cleaned unless defined? @@storage_cleaned
FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/*.command_log']) FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/*.command_log'])
FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/*.snapshot']) FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/*.snapshot'])
FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/*.tex']) FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/*.tex'])
FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/*.zip']) FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/*.zip'])
FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/*.pdf']) FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/*.pdf'])
FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/instiki/*']) FileUtils.rm(Dir[RAILS_ROOT + '/storage/test/instiki/*'])
@@cleaned_storage = true @@cleaned_storage = true
WikiService.instance.setup('pswd', 'Wiki', 'wiki') WikiService.instance.setup('pswd', 'Wiki', 'wiki')
end end
def setup def setup
@s = WikiService.instance @s = WikiService.instance
@s.create_web 'Instiki', 'instiki' @s.create_web 'Instiki', 'instiki'
@web = @s.webs['instiki'] @web = @s.webs['instiki']
end end
def teardown def teardown
@s.delete_web 'instiki' @s.delete_web 'instiki'
end end
def test_read_write_page def test_read_write_page
@s.write_page 'instiki', 'FirstPage', "Electric shocks, I love 'em", @s.write_page 'instiki', 'FirstPage', "Electric shocks, I love 'em",
Time.now, 'DavidHeinemeierHansson' Time.now, 'DavidHeinemeierHansson'
assert_equal "Electric shocks, I love 'em", @s.read_page('instiki', 'FirstPage').content assert_equal "Electric shocks, I love 'em", @s.read_page('instiki', 'FirstPage').content
end end
def test_read_only_operations def test_read_only_operations
@s.write_page 'instiki', 'TestReadOnlyOperations', 'Read only operations dont change the' + @s.write_page 'instiki', 'TestReadOnlyOperations', 'Read only operations dont change the' +
'state of any object, and therefore should not be logged by Madeleine!', 'state of any object, and therefore should not be logged by Madeleine!',
Time.now, 'AlexeyVerkhovsky' Time.now, 'AlexeyVerkhovsky'
assert_doesnt_change_state_or_log :authenticate, 'pswd' assert_doesnt_change_state_or_log :authenticate, 'pswd'
assert_doesnt_change_state_or_log :read_page, 'instiki', 'TestReadOnlyOperations' assert_doesnt_change_state_or_log :read_page, 'instiki', 'TestReadOnlyOperations'
assert_doesnt_change_state_or_log :setup? assert_doesnt_change_state_or_log :setup?
assert_doesnt_change_state_or_log :webs assert_doesnt_change_state_or_log :webs
@s.write_page 'instiki', 'FirstPage', "Electric shocks, I love 'em", @s.write_page 'instiki', 'FirstPage', "Electric shocks, I love 'em",
Time.now, 'DavidHeinemeierHansson' Time.now, 'DavidHeinemeierHansson'
assert_equal "Electric shocks, I love 'em", @s.read_page('instiki', 'FirstPage').content assert_equal "Electric shocks, I love 'em", @s.read_page('instiki', 'FirstPage').content
end end
def test_aborted_transaction def test_aborted_transaction
@s.write_page 'instiki', 'FirstPage', "Electric shocks, I love 'em", @s.write_page 'instiki', 'FirstPage', "Electric shocks, I love 'em",
10.minutes.ago, 'DavidHeinemeierHansson' 10.minutes.ago, 'DavidHeinemeierHansson'
assert_doesnt_change_state('revise_page with unchanged content') { assert_doesnt_change_state('revise_page with unchanged content') {
begin begin
@s.revise_page 'instiki', 'FirstPage', "Electric shocks, I love 'em", @s.revise_page 'instiki', 'FirstPage', "Electric shocks, I love 'em",
Time.now, 'DavidHeinemeierHansson' Time.now, 'DavidHeinemeierHansson'
fail 'Expected Instiki::ValidationError not raised' fail 'Expected Instiki::ValidationError not raised'
rescue Instiki::ValidationError rescue Instiki::ValidationError
end end
} }
end end
def test_file_yard def test_file_yard
file_yard = @s.file_yard(@web) file_yard = @s.file_yard(@web)
assert_equal FileYard, file_yard.class assert_equal FileYard, file_yard.class
assert_equal(@s.storage_path + '/instiki', file_yard.files_path) assert_equal(@s.storage_path + '/instiki', file_yard.files_path)
end end
# Checks that a method call or a block doesn;t change the persisted state of the wiki # Checks that a method call or a block doesn;t change the persisted state of the wiki
# Usage: # Usage:
# assert_doesnt_change_state :read_page, 'instiki', 'TestReadOnlyOperations' # assert_doesnt_change_state :read_page, 'instiki', 'TestReadOnlyOperations'
# or # or
# assert_doesnt_change_state {|wiki| wiki.webs} # assert_doesnt_change_state {|wiki| wiki.webs}
def assert_doesnt_change_state(method, *args, &block) def assert_doesnt_change_state(method, *args, &block)
_assert_doesnt_change_state(including_command_log = false, method, *args, &block) _assert_doesnt_change_state(including_command_log = false, method, *args, &block)
end end
# Same as assert_doesnt_change_state, but also asserts that no vommand log is generated # 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) def assert_doesnt_change_state_or_log(method, *args, &block)
_assert_doesnt_change_state(including_command_log = true, method, *args, &block) _assert_doesnt_change_state(including_command_log = true, method, *args, &block)
end end
private private
def _assert_doesnt_change_state(including_log, method, *args) def _assert_doesnt_change_state(including_log, method, *args)
WikiService.snapshot WikiService.snapshot
last_snapshot_before = last_snapshot last_snapshot_before = last_snapshot
if block_given? if block_given?
yield @s yield @s
else else
@s.send(method, *args) @s.send(method, *args)
end end
if including_log if including_log
command_logs = Dir[RAILS_ROOT + 'storage/test/*.command_log'] command_logs = Dir[RAILS_ROOT + 'storage/test/*.command_log']
assert command_logs.empty?, "Calls to #{method} should not be logged" assert command_logs.empty?, "Calls to #{method} should not be logged"
end end
last_snapshot_after = last_snapshot last_snapshot_after = last_snapshot
assert last_snapshot_before == last_snapshot_after, assert last_snapshot_before == last_snapshot_after,
'Calls to #{method} should not change the state of any persisted object' 'Calls to #{method} should not change the state of any persisted object'
end end
def last_snapshot def last_snapshot
snapshots = Dir[RAILS_ROOT + '/storage/test/*.snapshot'] snapshots = Dir[RAILS_ROOT + '/storage/test/*.snapshot']
assert !snapshots.empty?, "No snapshots found at #{RAILS_ROOT}/storage/test/" assert !snapshots.empty?, "No snapshots found at #{RAILS_ROOT}/storage/test/"
File.read(snapshots.last) File.read(snapshots.last)
end end
end end

View file

@ -1,14 +1,14 @@
#!/bin/env ruby -w #!/bin/env ruby -w
require File.dirname(__FILE__) + '/../test_helper' require File.dirname(__FILE__) + '/../test_helper'
require 'wiki_words' require 'wiki_words'
class WikiWordsTest < Test::Unit::TestCase class WikiWordsTest < Test::Unit::TestCase
def test_utf8_characters_in_wiki_word 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 "ÆÅØ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 "Æe ÅØle Øen", WikiWords.separate("ÆeÅØleØen")
assert_equal "Legetøj", WikiWords.separate("Legetøj") assert_equal "Legetøj", WikiWords.separate("Legetøj")
end end
end end

View file

@ -1,99 +1,99 @@
BlueCloth BlueCloth
========= =========
Version 1.0.0 - 2004/08/24 Version 1.0.0 - 2004/08/24
Original version by John Gruber <http://daringfireball.net/>. Original version by John Gruber <http://daringfireball.net/>.
Ruby port by Michael Granger <http://www.deveiate.org/>. Ruby port by Michael Granger <http://www.deveiate.org/>.
BlueCloth is a Ruby implementation of [Markdown][1], a text-to-HTML conversion 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 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 write using an easy-to-read, easy-to-write plain text format, then convert it to
structurally valid XHTML (or HTML). structurally valid XHTML (or HTML).
It borrows a naming convention and several helpings of interface from 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 [Redcloth][2], [Why the Lucky Stiff][3]'s processor for a similar text-to-HTML
conversion syntax called [Textile][4]. conversion syntax called [Textile][4].
Installation Installation
------------ ------------
You can install this module either by running the included `install.rb` script, 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. or by simply copying `lib/bluecloth.rb` to a directory in your load path.
Dependencies Dependencies
------------ ------------
BlueCloth uses the `StringScanner` class from the `strscan` library, which comes 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 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. versions, and the `logger` library, which is also included in 1.8.x and later.
Example Usage Example Usage
------------- -------------
The BlueCloth class is a subclass of Ruby's String, and can be used thusly: The BlueCloth class is a subclass of Ruby's String, and can be used thusly:
bc = BlueCloth::new( str ) bc = BlueCloth::new( str )
puts bc.to_html puts bc.to_html
This `README` file is an example of Markdown syntax. The sample program 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) `bluecloth` in the `bin/` directory can be used to convert this (or any other)
file with Markdown syntax into HTML: file with Markdown syntax into HTML:
$ bin/bluecloth README > README.html $ bin/bluecloth README > README.html
Acknowledgements Acknowledgements
---------------- ----------------
This library is a port of the canonical Perl one, and so owes most of its 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 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 certainly an artifact of my porting it and not an artifact of the excellent code
from which it is derived. from which it is derived.
It also, as mentioned before, borrows its API liberally from RedCloth, both for 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 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 code and peerless prose have been an inspiration to me, and this module is
intended as the sincerest flattery. intended as the sincerest flattery.
Also contributing to any success this module may enjoy are those among my peers 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 who have taken the time to help out, either by submitting patches, testing, or
offering suggestions and review: offering suggestions and review:
* Martin Chase <stillflame@FaerieMUD.org> * Martin Chase <stillflame@FaerieMUD.org>
* Florian Gross <flgr@ccan.de> * Florian Gross <flgr@ccan.de>
Author/Legal Author/Legal
------------ ------------
Original version: Original version:
Copyright (c) 2003-2004 John Gruber Copyright (c) 2003-2004 John Gruber
<http://daringfireball.net/> <http://daringfireball.net/>
All rights reserved. All rights reserved.
Ruby version: Ruby version:
Copyright (c) 2004 The FaerieMUD Consortium Copyright (c) 2004 The FaerieMUD Consortium
BlueCloth is free software; you can redistribute it and/or modify it under the 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 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 Foundation; either version 2 of the License, or (at your option) any later
version. version.
BlueCloth is distributed in the hope that it will be useful, but WITHOUT ANY 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 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details. PARTICULAR PURPOSE. See the GNU General Public License for more details.
[1]: http://daringfireball.net/projects/markdown/ [1]: http://daringfireball.net/projects/markdown/
[2]: http://www.whytheluckystiff.net/ruby/redcloth/ [2]: http://www.whytheluckystiff.net/ruby/redcloth/
[3]: http://www.whytheluckystiff.net/ [3]: http://www.whytheluckystiff.net/
[4]: http://www.textism.com/tools/textile/ [4]: http://www.textism.com/tools/textile/
$Id: README,v 1.1 2005/01/07 23:01:51 alexeyv Exp $ $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 $ $URL: svn+ssh://svn.FaerieMUD.org/usr/local/svn/BlueCloth/trunk/README $

View file

@ -1,150 +1,150 @@
#!/usr/bin/ruby #!/usr/bin/ruby
# #
# BlueCloth Module Install Script # BlueCloth Module Install Script
# $Id: install.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $ # $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. # Thanks to Masatoshi SEKI for ideas found in his install.rb.
# #
# Copyright (c) 2001-2004 The FaerieMUD Consortium. # Copyright (c) 2001-2004 The FaerieMUD Consortium.
# #
# This is free software. You may use, modify, and/or redistribute this # This is free software. You may use, modify, and/or redistribute this
# software under the terms of the Perl Artistic License. (See # software under the terms of the Perl Artistic License. (See
# http://language.perl.com/misc/Artistic.html) # http://language.perl.com/misc/Artistic.html)
# #
require './utils.rb' require './utils.rb'
include UtilityFunctions include UtilityFunctions
require 'rbconfig' require 'rbconfig'
include Config include Config
require 'find' require 'find'
require 'ftools' require 'ftools'
$version = %q$Revision: 1.1 $ $version = %q$Revision: 1.1 $
$rcsId = %q$Id: install.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $ $rcsId = %q$Id: install.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
# Define required libraries # Define required libraries
RequiredLibraries = [ RequiredLibraries = [
# libraryname, nice name, RAA URL, Download URL # libraryname, nice name, RAA URL, Download URL
[ 'strscan', "StrScan", [ 'strscan', "StrScan",
'http://raa.ruby-lang.org/list.rhtml?name=strscan', 'http://raa.ruby-lang.org/list.rhtml?name=strscan',
'http://i.loveruby.net/archive/strscan/strscan-0.6.7.tar.gz' ], 'http://i.loveruby.net/archive/strscan/strscan-0.6.7.tar.gz' ],
[ 'logger', "Devel-Logger", [ 'logger', "Devel-Logger",
'http://raa.ruby-lang.org/list.rhtml?name=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' ], 'http://rrr.jin.gr.jp/download/devel-logger-1_2_2.tar.gz' ],
] ]
class Installer class Installer
@@PrunePatterns = [ @@PrunePatterns = [
/CVS/, /CVS/,
/~$/, /~$/,
%r:(^|/)\.:, %r:(^|/)\.:,
/\.tpl$/, /\.tpl$/,
] ]
def initialize( testing=false ) def initialize( testing=false )
@ftools = (testing) ? self : File @ftools = (testing) ? self : File
end end
### Make the specified dirs (which can be a String or an Array of Strings) ### Make the specified dirs (which can be a String or an Array of Strings)
### with the specified mode. ### with the specified mode.
def makedirs( dirs, mode=0755, verbose=false ) def makedirs( dirs, mode=0755, verbose=false )
dirs = [ dirs ] unless dirs.is_a? Array dirs = [ dirs ] unless dirs.is_a? Array
oldumask = File::umask oldumask = File::umask
File::umask( 0777 - mode ) File::umask( 0777 - mode )
for dir in dirs for dir in dirs
if @ftools == File if @ftools == File
File::mkpath( dir, $verbose ) File::mkpath( dir, $verbose )
else else
$stderr.puts "Make path %s with mode %o" % [ dir, mode ] $stderr.puts "Make path %s with mode %o" % [ dir, mode ]
end end
end end
File::umask( oldumask ) File::umask( oldumask )
end end
def install( srcfile, dstfile, mode=nil, verbose=false ) def install( srcfile, dstfile, mode=nil, verbose=false )
dstfile = File.catname(srcfile, dstfile) dstfile = File.catname(srcfile, dstfile)
unless FileTest.exist? dstfile and File.cmp srcfile, dstfile unless FileTest.exist? dstfile and File.cmp srcfile, dstfile
$stderr.puts " install #{srcfile} -> #{dstfile}" $stderr.puts " install #{srcfile} -> #{dstfile}"
else else
$stderr.puts " skipping #{dstfile}: unchanged" $stderr.puts " skipping #{dstfile}: unchanged"
end end
end end
public public
def installFiles( src, dstDir, mode=0444, verbose=false ) def installFiles( src, dstDir, mode=0444, verbose=false )
directories = [] directories = []
files = [] files = []
if File.directory?( src ) if File.directory?( src )
Find.find( src ) {|f| Find.find( src ) {|f|
Find.prune if @@PrunePatterns.find {|pat| f =~ pat} Find.prune if @@PrunePatterns.find {|pat| f =~ pat}
next if f == src next if f == src
if FileTest.directory?( f ) if FileTest.directory?( f )
directories << f.gsub( /^#{src}#{File::Separator}/, '' ) directories << f.gsub( /^#{src}#{File::Separator}/, '' )
next next
elsif FileTest.file?( f ) elsif FileTest.file?( f )
files << f.gsub( /^#{src}#{File::Separator}/, '' ) files << f.gsub( /^#{src}#{File::Separator}/, '' )
else else
Find.prune Find.prune
end end
} }
else else
files << File.basename( src ) files << File.basename( src )
src = File.dirname( src ) src = File.dirname( src )
end end
dirs = [ dstDir ] dirs = [ dstDir ]
dirs |= directories.collect {|d| File.join(dstDir,d)} dirs |= directories.collect {|d| File.join(dstDir,d)}
makedirs( dirs, 0755, verbose ) makedirs( dirs, 0755, verbose )
files.each {|f| files.each {|f|
srcfile = File.join(src,f) srcfile = File.join(src,f)
dstfile = File.dirname(File.join( dstDir,f )) dstfile = File.dirname(File.join( dstDir,f ))
if verbose if verbose
if mode if mode
$stderr.puts "Install #{srcfile} -> #{dstfile} (mode %o)" % mode $stderr.puts "Install #{srcfile} -> #{dstfile} (mode %o)" % mode
else else
$stderr.puts "Install #{srcfile} -> #{dstfile}" $stderr.puts "Install #{srcfile} -> #{dstfile}"
end end
end end
@ftools.install( srcfile, dstfile, mode, verbose ) @ftools.install( srcfile, dstfile, mode, verbose )
} }
end end
end end
if $0 == __FILE__ if $0 == __FILE__
header "BlueCloth Installer #$version" header "BlueCloth Installer #$version"
for lib in RequiredLibraries for lib in RequiredLibraries
testForRequiredLibrary( *lib ) testForRequiredLibrary( *lib )
end end
viewOnly = ARGV.include? '-n' viewOnly = ARGV.include? '-n'
verbose = ARGV.include? '-v' verbose = ARGV.include? '-v'
debugMsg "Sitelibdir = '#{CONFIG['sitelibdir']}'" debugMsg "Sitelibdir = '#{CONFIG['sitelibdir']}'"
sitelibdir = CONFIG['sitelibdir'] sitelibdir = CONFIG['sitelibdir']
debugMsg "Sitearchdir = '#{CONFIG['sitearchdir']}'" debugMsg "Sitearchdir = '#{CONFIG['sitearchdir']}'"
sitearchdir = CONFIG['sitearchdir'] sitearchdir = CONFIG['sitearchdir']
message "Installing\n" message "Installing\n"
i = Installer.new( viewOnly ) i = Installer.new( viewOnly )
i.installFiles( "lib", sitelibdir, 0444, verbose ) i.installFiles( "lib", sitelibdir, 0444, verbose )
end end

File diff suppressed because it is too large Load diff

View file

@ -1,117 +1,117 @@
#!/usr/bin/ruby #!/usr/bin/ruby
# #
# Test suite for BlueCloth classes # Test suite for BlueCloth classes
# $Id: test.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $ # $Id: test.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
# #
BEGIN { BEGIN {
$basedir = File::dirname( __FILE__ ) $basedir = File::dirname( __FILE__ )
["lib", "tests", "redist"].each do |subdir| ["lib", "tests", "redist"].each do |subdir|
$LOAD_PATH.unshift File::join( $basedir, subdir ) $LOAD_PATH.unshift File::join( $basedir, subdir )
end end
require "#{$basedir}/utils" require "#{$basedir}/utils"
include UtilityFunctions include UtilityFunctions
} }
verboseOff { verboseOff {
require 'bctestcase' require 'bctestcase'
require 'find' require 'find'
require 'test/unit' require 'test/unit'
require 'test/unit/testsuite' require 'test/unit/testsuite'
require 'test/unit/ui/console/testrunner' require 'test/unit/ui/console/testrunner'
require 'optparse' require 'optparse'
} }
# Turn off output buffering # Turn off output buffering
$stderr.sync = $stdout.sync = true $stderr.sync = $stdout.sync = true
$DebugPattern = nil $DebugPattern = nil
# Initialize variables # Initialize variables
safelevel = 0 safelevel = 0
patterns = [] patterns = []
requires = [] requires = []
# Parse command-line switches # Parse command-line switches
ARGV.options {|oparser| ARGV.options {|oparser|
oparser.banner = "Usage: #$0 [options] [TARGETS]\n" oparser.banner = "Usage: #$0 [options] [TARGETS]\n"
oparser.on( "--debug[=PATTERN]", "-d[=PATTERN]", String, oparser.on( "--debug[=PATTERN]", "-d[=PATTERN]", String,
"Turn debugging on (for tests which match PATTERN)" ) {|arg| "Turn debugging on (for tests which match PATTERN)" ) {|arg|
if arg if arg
$DebugPattern = Regexp::new( arg ) $DebugPattern = Regexp::new( arg )
puts "Turned debugging on for %p." % $DebugPattern puts "Turned debugging on for %p." % $DebugPattern
else else
$DEBUG = true $DEBUG = true
debugMsg "Turned debugging on globally." debugMsg "Turned debugging on globally."
end end
} }
oparser.on( "--verbose", "-v", TrueClass, "Make progress verbose" ) { oparser.on( "--verbose", "-v", TrueClass, "Make progress verbose" ) {
$VERBOSE = true $VERBOSE = true
debugMsg "Turned verbose on." debugMsg "Turned verbose on."
} }
# Handle the 'help' option # Handle the 'help' option
oparser.on( "--help", "-h", "Display this text." ) { oparser.on( "--help", "-h", "Display this text." ) {
$stderr.puts oparser $stderr.puts oparser
exit!(0) exit!(0)
} }
oparser.parse! oparser.parse!
} }
# Parse test patterns # Parse test patterns
ARGV.each {|pat| patterns << Regexp::new( pat, Regexp::IGNORECASE )} ARGV.each {|pat| patterns << Regexp::new( pat, Regexp::IGNORECASE )}
$stderr.puts "#{patterns.length} patterns given on the command line" $stderr.puts "#{patterns.length} patterns given on the command line"
### Load all the tests from the tests dir ### Load all the tests from the tests dir
Find.find("#{$basedir}/tests") {|file| Find.find("#{$basedir}/tests") {|file|
Find.prune if /\/\./ =~ file or /~$/ =~ file Find.prune if /\/\./ =~ file or /~$/ =~ file
Find.prune if /TEMPLATE/ =~ file Find.prune if /TEMPLATE/ =~ file
next if File.stat( file ).directory? next if File.stat( file ).directory?
unless patterns.empty? unless patterns.empty?
Find.prune unless patterns.find {|pat| pat =~ file} Find.prune unless patterns.find {|pat| pat =~ file}
end end
debugMsg "Considering '%s': " % file debugMsg "Considering '%s': " % file
next unless file =~ /\.tests.rb$/ next unless file =~ /\.tests.rb$/
debugMsg "Requiring '%s'..." % file debugMsg "Requiring '%s'..." % file
require "#{file}" require "#{file}"
requires << file requires << file
} }
$stderr.puts "Required #{requires.length} files." $stderr.puts "Required #{requires.length} files."
unless patterns.empty? unless patterns.empty?
$stderr.puts "[" + requires.sort.join( ", " ) + "]" $stderr.puts "[" + requires.sort.join( ", " ) + "]"
end end
# Build the test suite # Build the test suite
class BlueClothTests class BlueClothTests
class << self class << self
def suite def suite
suite = Test::Unit::TestSuite.new( "BlueCloth" ) suite = Test::Unit::TestSuite.new( "BlueCloth" )
if suite.respond_to?( :add ) if suite.respond_to?( :add )
ObjectSpace.each_object( Class ) {|klass| ObjectSpace.each_object( Class ) {|klass|
suite.add( klass.suite ) if klass < BlueCloth::TestCase suite.add( klass.suite ) if klass < BlueCloth::TestCase
} }
else else
ObjectSpace.each_object( Class ) {|klass| ObjectSpace.each_object( Class ) {|klass|
suite << klass.suite if klass < BlueCloth::TestCase suite << klass.suite if klass < BlueCloth::TestCase
} }
end end
return suite return suite
end end
end end
end end
# Run tests # Run tests
$SAFE = safelevel $SAFE = safelevel
Test::Unit::UI::Console::TestRunner.new( BlueClothTests ).start Test::Unit::UI::Console::TestRunner.new( BlueClothTests ).start

View file

@ -1,71 +1,71 @@
#!/usr/bin/ruby #!/usr/bin/ruby
# #
# Unit test for the BlueCloth class object # Unit test for the BlueCloth class object
# $Id: 00_Class.tests.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $ # $Id: 00_Class.tests.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
# #
# Copyright (c) 2004 The FaerieMUD Consortium. # Copyright (c) 2004 The FaerieMUD Consortium.
# #
if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase ) if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
basedir = File::dirname( __FILE__ ) basedir = File::dirname( __FILE__ )
require File::join( basedir, 'bctestcase' ) require File::join( basedir, 'bctestcase' )
end end
### This test case tests ... ### This test case tests ...
class BlueClothClassTestCase < BlueCloth::TestCase class BlueClothClassTestCase < BlueCloth::TestCase
TestString = "foo" TestString = "foo"
def test_00_class_constant def test_00_class_constant
printTestHeader "BlueCloth: Class Constant" printTestHeader "BlueCloth: Class Constant"
assert Object::constants.include?( "BlueCloth" ), assert Object::constants.include?( "BlueCloth" ),
"No BlueCloth constant in Object" "No BlueCloth constant in Object"
assert_instance_of Class, BlueCloth assert_instance_of Class, BlueCloth
end end
def test_01_instantiation def test_01_instantiation
printTestHeader "BlueCloth: Instantiation" printTestHeader "BlueCloth: Instantiation"
rval = nil rval = nil
# With no argument... ("") # With no argument... ("")
assert_nothing_raised { assert_nothing_raised {
rval = BlueCloth::new rval = BlueCloth::new
} }
assert_instance_of BlueCloth, rval assert_instance_of BlueCloth, rval
assert_kind_of String, rval assert_kind_of String, rval
assert_equal "", rval assert_equal "", rval
# String argument # String argument
assert_nothing_raised { assert_nothing_raised {
rval = BlueCloth::new TestString rval = BlueCloth::new TestString
} }
assert_instance_of BlueCloth, rval assert_instance_of BlueCloth, rval
assert_kind_of String, rval assert_kind_of String, rval
assert_equal TestString, rval assert_equal TestString, rval
addSetupBlock { addSetupBlock {
debugMsg "Creating a new BlueCloth" debugMsg "Creating a new BlueCloth"
@obj = BlueCloth::new( TestString ) @obj = BlueCloth::new( TestString )
} }
addTeardownBlock { addTeardownBlock {
@obj = nil @obj = nil
} }
end end
def test_02_duplication def test_02_duplication
printTestHeader "BlueCloth: Duplication" printTestHeader "BlueCloth: Duplication"
rval = nil rval = nil
assert_nothing_raised { assert_nothing_raised {
rval = @obj.dup rval = @obj.dup
} }
assert_instance_of BlueCloth, rval assert_instance_of BlueCloth, rval
assert_kind_of String, rval assert_kind_of String, rval
assert_equal TestString, rval assert_equal TestString, rval
end end
end end

File diff suppressed because it is too large Load diff

View file

@ -1,57 +1,57 @@
#!/usr/bin/ruby #!/usr/bin/ruby
# #
# Unit test for bugs found in BlueCloth # Unit test for bugs found in BlueCloth
# $Id: 10_Bug.tests.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $ # $Id: 10_Bug.tests.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
# #
# Copyright (c) 2004 The FaerieMUD Consortium. # Copyright (c) 2004 The FaerieMUD Consortium.
# #
if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase ) if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
basedir = File::dirname( __FILE__ ) basedir = File::dirname( __FILE__ )
require File::join( basedir, 'bctestcase' ) require File::join( basedir, 'bctestcase' )
end end
require 'timeout' require 'timeout'
### This test case tests ... ### This test case tests ...
class BugsTestCase < BlueCloth::TestCase class BugsTestCase < BlueCloth::TestCase
BaseDir = File::dirname( File::dirname(File::expand_path( __FILE__ )) ) BaseDir = File::dirname( File::dirname(File::expand_path( __FILE__ )) )
### Test to be sure the README file can be transformed. ### Test to be sure the README file can be transformed.
def test_00_slow_block_regex def test_00_slow_block_regex
contents = File::read( File::join(BaseDir,"README") ) contents = File::read( File::join(BaseDir,"README") )
bcobj = BlueCloth::new( contents ) bcobj = BlueCloth::new( contents )
assert_nothing_raised { assert_nothing_raised {
timeout( 2 ) do timeout( 2 ) do
bcobj.to_html bcobj.to_html
end end
} }
end end
### :TODO: Add more documents and test their transforms. ### :TODO: Add more documents and test their transforms.
def test_10_regexp_engine_overflow_bug def test_10_regexp_engine_overflow_bug
contents = File::read( File::join(BaseDir,"tests/data/re-overflow.txt") ) contents = File::read( File::join(BaseDir,"tests/data/re-overflow.txt") )
bcobj = BlueCloth::new( contents ) bcobj = BlueCloth::new( contents )
assert_nothing_raised { assert_nothing_raised {
bcobj.to_html bcobj.to_html
} }
end end
def test_15_regexp_engine_overflow_bug2 def test_15_regexp_engine_overflow_bug2
contents = File::read( File::join(BaseDir,"tests/data/re-overflow2.txt") ) contents = File::read( File::join(BaseDir,"tests/data/re-overflow2.txt") )
bcobj = BlueCloth::new( contents ) bcobj = BlueCloth::new( contents )
assert_nothing_raised { assert_nothing_raised {
bcobj.to_html bcobj.to_html
} }
end end
end end
__END__ __END__

Some files were not shown because too many files have changed in this diff Show more