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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View file

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View file

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

View file

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

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

@ -1,16 +1,16 @@
<div id="TextileHelp" style="float: right; width: 250px; margin-top: 5px">
<h3>Markdown formatting tips (<a target="_new" href="http://daringfireball.net/projects/markdown/syntax">advanced</a>)</h3>
<table cellspacing="0" cellpadding="0">
<tr><td>_your text_</td><td class="arrow">&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>`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>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>***</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>![Alt text](URL)</td><td class="arrow">&rarr;</td><td>Image</td></tr>
</table>
<%= render 'wiki_words_help' %>
<div id="TextileHelp" style="float: right; width: 250px; margin-top: 5px">
<h3>Markdown formatting tips (<a target="_new" href="http://daringfireball.net/projects/markdown/syntax">advanced</a>)</h3>
<table cellspacing="0" cellpadding="0">
<tr><td>_your text_</td><td class="arrow">&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>`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>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>***</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>![Alt text](URL)</td><td class="arrow">&rarr;</td><td>Image</td></tr>
</table>
<%= render 'wiki_words_help' %>
</div>

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

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

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

@ -1,16 +1,16 @@
<div id="TextileHelp" style="float: right; width: 250px; margin-top: 5px">
<h3>RDoc formatting tips (<a target="_new" href="http://rdoc.sourceforge.net/doc/files/markup/simple_markup_rb.html">advanced</a>)</h3>
<table cellspacing="0" cellpadding="0">
<tr><td>_your text_</td><td class="arrow">&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>* 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>+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>[[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>imageURL</td><td class="arrow">&rarr;</td><td>Image</td></tr>
</table>
<%= render 'wiki_words_help' %>
<div id="TextileHelp" style="float: right; width: 250px; margin-top: 5px">
<h3>RDoc formatting tips (<a target="_new" href="http://rdoc.sourceforge.net/doc/files/markup/simple_markup_rb.html">advanced</a>)</h3>
<table cellspacing="0" cellpadding="0">
<tr><td>_your text_</td><td class="arrow">&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>* 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>+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>[[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>imageURL</td><td class="arrow">&rarr;</td><td>Image</td></tr>
</table>
<%= render 'wiki_words_help' %>
</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">
<h3>Textile formatting tips (<a href="#" onClick="quickRedReference(); return false;">advanced</a>)</h3>
<table cellspacing="0" cellpadding="0">
<tr><td>_your text_</td><td class="arrow">&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>%{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># 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>|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>!imageURL!</td><td class="arrow">&rarr;</td><td>Image</td></tr>
</table>
<%= render 'wiki_words_help' %>
</div>
<script language="JavaScript">
function quickRedReference() {
window.open(
"http://hobix.com/textile/quick.html",
"redRef",
"height=600,width=550,channelmode=0,dependent=0," +
"directories=0,fullscreen=0,location=0,menubar=0," +
"resizable=0,scrollbars=1,status=1,toolbar=0"
);
}
<div id="TextileHelp" style="float: right; width: 250px; margin-top: 5px">
<h3>Textile formatting tips (<a href="#" onClick="quickRedReference(); return false;">advanced</a>)</h3>
<table cellspacing="0" cellpadding="0">
<tr><td>_your text_</td><td class="arrow">&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>%{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># 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>|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>!imageURL!</td><td class="arrow">&rarr;</td><td>Image</td></tr>
</table>
<%= render 'wiki_words_help' %>
</div>
<script language="JavaScript">
function quickRedReference() {
window.open(
"http://hobix.com/textile/quick.html",
"redRef",
"height=600,width=550,channelmode=0,dependent=0," +
"directories=0,fullscreen=0,location=0,menubar=0," +
"resizable=0,scrollbars=1,status=1,toolbar=0"
);
}
</script>

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

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

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

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

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

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

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

@ -1,12 +1,12 @@
<% @title = "Export" %>
<p>You can export all the pages in this web as a zip file in either HTML (with working links and all) or the pure markup (to import in another wiki).</p>
<ul id="feedsList">
<li><a href="../export_html">HTML</a>
<li><a href="../export_markup">Markup (<%= @web.markup %>)</a>
<% if OPTIONS[:pdflatex] && @web.markup == :textile %>
<li><a href="../export_tex">TeX</a>
<li><a href="../export_pdf">PDF</a>
<% end %>
</ul>
<% @title = "Export" %>
<p>You can export all the pages in this web as a zip file in either HTML (with working links and all) or the pure markup (to import in another wiki).</p>
<ul id="feedsList">
<li><a href="../export_html">HTML</a>
<li><a href="../export_markup">Markup (<%= @web.markup %>)</a>
<% if OPTIONS[:pdflatex] && @web.markup == :textile %>
<li><a href="../export_tex">TeX</a>
<li><a href="../export_pdf">PDF</a>
<% end %>
</ul>

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

@ -1,8 +1,8 @@
<% @title = "Feeds" %>
<p>You can subscribe to this wiki by RSS and get either just the headlines of the pages that change or the entire page.</p>
<ul id="feedsList">
<li><a href="../rss_with_content<%= "?password=#{web.password}" if @web.password %>">Full content (RSS 2.0)</a>
<li><a href="../rss_with_headlines<%= "?password=#{web.password}" if @web.password %>">Headlines (RSS 2.0)</a>
</ul>
<% @title = "Feeds" %>
<p>You can subscribe to this wiki by RSS and get either just the headlines of the pages that change or the entire page.</p>
<ul id="feedsList">
<li><a href="../rss_with_content<%= "?password=#{web.password}" if @web.password %>">Full content (RSS 2.0)</a>
<li><a href="../rss_with_headlines<%= "?password=#{web.password}" if @web.password %>">Headlines (RSS 2.0)</a>
</ul>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -1,13 +1,13 @@
<% @title = @results.length > 0 ? "#{@results.length} pages contains \"#{@params["query"]}\"" : "No pages contains \"#{@query}\"" %>
<% if @results.length > 0 %>
<ul>
<% for page in @results %>
<li><a href="../show/<%= page.name %>"><%= page.plain_name %></a></li>
<% end %>
</ul>
<% else %>
<p>Perhaps you should try expanding your query. Remember that Instiki searches for entire phrases, so if you search for "all that jazz" it will not match pages that contain these words in separation&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>
<% end %>
<% @title = @results.length > 0 ? "#{@results.length} pages contains \"#{@params["query"]}\"" : "No pages contains \"#{@query}\"" %>
<% if @results.length > 0 %>
<ul>
<% for page in @results %>
<li><a href="../show/<%= page.name %>"><%= page.plain_name %></a></li>
<% end %>
</ul>
<% else %>
<p>Perhaps you should try expanding your query. Remember that Instiki searches for entire phrases, so if you search for "all that jazz" it will not match pages that contain these words in separation&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>
<% end %>

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

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

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

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

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

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

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

@ -1,9 +1,9 @@
<h3>Wiki words</h3>
<p style="border-top: 1px dotted #ccc; margin-top: 0px">
Two or more uppercase words stuck together (camel case) or any phrase surrounded by double
brackets is a wiki word. A camel-case wiki word can be escaped by putting \ in front of it.
</p>
<p>
Wiki words: <i>HomePage, ThreeWordsTogether, [[C++]], [[Let's play again!]]</i><br/>
Not wiki words: <i>IBM, School</i>
</p>
<h3>Wiki words</h3>
<p style="border-top: 1px dotted #ccc; margin-top: 0px">
Two or more uppercase words stuck together (camel case) or any phrase surrounded by double
brackets is a wiki word. A camel-case wiki word can be escaped by putting \ in front of it.
</p>
<p>
Wiki words: <i>HomePage, ThreeWordsTogether, [[C++]], [[Let's play again!]]</i><br/>
Not wiki words: <i>IBM, School</i>
</p>