Massive change of SVN properties to deal with EOL style problem
This commit is contained in:
parent
b747b611b3
commit
3b6566577c
108 changed files with 12417 additions and 12417 deletions
|
@ -1,71 +1,71 @@
|
|||
require 'fileutils'
|
||||
require 'application'
|
||||
require 'instiki_errors'
|
||||
|
||||
class FileController < ApplicationController
|
||||
|
||||
layout 'default'
|
||||
|
||||
before_filter :check_allow_uploads
|
||||
|
||||
def file
|
||||
check_path
|
||||
|
||||
if @params['file']
|
||||
# form supplied
|
||||
file_yard.upload_file(@file_name, @params['file'])
|
||||
flash[:info] = "File '#{@file_name}' successfully uploaded"
|
||||
@web.refresh_pages_with_references(@file_name)
|
||||
return_to_last_remembered
|
||||
elsif file_yard.has_file?(@file_name)
|
||||
send_file(file_yard.file_path(@file_name))
|
||||
else
|
||||
logger.debug("File not found: #{file_yard.files_path}/#{@file_name}")
|
||||
# go to the template, which is a file upload form
|
||||
end
|
||||
end
|
||||
|
||||
def cancel_upload
|
||||
return_to_last_remembered
|
||||
end
|
||||
|
||||
def pic
|
||||
check_path
|
||||
if @params['file']
|
||||
# form supplied
|
||||
file_yard.upload_file(@file_name, @params['file'])
|
||||
flash[:info] = "Image '#{@file_name}' successfully uploaded"
|
||||
@web.refresh_pages_with_references(@file_name)
|
||||
return_to_last_remembered
|
||||
elsif file_yard.has_file?(@file_name)
|
||||
send_file(file_yard.file_path(@file_name))
|
||||
else
|
||||
logger.debug("Image not found: #{file_yard.files_path}/#{@file_name}")
|
||||
render_action 'file'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
|
||||
def check_allow_uploads
|
||||
unless @web.allow_uploads
|
||||
render_text 'File uploads are blocked by the webmaster', '403 Forbidden'
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def check_path
|
||||
raise Instiki::ValidationError.new("Invalid path: no file name") unless @file_name
|
||||
raise Instiki::ValidationError.new("Invalid path: no web name") unless @web_name
|
||||
raise Instiki::ValidationError.new("Invalid path: unknown web name") unless @web
|
||||
end
|
||||
|
||||
def file_yard
|
||||
@wiki.file_yard(@web)
|
||||
end
|
||||
|
||||
end
|
||||
require 'fileutils'
|
||||
require 'application'
|
||||
require 'instiki_errors'
|
||||
|
||||
class FileController < ApplicationController
|
||||
|
||||
layout 'default'
|
||||
|
||||
before_filter :check_allow_uploads
|
||||
|
||||
def file
|
||||
check_path
|
||||
|
||||
if @params['file']
|
||||
# form supplied
|
||||
file_yard.upload_file(@file_name, @params['file'])
|
||||
flash[:info] = "File '#{@file_name}' successfully uploaded"
|
||||
@web.refresh_pages_with_references(@file_name)
|
||||
return_to_last_remembered
|
||||
elsif file_yard.has_file?(@file_name)
|
||||
send_file(file_yard.file_path(@file_name))
|
||||
else
|
||||
logger.debug("File not found: #{file_yard.files_path}/#{@file_name}")
|
||||
# go to the template, which is a file upload form
|
||||
end
|
||||
end
|
||||
|
||||
def cancel_upload
|
||||
return_to_last_remembered
|
||||
end
|
||||
|
||||
def pic
|
||||
check_path
|
||||
if @params['file']
|
||||
# form supplied
|
||||
file_yard.upload_file(@file_name, @params['file'])
|
||||
flash[:info] = "Image '#{@file_name}' successfully uploaded"
|
||||
@web.refresh_pages_with_references(@file_name)
|
||||
return_to_last_remembered
|
||||
elsif file_yard.has_file?(@file_name)
|
||||
send_file(file_yard.file_path(@file_name))
|
||||
else
|
||||
logger.debug("Image not found: #{file_yard.files_path}/#{@file_name}")
|
||||
render_action 'file'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
|
||||
def check_allow_uploads
|
||||
unless @web.allow_uploads
|
||||
render_text 'File uploads are blocked by the webmaster', '403 Forbidden'
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def check_path
|
||||
raise Instiki::ValidationError.new("Invalid path: no file name") unless @file_name
|
||||
raise Instiki::ValidationError.new("Invalid path: no web name") unless @web_name
|
||||
raise Instiki::ValidationError.new("Invalid path: unknown web name") unless @web
|
||||
end
|
||||
|
||||
def file_yard
|
||||
@wiki.file_yard(@web)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
766
app/controllers/wiki_controller.rb
Executable file → Normal file
766
app/controllers/wiki_controller.rb
Executable file → Normal file
|
@ -1,383 +1,383 @@
|
|||
require 'application'
|
||||
require 'fileutils'
|
||||
require 'redcloth_for_tex'
|
||||
|
||||
class WikiController < ApplicationController
|
||||
|
||||
layout 'default', :except => [:rss_feed, :rss_with_headlines, :tex, :export_tex, :export_html]
|
||||
|
||||
def index
|
||||
if @web_name
|
||||
redirect_show 'HomePage'
|
||||
elsif not @wiki.setup?
|
||||
redirect_to :action => 'new_system'
|
||||
elsif @wiki.webs.length == 1
|
||||
redirect_show 'HomePage', @wiki.webs.values.first.address
|
||||
else
|
||||
redirect_to :action => 'web_list'
|
||||
end
|
||||
end
|
||||
|
||||
# Administrating the Instiki setup --------------------------------------------
|
||||
|
||||
def create_system
|
||||
@wiki.setup(@params['password'], @params['web_name'], @params['web_address']) unless @wiki.setup?
|
||||
redirect_show('HomePage', @params['web_address'])
|
||||
end
|
||||
|
||||
def create_web
|
||||
if @wiki.authenticate(@params['system_password'])
|
||||
@wiki.create_web(@params['name'], @params['address'])
|
||||
redirect_show('HomePage', @params['address'])
|
||||
else
|
||||
redirect_to :action => 'index'
|
||||
end
|
||||
end
|
||||
|
||||
def edit_web
|
||||
# to template
|
||||
end
|
||||
|
||||
def new_system
|
||||
redirect_to(:action => 'index') if wiki.setup?
|
||||
# otherwise, to template
|
||||
end
|
||||
|
||||
def new_web
|
||||
redirect_to :action => 'index' if wiki.system['password'].nil?
|
||||
# otherwise, to template
|
||||
end
|
||||
|
||||
|
||||
# Outside a single web --------------------------------------------------------
|
||||
|
||||
def authenticate
|
||||
if password_check(@params['password'])
|
||||
redirect_show('HomePage')
|
||||
else
|
||||
redirect_to :action => 'login'
|
||||
end
|
||||
end
|
||||
|
||||
def login
|
||||
# to template
|
||||
end
|
||||
|
||||
def web_list
|
||||
@webs = wiki.webs.values.sort_by { |web| web.name }
|
||||
end
|
||||
|
||||
|
||||
# Within a single web ---------------------------------------------------------
|
||||
|
||||
def authors
|
||||
@authors = @web.select.authors
|
||||
end
|
||||
|
||||
def export_html
|
||||
export_pages_as_zip('html') { |page| @page = page; render_to_string 'wiki/print' }
|
||||
end
|
||||
|
||||
def export_markup
|
||||
export_pages_as_zip(@web.markup) { |page| page.content }
|
||||
end
|
||||
|
||||
def export_pdf
|
||||
file_name = "#{@web.address}-tex-#{@web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')}"
|
||||
file_path = @wiki.storage_path + file_name
|
||||
|
||||
export_web_to_tex "#{file_path}.tex" unless FileTest.exists? "#{file_path}.tex"
|
||||
convert_tex_to_pdf "#{file_path}.tex"
|
||||
send_file "#{file_path}.pdf"
|
||||
end
|
||||
|
||||
def export_tex
|
||||
file_name = "#{@web.address}-tex-#{@web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')}.tex"
|
||||
file_path = @wiki.storage_path + file_name
|
||||
|
||||
export_web_to_tex(file_path) unless FileTest.exists?(file_path)
|
||||
send_file file_path
|
||||
end
|
||||
|
||||
def feeds
|
||||
# to template
|
||||
end
|
||||
|
||||
def list
|
||||
parse_category
|
||||
@pages_by_name = @pages_in_category.by_name
|
||||
@page_names_that_are_wanted = @pages_in_category.wanted_pages
|
||||
@pages_that_are_orphaned = @pages_in_category.orphaned_pages
|
||||
end
|
||||
|
||||
def recently_revised
|
||||
parse_category
|
||||
@pages_by_revision = @pages_in_category.by_revision
|
||||
end
|
||||
|
||||
def remove_orphaned_pages
|
||||
if wiki.authenticate(@params['system_password'])
|
||||
wiki.remove_orphaned_pages(@web_name)
|
||||
redirect_to :action => 'list'
|
||||
else
|
||||
redirect_show 'HomePage'
|
||||
end
|
||||
end
|
||||
|
||||
def rss_with_content
|
||||
render_rss
|
||||
end
|
||||
|
||||
def rss_with_headlines
|
||||
render_rss(hide_description = true)
|
||||
end
|
||||
|
||||
def search
|
||||
@query = @params['query']
|
||||
@results = @web.select { |page| page.content =~ /#{@query}/i }.sort
|
||||
redirect_show(@results.first.name) if @results.length == 1
|
||||
end
|
||||
|
||||
def update_web
|
||||
if wiki.authenticate(@params['system_password'])
|
||||
wiki.update_web(
|
||||
@web.address, @params['address'], @params['name'],
|
||||
@params['markup'].intern,
|
||||
@params['color'], @params['additional_style'],
|
||||
@params['safe_mode'] ? true : false,
|
||||
@params['password'].empty? ? nil : @params['password'],
|
||||
@params['published'] ? true : false,
|
||||
@params['brackets_only'] ? true : false,
|
||||
@params['count_pages'] ? true : false,
|
||||
@params['allow_uploads'] ? true : false
|
||||
)
|
||||
redirect_show('HomePage', @params['address'])
|
||||
else
|
||||
redirect_show('HomePage')
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Within a single page --------------------------------------------------------
|
||||
|
||||
def cancel_edit
|
||||
@page.unlock
|
||||
redirect_show
|
||||
end
|
||||
|
||||
def edit
|
||||
if @page.nil?
|
||||
redirect_to :action => 'index'
|
||||
elsif @page.locked?(Time.now) and not @params['break_lock']
|
||||
redirect_to :web => @web_name, :action => 'locked', :id => @page_name
|
||||
else
|
||||
@page.lock(Time.now, @author)
|
||||
end
|
||||
end
|
||||
|
||||
def locked
|
||||
# to template
|
||||
end
|
||||
|
||||
def new
|
||||
# to template
|
||||
end
|
||||
|
||||
def pdf
|
||||
page = wiki.read_page(@web_name, @page_name)
|
||||
safe_page_name = @page.name.gsub(/\W/, '')
|
||||
file_name = "#{safe_page_name}-#{@web.address}-#{@page.created_at.strftime('%Y-%m-%d-%H-%M-%S')}"
|
||||
file_path = @wiki.storage_path + file_name
|
||||
|
||||
export_page_to_tex("#{file_path}.tex") unless FileTest.exists?("#{file_path}.tex")
|
||||
# NB: this is _very_ slow
|
||||
convert_tex_to_pdf("#{file_path}.tex")
|
||||
send_file "#{file_path}.pdf"
|
||||
end
|
||||
|
||||
def print
|
||||
# to template
|
||||
end
|
||||
|
||||
def published
|
||||
if @web.published
|
||||
@page = wiki.read_page(@web_name, @page_name || 'HomePage')
|
||||
else
|
||||
redirect_show('HomePage')
|
||||
end
|
||||
end
|
||||
|
||||
def revision
|
||||
get_page_and_revision
|
||||
end
|
||||
|
||||
def rollback
|
||||
get_page_and_revision
|
||||
end
|
||||
|
||||
def save
|
||||
redirect_to :action => 'index' if @page_name.nil?
|
||||
cookies['author'] = @params['author']
|
||||
|
||||
begin
|
||||
page = @web.pages[@page_name]
|
||||
if @web.pages[@page_name]
|
||||
wiki.revise_page(
|
||||
@web_name, @page_name, @params['content'], Time.now,
|
||||
Author.new(@params['author'], remote_ip)
|
||||
)
|
||||
page.unlock
|
||||
else
|
||||
wiki.write_page(
|
||||
@web_name, @page_name, @params['content'], Time.now,
|
||||
Author.new(@params['author'], remote_ip)
|
||||
)
|
||||
end
|
||||
redirect_show(@page_name)
|
||||
rescue Instiki::ValidationError => e
|
||||
page.unlock if defined? page
|
||||
flash[:error] = e
|
||||
return_to_last_remembered
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
if @page
|
||||
begin
|
||||
render_action 'page'
|
||||
# TODO this rescue should differentiate between errors due to rendering and errors in
|
||||
# the application itself (for application errors, it's better not to rescue the error at all)
|
||||
rescue => e
|
||||
logger.error e
|
||||
if in_a_web?
|
||||
redirect_to :web => @web_name, :action => 'edit',
|
||||
:action_suffix => "#{CGI.escape(@page_name)}?msg=#{CGI.escape(e.message)}"
|
||||
else
|
||||
raise e
|
||||
end
|
||||
end
|
||||
else
|
||||
redirect_to :web => @web_name, :action => 'new', :id => CGI.escape(@page_name)
|
||||
end
|
||||
end
|
||||
|
||||
def tex
|
||||
@tex_content = RedClothForTex.new(@page.content).to_tex
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def convert_tex_to_pdf(tex_path)
|
||||
# TODO remove earlier PDF files with the same prefix
|
||||
# TODO handle gracefully situation where pdflatex is not available
|
||||
logger.info `pdflatex --interaction=nonstopmode --output-directory #{File.dirname(tex_path)} #{File.basename(tex_path)}`
|
||||
end
|
||||
|
||||
def export_page_to_tex(file_path)
|
||||
tex
|
||||
File.open(file_path, 'w') { |f| f.write(render_to_string('wiki/tex')) }
|
||||
end
|
||||
|
||||
def export_pages_as_zip(file_type, &block)
|
||||
|
||||
file_prefix = "#{@web.address}-#{file_type}-"
|
||||
timestamp = @web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')
|
||||
file_path = @wiki.storage_path + file_prefix + timestamp + '.zip'
|
||||
tmp_path = "#{file_path}.tmp"
|
||||
|
||||
Zip::ZipOutputStream.open(tmp_path) do |zip_out|
|
||||
@web.select.by_name.each do |page|
|
||||
zip_out.put_next_entry("#{page.name}.#{file_type}")
|
||||
zip_out.puts(block.call(page))
|
||||
end
|
||||
# add an index file, if exporting to HTML
|
||||
if file_type.to_s.downcase == 'html'
|
||||
zip_out.put_next_entry 'index.html'
|
||||
zip_out.puts <<-EOL
|
||||
<html>
|
||||
<head>
|
||||
<META HTTP-EQUIV="Refresh" CONTENT="0;URL=HomePage.#{file_type}">
|
||||
</head>
|
||||
</html>
|
||||
EOL
|
||||
end
|
||||
end
|
||||
FileUtils.rm_rf(Dir[@wiki.storage_path + file_prefix + '*.zip'])
|
||||
FileUtils.mv(tmp_path, file_path)
|
||||
send_file file_path
|
||||
end
|
||||
|
||||
def export_web_to_tex(file_path)
|
||||
@tex_content = table_of_contents(@web.pages['HomePage'].content.dup, render_tex_web)
|
||||
File.open(file_path, 'w') { |f| f.write(render_to_string('wiki/tex_web')) }
|
||||
end
|
||||
|
||||
def get_page_and_revision
|
||||
@revision = @page.revisions[@params['rev'].to_i]
|
||||
end
|
||||
|
||||
def parse_category
|
||||
@categories = @web.categories
|
||||
@category = @params['category']
|
||||
if @categories.include?(@category)
|
||||
@pages_in_category = @web.select { |page| page.in_category?(@category) }
|
||||
@set_name = "category '#{@category}'"
|
||||
else
|
||||
@pages_in_category = PageSet.new(@web).by_name
|
||||
@set_name = 'the web'
|
||||
end
|
||||
@category_links = @categories.map { |c|
|
||||
if @category == c
|
||||
%{<span class="selected">#{c}</span>}
|
||||
else
|
||||
%{<a href="?category=#{c}">#{c}</a>}
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def password_check(password)
|
||||
if password == @web.password
|
||||
cookies['web_address'] = password
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def redirect_show(page_name = @page_name, web = @web_name)
|
||||
redirect_to :web => web, :action => 'show', :id => CGI.escape(page_name)
|
||||
end
|
||||
|
||||
def remote_ip
|
||||
ip = @request.remote_ip
|
||||
logger.info(ip)
|
||||
ip
|
||||
end
|
||||
|
||||
def render_rss(hide_description = false)
|
||||
@pages_by_revision = @web.select.by_revision.first(15)
|
||||
@hide_description = hide_description
|
||||
@response.headers['Content-Type'] = 'text/xml'
|
||||
render 'wiki/rss_feed'
|
||||
end
|
||||
|
||||
def render_tex_web
|
||||
@web.select.by_name.inject({}) do |tex_web, page|
|
||||
tex_web[page.name] = RedClothForTex.new(page.content).to_tex
|
||||
tex_web
|
||||
end
|
||||
end
|
||||
|
||||
def render_to_string(template_name)
|
||||
add_variables_to_assigns
|
||||
render template_name
|
||||
@performed_render = false
|
||||
@template.render_file(template_name)
|
||||
end
|
||||
|
||||
def truncate(text, length = 30, truncate_string = '...')
|
||||
if text.length > length then text[0..(length - 3)] + truncate_string else text end
|
||||
end
|
||||
|
||||
end
|
||||
require 'application'
|
||||
require 'fileutils'
|
||||
require 'redcloth_for_tex'
|
||||
|
||||
class WikiController < ApplicationController
|
||||
|
||||
layout 'default', :except => [:rss_feed, :rss_with_headlines, :tex, :export_tex, :export_html]
|
||||
|
||||
def index
|
||||
if @web_name
|
||||
redirect_show 'HomePage'
|
||||
elsif not @wiki.setup?
|
||||
redirect_to :action => 'new_system'
|
||||
elsif @wiki.webs.length == 1
|
||||
redirect_show 'HomePage', @wiki.webs.values.first.address
|
||||
else
|
||||
redirect_to :action => 'web_list'
|
||||
end
|
||||
end
|
||||
|
||||
# Administrating the Instiki setup --------------------------------------------
|
||||
|
||||
def create_system
|
||||
@wiki.setup(@params['password'], @params['web_name'], @params['web_address']) unless @wiki.setup?
|
||||
redirect_show('HomePage', @params['web_address'])
|
||||
end
|
||||
|
||||
def create_web
|
||||
if @wiki.authenticate(@params['system_password'])
|
||||
@wiki.create_web(@params['name'], @params['address'])
|
||||
redirect_show('HomePage', @params['address'])
|
||||
else
|
||||
redirect_to :action => 'index'
|
||||
end
|
||||
end
|
||||
|
||||
def edit_web
|
||||
# to template
|
||||
end
|
||||
|
||||
def new_system
|
||||
redirect_to(:action => 'index') if wiki.setup?
|
||||
# otherwise, to template
|
||||
end
|
||||
|
||||
def new_web
|
||||
redirect_to :action => 'index' if wiki.system['password'].nil?
|
||||
# otherwise, to template
|
||||
end
|
||||
|
||||
|
||||
# Outside a single web --------------------------------------------------------
|
||||
|
||||
def authenticate
|
||||
if password_check(@params['password'])
|
||||
redirect_show('HomePage')
|
||||
else
|
||||
redirect_to :action => 'login'
|
||||
end
|
||||
end
|
||||
|
||||
def login
|
||||
# to template
|
||||
end
|
||||
|
||||
def web_list
|
||||
@webs = wiki.webs.values.sort_by { |web| web.name }
|
||||
end
|
||||
|
||||
|
||||
# Within a single web ---------------------------------------------------------
|
||||
|
||||
def authors
|
||||
@authors = @web.select.authors
|
||||
end
|
||||
|
||||
def export_html
|
||||
export_pages_as_zip('html') { |page| @page = page; render_to_string 'wiki/print' }
|
||||
end
|
||||
|
||||
def export_markup
|
||||
export_pages_as_zip(@web.markup) { |page| page.content }
|
||||
end
|
||||
|
||||
def export_pdf
|
||||
file_name = "#{@web.address}-tex-#{@web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')}"
|
||||
file_path = @wiki.storage_path + file_name
|
||||
|
||||
export_web_to_tex "#{file_path}.tex" unless FileTest.exists? "#{file_path}.tex"
|
||||
convert_tex_to_pdf "#{file_path}.tex"
|
||||
send_file "#{file_path}.pdf"
|
||||
end
|
||||
|
||||
def export_tex
|
||||
file_name = "#{@web.address}-tex-#{@web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')}.tex"
|
||||
file_path = @wiki.storage_path + file_name
|
||||
|
||||
export_web_to_tex(file_path) unless FileTest.exists?(file_path)
|
||||
send_file file_path
|
||||
end
|
||||
|
||||
def feeds
|
||||
# to template
|
||||
end
|
||||
|
||||
def list
|
||||
parse_category
|
||||
@pages_by_name = @pages_in_category.by_name
|
||||
@page_names_that_are_wanted = @pages_in_category.wanted_pages
|
||||
@pages_that_are_orphaned = @pages_in_category.orphaned_pages
|
||||
end
|
||||
|
||||
def recently_revised
|
||||
parse_category
|
||||
@pages_by_revision = @pages_in_category.by_revision
|
||||
end
|
||||
|
||||
def remove_orphaned_pages
|
||||
if wiki.authenticate(@params['system_password'])
|
||||
wiki.remove_orphaned_pages(@web_name)
|
||||
redirect_to :action => 'list'
|
||||
else
|
||||
redirect_show 'HomePage'
|
||||
end
|
||||
end
|
||||
|
||||
def rss_with_content
|
||||
render_rss
|
||||
end
|
||||
|
||||
def rss_with_headlines
|
||||
render_rss(hide_description = true)
|
||||
end
|
||||
|
||||
def search
|
||||
@query = @params['query']
|
||||
@results = @web.select { |page| page.content =~ /#{@query}/i }.sort
|
||||
redirect_show(@results.first.name) if @results.length == 1
|
||||
end
|
||||
|
||||
def update_web
|
||||
if wiki.authenticate(@params['system_password'])
|
||||
wiki.update_web(
|
||||
@web.address, @params['address'], @params['name'],
|
||||
@params['markup'].intern,
|
||||
@params['color'], @params['additional_style'],
|
||||
@params['safe_mode'] ? true : false,
|
||||
@params['password'].empty? ? nil : @params['password'],
|
||||
@params['published'] ? true : false,
|
||||
@params['brackets_only'] ? true : false,
|
||||
@params['count_pages'] ? true : false,
|
||||
@params['allow_uploads'] ? true : false
|
||||
)
|
||||
redirect_show('HomePage', @params['address'])
|
||||
else
|
||||
redirect_show('HomePage')
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Within a single page --------------------------------------------------------
|
||||
|
||||
def cancel_edit
|
||||
@page.unlock
|
||||
redirect_show
|
||||
end
|
||||
|
||||
def edit
|
||||
if @page.nil?
|
||||
redirect_to :action => 'index'
|
||||
elsif @page.locked?(Time.now) and not @params['break_lock']
|
||||
redirect_to :web => @web_name, :action => 'locked', :id => @page_name
|
||||
else
|
||||
@page.lock(Time.now, @author)
|
||||
end
|
||||
end
|
||||
|
||||
def locked
|
||||
# to template
|
||||
end
|
||||
|
||||
def new
|
||||
# to template
|
||||
end
|
||||
|
||||
def pdf
|
||||
page = wiki.read_page(@web_name, @page_name)
|
||||
safe_page_name = @page.name.gsub(/\W/, '')
|
||||
file_name = "#{safe_page_name}-#{@web.address}-#{@page.created_at.strftime('%Y-%m-%d-%H-%M-%S')}"
|
||||
file_path = @wiki.storage_path + file_name
|
||||
|
||||
export_page_to_tex("#{file_path}.tex") unless FileTest.exists?("#{file_path}.tex")
|
||||
# NB: this is _very_ slow
|
||||
convert_tex_to_pdf("#{file_path}.tex")
|
||||
send_file "#{file_path}.pdf"
|
||||
end
|
||||
|
||||
def print
|
||||
# to template
|
||||
end
|
||||
|
||||
def published
|
||||
if @web.published
|
||||
@page = wiki.read_page(@web_name, @page_name || 'HomePage')
|
||||
else
|
||||
redirect_show('HomePage')
|
||||
end
|
||||
end
|
||||
|
||||
def revision
|
||||
get_page_and_revision
|
||||
end
|
||||
|
||||
def rollback
|
||||
get_page_and_revision
|
||||
end
|
||||
|
||||
def save
|
||||
redirect_to :action => 'index' if @page_name.nil?
|
||||
cookies['author'] = @params['author']
|
||||
|
||||
begin
|
||||
page = @web.pages[@page_name]
|
||||
if @web.pages[@page_name]
|
||||
wiki.revise_page(
|
||||
@web_name, @page_name, @params['content'], Time.now,
|
||||
Author.new(@params['author'], remote_ip)
|
||||
)
|
||||
page.unlock
|
||||
else
|
||||
wiki.write_page(
|
||||
@web_name, @page_name, @params['content'], Time.now,
|
||||
Author.new(@params['author'], remote_ip)
|
||||
)
|
||||
end
|
||||
redirect_show(@page_name)
|
||||
rescue Instiki::ValidationError => e
|
||||
page.unlock if defined? page
|
||||
flash[:error] = e
|
||||
return_to_last_remembered
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
if @page
|
||||
begin
|
||||
render_action 'page'
|
||||
# TODO this rescue should differentiate between errors due to rendering and errors in
|
||||
# the application itself (for application errors, it's better not to rescue the error at all)
|
||||
rescue => e
|
||||
logger.error e
|
||||
if in_a_web?
|
||||
redirect_to :web => @web_name, :action => 'edit',
|
||||
:action_suffix => "#{CGI.escape(@page_name)}?msg=#{CGI.escape(e.message)}"
|
||||
else
|
||||
raise e
|
||||
end
|
||||
end
|
||||
else
|
||||
redirect_to :web => @web_name, :action => 'new', :id => CGI.escape(@page_name)
|
||||
end
|
||||
end
|
||||
|
||||
def tex
|
||||
@tex_content = RedClothForTex.new(@page.content).to_tex
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def convert_tex_to_pdf(tex_path)
|
||||
# TODO remove earlier PDF files with the same prefix
|
||||
# TODO handle gracefully situation where pdflatex is not available
|
||||
logger.info `pdflatex --interaction=nonstopmode --output-directory #{File.dirname(tex_path)} #{File.basename(tex_path)}`
|
||||
end
|
||||
|
||||
def export_page_to_tex(file_path)
|
||||
tex
|
||||
File.open(file_path, 'w') { |f| f.write(render_to_string('wiki/tex')) }
|
||||
end
|
||||
|
||||
def export_pages_as_zip(file_type, &block)
|
||||
|
||||
file_prefix = "#{@web.address}-#{file_type}-"
|
||||
timestamp = @web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')
|
||||
file_path = @wiki.storage_path + file_prefix + timestamp + '.zip'
|
||||
tmp_path = "#{file_path}.tmp"
|
||||
|
||||
Zip::ZipOutputStream.open(tmp_path) do |zip_out|
|
||||
@web.select.by_name.each do |page|
|
||||
zip_out.put_next_entry("#{page.name}.#{file_type}")
|
||||
zip_out.puts(block.call(page))
|
||||
end
|
||||
# add an index file, if exporting to HTML
|
||||
if file_type.to_s.downcase == 'html'
|
||||
zip_out.put_next_entry 'index.html'
|
||||
zip_out.puts <<-EOL
|
||||
<html>
|
||||
<head>
|
||||
<META HTTP-EQUIV="Refresh" CONTENT="0;URL=HomePage.#{file_type}">
|
||||
</head>
|
||||
</html>
|
||||
EOL
|
||||
end
|
||||
end
|
||||
FileUtils.rm_rf(Dir[@wiki.storage_path + file_prefix + '*.zip'])
|
||||
FileUtils.mv(tmp_path, file_path)
|
||||
send_file file_path
|
||||
end
|
||||
|
||||
def export_web_to_tex(file_path)
|
||||
@tex_content = table_of_contents(@web.pages['HomePage'].content.dup, render_tex_web)
|
||||
File.open(file_path, 'w') { |f| f.write(render_to_string('wiki/tex_web')) }
|
||||
end
|
||||
|
||||
def get_page_and_revision
|
||||
@revision = @page.revisions[@params['rev'].to_i]
|
||||
end
|
||||
|
||||
def parse_category
|
||||
@categories = @web.categories
|
||||
@category = @params['category']
|
||||
if @categories.include?(@category)
|
||||
@pages_in_category = @web.select { |page| page.in_category?(@category) }
|
||||
@set_name = "category '#{@category}'"
|
||||
else
|
||||
@pages_in_category = PageSet.new(@web).by_name
|
||||
@set_name = 'the web'
|
||||
end
|
||||
@category_links = @categories.map { |c|
|
||||
if @category == c
|
||||
%{<span class="selected">#{c}</span>}
|
||||
else
|
||||
%{<a href="?category=#{c}">#{c}</a>}
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def password_check(password)
|
||||
if password == @web.password
|
||||
cookies['web_address'] = password
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def redirect_show(page_name = @page_name, web = @web_name)
|
||||
redirect_to :web => web, :action => 'show', :id => CGI.escape(page_name)
|
||||
end
|
||||
|
||||
def remote_ip
|
||||
ip = @request.remote_ip
|
||||
logger.info(ip)
|
||||
ip
|
||||
end
|
||||
|
||||
def render_rss(hide_description = false)
|
||||
@pages_by_revision = @web.select.by_revision.first(15)
|
||||
@hide_description = hide_description
|
||||
@response.headers['Content-Type'] = 'text/xml'
|
||||
render 'wiki/rss_feed'
|
||||
end
|
||||
|
||||
def render_tex_web
|
||||
@web.select.by_name.inject({}) do |tex_web, page|
|
||||
tex_web[page.name] = RedClothForTex.new(page.content).to_tex
|
||||
tex_web
|
||||
end
|
||||
end
|
||||
|
||||
def render_to_string(template_name)
|
||||
add_variables_to_assigns
|
||||
render template_name
|
||||
@performed_render = false
|
||||
@template.render_file(template_name)
|
||||
end
|
||||
|
||||
def truncate(text, length = 30, truncate_string = '...')
|
||||
if text.length > length then text[0..(length - 3)] + truncate_string else text end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
6
app/models/author.rb
Executable file → Normal file
6
app/models/author.rb
Executable file → Normal file
|
@ -1,4 +1,4 @@
|
|||
class Author < String
|
||||
attr_accessor :ip
|
||||
def initialize(name, ip) @ip = ip; super(name) end
|
||||
class Author < String
|
||||
attr_accessor :ip
|
||||
def initialize(name, ip) @ip = ip; super(name) end
|
||||
end
|
70
app/models/chunks/category.rb
Executable file → Normal file
70
app/models/chunks/category.rb
Executable file → Normal file
|
@ -1,35 +1,35 @@
|
|||
require 'chunks/chunk'
|
||||
|
||||
# The category chunk looks for "category: news" on a line by
|
||||
# itself and parses the terms after the ':' as categories.
|
||||
# Other classes can search for Category chunks within
|
||||
# rendered content to find out what categories this page
|
||||
# should be in.
|
||||
#
|
||||
# Category lines can be hidden using ':category: news', for example
|
||||
class Category < Chunk::Abstract
|
||||
def self.pattern() return /^(:)?category\s*:(.*)$/i end
|
||||
|
||||
attr_reader :hidden, :list
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@hidden = match_data[1]
|
||||
@list = match_data[2].split(',').map { |c| c.strip }
|
||||
end
|
||||
|
||||
# If the chunk is hidden, erase the mask and return this chunk
|
||||
# otherwise, surround it with a 'div' block.
|
||||
def unmask(content)
|
||||
return '' if hidden
|
||||
|
||||
category_urls = @list.map{|category| url(category) }.join(', ')
|
||||
replacement = '<div class="property"> category: ' + category_urls + '</div>'
|
||||
self if content.sub!(mask(content), replacement)
|
||||
end
|
||||
|
||||
# TODO move presentation of page metadata to controller/view
|
||||
def url(category)
|
||||
%{<a class="category_link" href="../list/?category=#{category}">#{category}</a>}
|
||||
end
|
||||
end
|
||||
require 'chunks/chunk'
|
||||
|
||||
# The category chunk looks for "category: news" on a line by
|
||||
# itself and parses the terms after the ':' as categories.
|
||||
# Other classes can search for Category chunks within
|
||||
# rendered content to find out what categories this page
|
||||
# should be in.
|
||||
#
|
||||
# Category lines can be hidden using ':category: news', for example
|
||||
class Category < Chunk::Abstract
|
||||
def self.pattern() return /^(:)?category\s*:(.*)$/i end
|
||||
|
||||
attr_reader :hidden, :list
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@hidden = match_data[1]
|
||||
@list = match_data[2].split(',').map { |c| c.strip }
|
||||
end
|
||||
|
||||
# If the chunk is hidden, erase the mask and return this chunk
|
||||
# otherwise, surround it with a 'div' block.
|
||||
def unmask(content)
|
||||
return '' if hidden
|
||||
|
||||
category_urls = @list.map{|category| url(category) }.join(', ')
|
||||
replacement = '<div class="property"> category: ' + category_urls + '</div>'
|
||||
self if content.sub!(mask(content), replacement)
|
||||
end
|
||||
|
||||
# TODO move presentation of page metadata to controller/view
|
||||
def url(category)
|
||||
%{<a class="category_link" href="../list/?category=#{category}">#{category}</a>}
|
||||
end
|
||||
end
|
||||
|
|
80
app/models/chunks/chunk.rb
Executable file → Normal file
80
app/models/chunks/chunk.rb
Executable file → Normal file
|
@ -1,40 +1,40 @@
|
|||
require 'digest/md5'
|
||||
require 'uri/common'
|
||||
|
||||
# A chunk is a pattern of text that can be protected
|
||||
# and interrogated by a renderer. Each Chunk class has a
|
||||
# +pattern+ that states what sort of text it matches.
|
||||
# Chunks are initalized by passing in the result of a
|
||||
# match by its pattern.
|
||||
module Chunk
|
||||
class Abstract
|
||||
attr_reader :text
|
||||
|
||||
def initialize(match_data) @text = match_data[0] end
|
||||
|
||||
# Find all the chunks of the given type in content
|
||||
# Each time the pattern is matched, create a new
|
||||
# chunk for it, and replace the occurance of the chunk
|
||||
# in this content with its mask.
|
||||
def self.apply_to(content)
|
||||
content.gsub!( self.pattern ) do |match|
|
||||
new_chunk = self.new($~)
|
||||
content.chunks << new_chunk
|
||||
new_chunk.mask(content)
|
||||
end
|
||||
end
|
||||
|
||||
def mask(content)
|
||||
"chunk#{self.object_id}#{self.class.to_s.delete(':').downcase}chunk"
|
||||
end
|
||||
|
||||
def revert(content)
|
||||
content.sub!( Regexp.new(mask(content)), text )
|
||||
end
|
||||
|
||||
def unmask(content)
|
||||
self if revert(content)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
require 'digest/md5'
|
||||
require 'uri/common'
|
||||
|
||||
# A chunk is a pattern of text that can be protected
|
||||
# and interrogated by a renderer. Each Chunk class has a
|
||||
# +pattern+ that states what sort of text it matches.
|
||||
# Chunks are initalized by passing in the result of a
|
||||
# match by its pattern.
|
||||
module Chunk
|
||||
class Abstract
|
||||
attr_reader :text
|
||||
|
||||
def initialize(match_data) @text = match_data[0] end
|
||||
|
||||
# Find all the chunks of the given type in content
|
||||
# Each time the pattern is matched, create a new
|
||||
# chunk for it, and replace the occurance of the chunk
|
||||
# in this content with its mask.
|
||||
def self.apply_to(content)
|
||||
content.gsub!( self.pattern ) do |match|
|
||||
new_chunk = self.new($~)
|
||||
content.chunks << new_chunk
|
||||
new_chunk.mask(content)
|
||||
end
|
||||
end
|
||||
|
||||
def mask(content)
|
||||
"chunk#{self.object_id}#{self.class.to_s.delete(':').downcase}chunk"
|
||||
end
|
||||
|
||||
def revert(content)
|
||||
content.sub!( Regexp.new(mask(content)), text )
|
||||
end
|
||||
|
||||
def unmask(content)
|
||||
self if revert(content)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
76
app/models/chunks/engines.rb
Executable file → Normal file
76
app/models/chunks/engines.rb
Executable file → Normal file
|
@ -1,38 +1,38 @@
|
|||
$: << File.dirname(__FILE__) + "../../libraries"
|
||||
|
||||
require 'redcloth'
|
||||
require 'bluecloth'
|
||||
require 'rdocsupport'
|
||||
require 'chunks/chunk'
|
||||
|
||||
# The markup engines are Chunks that call the one of RedCloth, BlueCloth
|
||||
# or RDoc to convert text. This markup occurs when the chunk is required
|
||||
# to mask itself.
|
||||
module Engines
|
||||
class Textile < Chunk::Abstract
|
||||
def self.pattern() /^(.*)$/m end
|
||||
def mask(content)
|
||||
RedCloth.new(text,content.options[:engine_opts]).to_html
|
||||
end
|
||||
def unmask(content) self end
|
||||
end
|
||||
|
||||
class Markdown < Chunk::Abstract
|
||||
def self.pattern() /^(.*)$/m end
|
||||
def mask(content)
|
||||
BlueCloth.new(text,content.options[:engine_opts]).to_html
|
||||
end
|
||||
def unmask(content) self end
|
||||
end
|
||||
|
||||
class RDoc < Chunk::Abstract
|
||||
def self.pattern() /^(.*)$/m end
|
||||
def mask(content)
|
||||
RDocSupport::RDocFormatter.new(text).to_html
|
||||
end
|
||||
def unmask(content) self end
|
||||
end
|
||||
|
||||
MAP = { :textile => Textile, :markdown => Markdown, :rdoc => RDoc }
|
||||
end
|
||||
|
||||
$: << File.dirname(__FILE__) + "../../libraries"
|
||||
|
||||
require 'redcloth'
|
||||
require 'bluecloth'
|
||||
require 'rdocsupport'
|
||||
require 'chunks/chunk'
|
||||
|
||||
# The markup engines are Chunks that call the one of RedCloth, BlueCloth
|
||||
# or RDoc to convert text. This markup occurs when the chunk is required
|
||||
# to mask itself.
|
||||
module Engines
|
||||
class Textile < Chunk::Abstract
|
||||
def self.pattern() /^(.*)$/m end
|
||||
def mask(content)
|
||||
RedCloth.new(text,content.options[:engine_opts]).to_html
|
||||
end
|
||||
def unmask(content) self end
|
||||
end
|
||||
|
||||
class Markdown < Chunk::Abstract
|
||||
def self.pattern() /^(.*)$/m end
|
||||
def mask(content)
|
||||
BlueCloth.new(text,content.options[:engine_opts]).to_html
|
||||
end
|
||||
def unmask(content) self end
|
||||
end
|
||||
|
||||
class RDoc < Chunk::Abstract
|
||||
def self.pattern() /^(.*)$/m end
|
||||
def mask(content)
|
||||
RDocSupport::RDocFormatter.new(text).to_html
|
||||
end
|
||||
def unmask(content) self end
|
||||
end
|
||||
|
||||
MAP = { :textile => Textile, :markdown => Markdown, :rdoc => RDoc }
|
||||
end
|
||||
|
||||
|
|
58
app/models/chunks/include.rb
Executable file → Normal file
58
app/models/chunks/include.rb
Executable file → Normal file
|
@ -1,29 +1,29 @@
|
|||
require 'chunks/wiki'
|
||||
|
||||
# Includes the contents of another page for rendering.
|
||||
# The include command looks like this: "[[!include PageName]]".
|
||||
# It is a WikiLink since it refers to another page (PageName)
|
||||
# and the wiki content using this command must be notified
|
||||
# of changes to that page.
|
||||
# If the included page could not be found, a warning is displayed.
|
||||
class Include < WikiChunk::WikiLink
|
||||
def self.pattern() /^\[\[!include(.*)\]\]\s*$/i end
|
||||
|
||||
attr_reader :page_name
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@page_name = match_data[1].strip
|
||||
end
|
||||
|
||||
# This replaces the [[!include PageName]] text with
|
||||
# the contents of PageName if it exists. Otherwise
|
||||
# a warning is displayed.
|
||||
def mask(content)
|
||||
page = content.web.pages[page_name]
|
||||
(page ? page.content : "<em>Could not include #{page_name}</em>")
|
||||
end
|
||||
|
||||
# Keep this chunk regardless of what happens.
|
||||
def unmask(content) self end
|
||||
end
|
||||
require 'chunks/wiki'
|
||||
|
||||
# Includes the contents of another page for rendering.
|
||||
# The include command looks like this: "[[!include PageName]]".
|
||||
# It is a WikiLink since it refers to another page (PageName)
|
||||
# and the wiki content using this command must be notified
|
||||
# of changes to that page.
|
||||
# If the included page could not be found, a warning is displayed.
|
||||
class Include < WikiChunk::WikiLink
|
||||
def self.pattern() /^\[\[!include(.*)\]\]\s*$/i end
|
||||
|
||||
attr_reader :page_name
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@page_name = match_data[1].strip
|
||||
end
|
||||
|
||||
# This replaces the [[!include PageName]] text with
|
||||
# the contents of PageName if it exists. Otherwise
|
||||
# a warning is displayed.
|
||||
def mask(content)
|
||||
page = content.web.pages[page_name]
|
||||
(page ? page.content : "<em>Could not include #{page_name}</em>")
|
||||
end
|
||||
|
||||
# Keep this chunk regardless of what happens.
|
||||
def unmask(content) self end
|
||||
end
|
||||
|
|
38
app/models/chunks/literal.rb
Executable file → Normal file
38
app/models/chunks/literal.rb
Executable file → Normal file
|
@ -1,19 +1,19 @@
|
|||
require 'chunks/chunk'
|
||||
|
||||
# These are basic chunks that have a pattern and can be protected.
|
||||
# They are used by rendering process to prevent wiki rendering
|
||||
# occuring within literal areas such as <code> and <pre> blocks
|
||||
# and within HTML tags.
|
||||
module Literal
|
||||
# A literal chunk that protects 'code' and 'pre' tags from wiki rendering.
|
||||
class Pre < Chunk::Abstract
|
||||
PRE_BLOCKS = "a|pre|code"
|
||||
def self.pattern() Regexp.new('<('+PRE_BLOCKS+')\b[^>]*?>.*?</\1>', Regexp::MULTILINE) end
|
||||
end
|
||||
|
||||
# A literal chunk that protects HTML tags from wiki rendering.
|
||||
class Tags < Chunk::Abstract
|
||||
TAGS = "a|img|em|strong|div|span|table|td|th|ul|ol|li|dl|dt|dd"
|
||||
def self.pattern() Regexp.new('<(?:'+TAGS+')[^>]*?>', Regexp::MULTILINE) end
|
||||
end
|
||||
end
|
||||
require 'chunks/chunk'
|
||||
|
||||
# These are basic chunks that have a pattern and can be protected.
|
||||
# They are used by rendering process to prevent wiki rendering
|
||||
# occuring within literal areas such as <code> and <pre> blocks
|
||||
# and within HTML tags.
|
||||
module Literal
|
||||
# A literal chunk that protects 'code' and 'pre' tags from wiki rendering.
|
||||
class Pre < Chunk::Abstract
|
||||
PRE_BLOCKS = "a|pre|code"
|
||||
def self.pattern() Regexp.new('<('+PRE_BLOCKS+')\b[^>]*?>.*?</\1>', Regexp::MULTILINE) end
|
||||
end
|
||||
|
||||
# A literal chunk that protects HTML tags from wiki rendering.
|
||||
class Tags < Chunk::Abstract
|
||||
TAGS = "a|img|em|strong|div|span|table|td|th|ul|ol|li|dl|dt|dd"
|
||||
def self.pattern() Regexp.new('<(?:'+TAGS+')[^>]*?>', Regexp::MULTILINE) end
|
||||
end
|
||||
end
|
||||
|
|
62
app/models/chunks/nowiki.rb
Executable file → Normal file
62
app/models/chunks/nowiki.rb
Executable file → Normal file
|
@ -1,31 +1,31 @@
|
|||
require 'chunks/chunk'
|
||||
|
||||
# This chunks allows certain parts of a wiki page to be hidden from the
|
||||
# rest of the rendering pipeline. It should be run at the beginning
|
||||
# of the pipeline in `wiki_content.rb`.
|
||||
#
|
||||
# An example use of this chunk is to markup double brackets or
|
||||
# auto URI links:
|
||||
# <nowiki>Here are [[double brackets]] and a URI: www.uri.org</nowiki>
|
||||
#
|
||||
# The contents of the chunks will not be processed by any other chunk
|
||||
# so the `www.uri.org` and the double brackets will appear verbatim.
|
||||
#
|
||||
# Author: Mark Reid <mark at threewordslong dot com>
|
||||
# Created: 8th June 2004
|
||||
class NoWiki < Chunk::Abstract
|
||||
|
||||
def self.pattern() Regexp.new('<nowiki>(.*?)</nowiki>') end
|
||||
|
||||
attr_reader :plain_text
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@plain_text = match_data[1]
|
||||
end
|
||||
|
||||
# The nowiki content is not unmasked. This means the chunk will be reverted
|
||||
# using the plain text.
|
||||
def unmask(content) nil end
|
||||
def revert(content) content.sub!(mask(content), plain_text) end
|
||||
end
|
||||
require 'chunks/chunk'
|
||||
|
||||
# This chunks allows certain parts of a wiki page to be hidden from the
|
||||
# rest of the rendering pipeline. It should be run at the beginning
|
||||
# of the pipeline in `wiki_content.rb`.
|
||||
#
|
||||
# An example use of this chunk is to markup double brackets or
|
||||
# auto URI links:
|
||||
# <nowiki>Here are [[double brackets]] and a URI: www.uri.org</nowiki>
|
||||
#
|
||||
# The contents of the chunks will not be processed by any other chunk
|
||||
# so the `www.uri.org` and the double brackets will appear verbatim.
|
||||
#
|
||||
# Author: Mark Reid <mark at threewordslong dot com>
|
||||
# Created: 8th June 2004
|
||||
class NoWiki < Chunk::Abstract
|
||||
|
||||
def self.pattern() Regexp.new('<nowiki>(.*?)</nowiki>') end
|
||||
|
||||
attr_reader :plain_text
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@plain_text = match_data[1]
|
||||
end
|
||||
|
||||
# The nowiki content is not unmasked. This means the chunk will be reverted
|
||||
# using the plain text.
|
||||
def unmask(content) nil end
|
||||
def revert(content) content.sub!(mask(content), plain_text) end
|
||||
end
|
||||
|
|
36
app/models/chunks/test.rb
Executable file → Normal file
36
app/models/chunks/test.rb
Executable file → Normal file
|
@ -1,18 +1,18 @@
|
|||
require 'test/unit'
|
||||
|
||||
class ChunkTest < Test::Unit::TestCase
|
||||
|
||||
# Asserts a number of tests for the given type and text.
|
||||
def match(type, test_text, expected)
|
||||
pattern = type.pattern
|
||||
assert_match(pattern, test_text)
|
||||
pattern =~ test_text # Previous assertion guarantees match
|
||||
chunk = type.new($~)
|
||||
|
||||
# Test if requested parts are correct.
|
||||
for method_sym, value in expected do
|
||||
assert_respond_to(chunk, method_sym)
|
||||
assert_equal(value, chunk.method(method_sym).call, "Checking value of '#{method_sym}'")
|
||||
end
|
||||
end
|
||||
end
|
||||
require 'test/unit'
|
||||
|
||||
class ChunkTest < Test::Unit::TestCase
|
||||
|
||||
# Asserts a number of tests for the given type and text.
|
||||
def match(type, test_text, expected)
|
||||
pattern = type.pattern
|
||||
assert_match(pattern, test_text)
|
||||
pattern =~ test_text # Previous assertion guarantees match
|
||||
chunk = type.new($~)
|
||||
|
||||
# Test if requested parts are correct.
|
||||
for method_sym, value in expected do
|
||||
assert_respond_to(chunk, method_sym)
|
||||
assert_equal(value, chunk.method(method_sym).call, "Checking value of '#{method_sym}'")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
358
app/models/chunks/uri.rb
Executable file → Normal file
358
app/models/chunks/uri.rb
Executable file → Normal file
|
@ -1,179 +1,179 @@
|
|||
require 'chunks/chunk'
|
||||
|
||||
# This wiki chunk matches arbitrary URIs, using patterns from the Ruby URI modules.
|
||||
# It parses out a variety of fields that could be used by renderers to format
|
||||
# the links in various ways (shortening domain names, hiding email addresses)
|
||||
# It matches email addresses and host.com.au domains without schemes (http://)
|
||||
# but adds these on as required.
|
||||
#
|
||||
# The heuristic used to match a URI is designed to err on the side of caution.
|
||||
# That is, it is more likely to not autolink a URI than it is to accidently
|
||||
# autolink something that is not a URI. The reason behind this is it is easier
|
||||
# to force a URI link by prefixing 'http://' to it than it is to escape and
|
||||
# incorrectly marked up non-URI.
|
||||
#
|
||||
# I'm using a part of the [ISO 3166-1 Standard][iso3166] for country name suffixes.
|
||||
# The generic names are from www.bnoack.com/data/countrycode2.html)
|
||||
# [iso3166]: http://geotags.com/iso3166/
|
||||
class URIChunk < Chunk::Abstract
|
||||
include URI::REGEXP::PATTERN
|
||||
|
||||
# this condition is to get rid of pesky warnings in tests
|
||||
unless defined? URIChunk::INTERNET_URI_REGEXP
|
||||
|
||||
GENERIC = '(?:aero|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org)'
|
||||
COUNTRY = '(?:au|at|be|ca|ch|de|dk|fr|hk|in|ir|it|jp|nl|no|pt|ru|se|sw|tv|tw|uk|us)'
|
||||
|
||||
# These are needed otherwise HOST will match almost anything
|
||||
TLDS = "(?:#{GENERIC}|#{COUNTRY})"
|
||||
|
||||
# Redefine USERINFO so that it must have non-zero length
|
||||
USERINFO = "(?:[#{UNRESERVED};:&=+$,]|#{ESCAPED})+"
|
||||
|
||||
# unreserved_no_ending = alphanum | mark, but URI_ENDING [)!] excluded
|
||||
UNRESERVED_NO_ENDING = "-_.~*'(#{ALNUM}"
|
||||
|
||||
# this ensures that query or fragment do not end with URI_ENDING
|
||||
# and enable us to use a much simpler self.pattern Regexp
|
||||
|
||||
# uric_no_ending = reserved | unreserved_no_ending | escaped
|
||||
URIC_NO_ENDING = "(?:[#{UNRESERVED_NO_ENDING}#{RESERVED}]|#{ESCAPED})"
|
||||
# query = *uric
|
||||
QUERY = "#{URIC_NO_ENDING}*"
|
||||
# fragment = *uric
|
||||
FRAGMENT = "#{URIC_NO_ENDING}*"
|
||||
|
||||
# DOMLABEL is defined in the ruby uri library, TLDS is defined above
|
||||
INTERNET_HOSTNAME = "(?:#{DOMLABEL}\\.)+#{TLDS}"
|
||||
|
||||
# Correct a typo bug in ruby 1.8.x lib/uri/common.rb
|
||||
PORT = '\\d*'
|
||||
|
||||
INTERNET_URI =
|
||||
"(?:(#{SCHEME}):/{0,2})?" + # Optional scheme: (\1)
|
||||
"(?:(#{USERINFO})@)?" + # Optional userinfo@ (\2)
|
||||
"(#{INTERNET_HOSTNAME})" + # Mandatory hostname (\3)
|
||||
"(?::(#{PORT}))?" + # Optional :port (\4)
|
||||
"(#{ABS_PATH})?" + # Optional absolute path (\5)
|
||||
"(?:\\?(#{QUERY}))?" + # Optional ?query (\6)
|
||||
"(?:\\#(#{FRAGMENT}))?" # Optional #fragment (\7)
|
||||
|
||||
TEXTILE_SYNTAX_PREFIX = '(!)?'
|
||||
|
||||
INTERNET_URI_REGEXP = Regexp.new(TEXTILE_SYNTAX_PREFIX + INTERNET_URI, Regexp::EXTENDED, 'N')
|
||||
|
||||
end
|
||||
|
||||
def URIChunk.pattern
|
||||
INTERNET_URI_REGEXP
|
||||
end
|
||||
|
||||
attr_reader :user, :host, :port, :path, :query, :fragment, :link_text
|
||||
|
||||
def self.apply_to(content)
|
||||
content.gsub!( self.pattern ) do |matched_text|
|
||||
chunk = self.new($~)
|
||||
if chunk.textile_url? or chunk.textile_image?
|
||||
# do not substitute
|
||||
matched_text
|
||||
else
|
||||
content.chunks << chunk
|
||||
chunk.mask(content)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@link_text = match_data[0]
|
||||
@textile_prefix, @original_scheme, @user, @host, @port, @path, @query, @fragment =
|
||||
match_data[1..-1]
|
||||
treat_trailing_character
|
||||
end
|
||||
|
||||
def textile_url?
|
||||
@textile_prefix == '":'
|
||||
end
|
||||
|
||||
def textile_image?
|
||||
@textile_prefix == '!' and @trailing_punctuation == '!'
|
||||
end
|
||||
|
||||
def treat_trailing_character
|
||||
# If the last character matched by URI pattern is in ! or ), this may be part of the markup,
|
||||
# not a URL. We should handle it as such. It is possible to do it by a regexp, but
|
||||
# much easier to do programmatically
|
||||
last_char = @link_text[-1..-1]
|
||||
if last_char == ')' or last_char == '!'
|
||||
@trailing_punctuation = last_char
|
||||
@link_text.chop!
|
||||
[@original_scheme, @user, @host, @port, @path, @query, @fragment].compact.last.chop!
|
||||
end
|
||||
end
|
||||
|
||||
# If the text should be escaped then don't keep this chunk.
|
||||
# Otherwise only keep this chunk if it was substituted back into the
|
||||
# content.
|
||||
def unmask(content)
|
||||
return nil if escaped_text
|
||||
return self if content.sub!(mask(content), "<a href=\"#{uri}\">#{link_text}</a>")
|
||||
end
|
||||
|
||||
# If there is no hostname in the URI, do not render it
|
||||
# It's probably only contains the scheme, eg 'something:'
|
||||
def escaped_text() ( host.nil? ? @uri : nil ) end
|
||||
|
||||
def scheme
|
||||
@original_scheme or (@user ? 'mailto' : 'http')
|
||||
end
|
||||
|
||||
def scheme_delimiter
|
||||
scheme == 'mailto' ? ':' : '://'
|
||||
end
|
||||
|
||||
def user_delimiter
|
||||
'@' unless @user.nil?
|
||||
end
|
||||
|
||||
def port_delimiter
|
||||
':' unless @port.nil?
|
||||
end
|
||||
|
||||
def query_delimiter
|
||||
'?' unless @query.nil?
|
||||
end
|
||||
|
||||
def uri
|
||||
[scheme, scheme_delimiter, user, user_delimiter, host, port_delimiter, port, path,
|
||||
query_delimiter, query].compact.join
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# uri with mandatory scheme but less restrictive hostname, like
|
||||
# http://localhost:2500/blah.html
|
||||
class LocalURIChunk < URIChunk
|
||||
|
||||
unless defined? LocalURIChunk::LOCAL_URI_REGEXP
|
||||
# hostname can be just a simple word like 'localhost'
|
||||
ANY_HOSTNAME = "(?:#{DOMLABEL}\\.)*#{TOPLABEL}\\.?"
|
||||
|
||||
# The basic URI expression as a string
|
||||
# Scheme and hostname are mandatory
|
||||
LOCAL_URI =
|
||||
"(?:(#{SCHEME})://)+" + # Mandatory scheme:// (\1)
|
||||
"(?:(#{USERINFO})@)?" + # Optional userinfo@ (\2)
|
||||
"(#{ANY_HOSTNAME})" + # Mandatory hostname (\3)
|
||||
"(?::(#{PORT}))?" + # Optional :port (\4)
|
||||
"(#{ABS_PATH})?" + # Optional absolute path (\5)
|
||||
"(?:\\?(#{QUERY}))?" + # Optional ?query (\6)
|
||||
"(?:\\#(#{FRAGMENT}))?" # Optional #fragment (\7)
|
||||
|
||||
LOCAL_URI_REGEXP = Regexp.new(TEXTILE_SYNTAX_PREFIX + LOCAL_URI, Regexp::EXTENDED, 'N')
|
||||
end
|
||||
|
||||
def LocalURIChunk.pattern
|
||||
LOCAL_URI_REGEXP
|
||||
end
|
||||
|
||||
end
|
||||
require 'chunks/chunk'
|
||||
|
||||
# This wiki chunk matches arbitrary URIs, using patterns from the Ruby URI modules.
|
||||
# It parses out a variety of fields that could be used by renderers to format
|
||||
# the links in various ways (shortening domain names, hiding email addresses)
|
||||
# It matches email addresses and host.com.au domains without schemes (http://)
|
||||
# but adds these on as required.
|
||||
#
|
||||
# The heuristic used to match a URI is designed to err on the side of caution.
|
||||
# That is, it is more likely to not autolink a URI than it is to accidently
|
||||
# autolink something that is not a URI. The reason behind this is it is easier
|
||||
# to force a URI link by prefixing 'http://' to it than it is to escape and
|
||||
# incorrectly marked up non-URI.
|
||||
#
|
||||
# I'm using a part of the [ISO 3166-1 Standard][iso3166] for country name suffixes.
|
||||
# The generic names are from www.bnoack.com/data/countrycode2.html)
|
||||
# [iso3166]: http://geotags.com/iso3166/
|
||||
class URIChunk < Chunk::Abstract
|
||||
include URI::REGEXP::PATTERN
|
||||
|
||||
# this condition is to get rid of pesky warnings in tests
|
||||
unless defined? URIChunk::INTERNET_URI_REGEXP
|
||||
|
||||
GENERIC = '(?:aero|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org)'
|
||||
COUNTRY = '(?:au|at|be|ca|ch|de|dk|fr|hk|in|ir|it|jp|nl|no|pt|ru|se|sw|tv|tw|uk|us)'
|
||||
|
||||
# These are needed otherwise HOST will match almost anything
|
||||
TLDS = "(?:#{GENERIC}|#{COUNTRY})"
|
||||
|
||||
# Redefine USERINFO so that it must have non-zero length
|
||||
USERINFO = "(?:[#{UNRESERVED};:&=+$,]|#{ESCAPED})+"
|
||||
|
||||
# unreserved_no_ending = alphanum | mark, but URI_ENDING [)!] excluded
|
||||
UNRESERVED_NO_ENDING = "-_.~*'(#{ALNUM}"
|
||||
|
||||
# this ensures that query or fragment do not end with URI_ENDING
|
||||
# and enable us to use a much simpler self.pattern Regexp
|
||||
|
||||
# uric_no_ending = reserved | unreserved_no_ending | escaped
|
||||
URIC_NO_ENDING = "(?:[#{UNRESERVED_NO_ENDING}#{RESERVED}]|#{ESCAPED})"
|
||||
# query = *uric
|
||||
QUERY = "#{URIC_NO_ENDING}*"
|
||||
# fragment = *uric
|
||||
FRAGMENT = "#{URIC_NO_ENDING}*"
|
||||
|
||||
# DOMLABEL is defined in the ruby uri library, TLDS is defined above
|
||||
INTERNET_HOSTNAME = "(?:#{DOMLABEL}\\.)+#{TLDS}"
|
||||
|
||||
# Correct a typo bug in ruby 1.8.x lib/uri/common.rb
|
||||
PORT = '\\d*'
|
||||
|
||||
INTERNET_URI =
|
||||
"(?:(#{SCHEME}):/{0,2})?" + # Optional scheme: (\1)
|
||||
"(?:(#{USERINFO})@)?" + # Optional userinfo@ (\2)
|
||||
"(#{INTERNET_HOSTNAME})" + # Mandatory hostname (\3)
|
||||
"(?::(#{PORT}))?" + # Optional :port (\4)
|
||||
"(#{ABS_PATH})?" + # Optional absolute path (\5)
|
||||
"(?:\\?(#{QUERY}))?" + # Optional ?query (\6)
|
||||
"(?:\\#(#{FRAGMENT}))?" # Optional #fragment (\7)
|
||||
|
||||
TEXTILE_SYNTAX_PREFIX = '(!)?'
|
||||
|
||||
INTERNET_URI_REGEXP = Regexp.new(TEXTILE_SYNTAX_PREFIX + INTERNET_URI, Regexp::EXTENDED, 'N')
|
||||
|
||||
end
|
||||
|
||||
def URIChunk.pattern
|
||||
INTERNET_URI_REGEXP
|
||||
end
|
||||
|
||||
attr_reader :user, :host, :port, :path, :query, :fragment, :link_text
|
||||
|
||||
def self.apply_to(content)
|
||||
content.gsub!( self.pattern ) do |matched_text|
|
||||
chunk = self.new($~)
|
||||
if chunk.textile_url? or chunk.textile_image?
|
||||
# do not substitute
|
||||
matched_text
|
||||
else
|
||||
content.chunks << chunk
|
||||
chunk.mask(content)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@link_text = match_data[0]
|
||||
@textile_prefix, @original_scheme, @user, @host, @port, @path, @query, @fragment =
|
||||
match_data[1..-1]
|
||||
treat_trailing_character
|
||||
end
|
||||
|
||||
def textile_url?
|
||||
@textile_prefix == '":'
|
||||
end
|
||||
|
||||
def textile_image?
|
||||
@textile_prefix == '!' and @trailing_punctuation == '!'
|
||||
end
|
||||
|
||||
def treat_trailing_character
|
||||
# If the last character matched by URI pattern is in ! or ), this may be part of the markup,
|
||||
# not a URL. We should handle it as such. It is possible to do it by a regexp, but
|
||||
# much easier to do programmatically
|
||||
last_char = @link_text[-1..-1]
|
||||
if last_char == ')' or last_char == '!'
|
||||
@trailing_punctuation = last_char
|
||||
@link_text.chop!
|
||||
[@original_scheme, @user, @host, @port, @path, @query, @fragment].compact.last.chop!
|
||||
end
|
||||
end
|
||||
|
||||
# If the text should be escaped then don't keep this chunk.
|
||||
# Otherwise only keep this chunk if it was substituted back into the
|
||||
# content.
|
||||
def unmask(content)
|
||||
return nil if escaped_text
|
||||
return self if content.sub!(mask(content), "<a href=\"#{uri}\">#{link_text}</a>")
|
||||
end
|
||||
|
||||
# If there is no hostname in the URI, do not render it
|
||||
# It's probably only contains the scheme, eg 'something:'
|
||||
def escaped_text() ( host.nil? ? @uri : nil ) end
|
||||
|
||||
def scheme
|
||||
@original_scheme or (@user ? 'mailto' : 'http')
|
||||
end
|
||||
|
||||
def scheme_delimiter
|
||||
scheme == 'mailto' ? ':' : '://'
|
||||
end
|
||||
|
||||
def user_delimiter
|
||||
'@' unless @user.nil?
|
||||
end
|
||||
|
||||
def port_delimiter
|
||||
':' unless @port.nil?
|
||||
end
|
||||
|
||||
def query_delimiter
|
||||
'?' unless @query.nil?
|
||||
end
|
||||
|
||||
def uri
|
||||
[scheme, scheme_delimiter, user, user_delimiter, host, port_delimiter, port, path,
|
||||
query_delimiter, query].compact.join
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# uri with mandatory scheme but less restrictive hostname, like
|
||||
# http://localhost:2500/blah.html
|
||||
class LocalURIChunk < URIChunk
|
||||
|
||||
unless defined? LocalURIChunk::LOCAL_URI_REGEXP
|
||||
# hostname can be just a simple word like 'localhost'
|
||||
ANY_HOSTNAME = "(?:#{DOMLABEL}\\.)*#{TOPLABEL}\\.?"
|
||||
|
||||
# The basic URI expression as a string
|
||||
# Scheme and hostname are mandatory
|
||||
LOCAL_URI =
|
||||
"(?:(#{SCHEME})://)+" + # Mandatory scheme:// (\1)
|
||||
"(?:(#{USERINFO})@)?" + # Optional userinfo@ (\2)
|
||||
"(#{ANY_HOSTNAME})" + # Mandatory hostname (\3)
|
||||
"(?::(#{PORT}))?" + # Optional :port (\4)
|
||||
"(#{ABS_PATH})?" + # Optional absolute path (\5)
|
||||
"(?:\\?(#{QUERY}))?" + # Optional ?query (\6)
|
||||
"(?:\\#(#{FRAGMENT}))?" # Optional #fragment (\7)
|
||||
|
||||
LOCAL_URI_REGEXP = Regexp.new(TEXTILE_SYNTAX_PREFIX + LOCAL_URI, Regexp::EXTENDED, 'N')
|
||||
end
|
||||
|
||||
def LocalURIChunk.pattern
|
||||
LOCAL_URI_REGEXP
|
||||
end
|
||||
|
||||
end
|
||||
|
|
284
app/models/chunks/wiki.rb
Executable file → Normal file
284
app/models/chunks/wiki.rb
Executable file → Normal file
|
@ -1,142 +1,142 @@
|
|||
require 'wiki_words'
|
||||
require 'chunks/chunk'
|
||||
require 'chunks/wiki'
|
||||
require 'cgi'
|
||||
|
||||
# Contains all the methods for finding and replacing wiki related links.
|
||||
module WikiChunk
|
||||
include Chunk
|
||||
|
||||
# A wiki link is the top-level class for anything that refers to
|
||||
# another wiki page.
|
||||
class WikiLink < Chunk::Abstract
|
||||
|
||||
attr_reader :page_name, :link_text, :link_type
|
||||
|
||||
def initialize(*args)
|
||||
super
|
||||
@link_type = 'show'
|
||||
end
|
||||
|
||||
def self.apply_to(content)
|
||||
content.gsub!( self.pattern ) do |matched_text|
|
||||
chunk = self.new($~)
|
||||
if chunk.textile_url?
|
||||
# do not substitute
|
||||
matched_text
|
||||
else
|
||||
content.chunks << chunk
|
||||
chunk.mask(content)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def textile_url?
|
||||
not @textile_link_suffix.nil?
|
||||
end
|
||||
|
||||
# By default, no escaped text
|
||||
def escaped_text() nil end
|
||||
|
||||
# Replace link with a mask, but if the word is escaped, then don't replace it
|
||||
def mask(content)
|
||||
escaped_text || super(content)
|
||||
end
|
||||
|
||||
def revert(content) content.sub!(mask(content), text) end
|
||||
|
||||
# Do not keep this chunk if it is escaped.
|
||||
# Otherwise, pass the link procedure a page_name and link_text and
|
||||
# get back a string of HTML to replace the mask with.
|
||||
def unmask(content)
|
||||
if escaped_text
|
||||
return self
|
||||
else
|
||||
chunk_found = content.sub!(mask(content)) do |match|
|
||||
content.page_link(page_name, link_text, link_type)
|
||||
end
|
||||
if chunk_found
|
||||
return self
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# This chunk matches a WikiWord. WikiWords can be escaped
|
||||
# by prepending a '\'. When this is the case, the +escaped_text+
|
||||
# method will return the WikiWord instead of the usual +nil+.
|
||||
# The +page_name+ method returns the matched WikiWord.
|
||||
class Word < WikiLink
|
||||
unless defined? WIKI_LINK
|
||||
WIKI_WORD = Regexp.new('(":)?(\\\\)?(' + WikiWords::WIKI_WORD_PATTERN + ')\b', 0, "utf-8")
|
||||
end
|
||||
|
||||
def self.pattern
|
||||
WIKI_WORD
|
||||
end
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@textile_link_suffix, @escape, @page_name = match_data[1..3]
|
||||
end
|
||||
|
||||
def escaped_text
|
||||
page_name unless @escape.nil?
|
||||
end
|
||||
def link_text() WikiWords.separate(page_name) end
|
||||
end
|
||||
|
||||
# This chunk handles [[bracketted wiki words]] and
|
||||
# [[AliasedWords|aliased wiki words]]. The first part of an
|
||||
# aliased wiki word must be a WikiWord. If the WikiWord
|
||||
# is aliased, the +link_text+ field will contain the
|
||||
# alias, otherwise +link_text+ will contain the entire
|
||||
# contents within the double brackets.
|
||||
#
|
||||
# NOTE: This chunk must be tested before WikiWord since
|
||||
# a WikiWords can be a substring of a WikiLink.
|
||||
class Link < WikiLink
|
||||
|
||||
unless defined? WIKI_LINK
|
||||
WIKI_LINK = /(":)?\[\[([^\]]+)\]\]/
|
||||
LINK_TYPE_SEPARATION = Regexp.new('^(.+):((file)|(pic))$', 0, 'utf-8')
|
||||
ALIAS_SEPARATION = Regexp.new('^(.+)\|(.+)$', 0, 'utf-8')
|
||||
end
|
||||
|
||||
def self.pattern() WIKI_LINK end
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@textile_link_suffix, @page_name = match_data[1..2]
|
||||
@link_text = @page_name
|
||||
separate_link_type
|
||||
separate_alias
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# if link wihin the brackets has a form of [[filename:file]] or [[filename:pic]],
|
||||
# this means a link to a picture or a file
|
||||
def separate_link_type
|
||||
link_type_match = LINK_TYPE_SEPARATION.match(@page_name)
|
||||
if link_type_match
|
||||
@link_text = @page_name = link_type_match[1]
|
||||
@link_type = link_type_match[2..3].compact[0]
|
||||
end
|
||||
end
|
||||
|
||||
# link text may be different from page name. this will look like [[actual page|link text]]
|
||||
def separate_alias
|
||||
alias_match = ALIAS_SEPARATION.match(@page_name)
|
||||
if alias_match
|
||||
@page_name, @link_text = alias_match[1..2]
|
||||
end
|
||||
# note that [[filename|link text:file]] is also supported
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
require 'wiki_words'
|
||||
require 'chunks/chunk'
|
||||
require 'chunks/wiki'
|
||||
require 'cgi'
|
||||
|
||||
# Contains all the methods for finding and replacing wiki related links.
|
||||
module WikiChunk
|
||||
include Chunk
|
||||
|
||||
# A wiki link is the top-level class for anything that refers to
|
||||
# another wiki page.
|
||||
class WikiLink < Chunk::Abstract
|
||||
|
||||
attr_reader :page_name, :link_text, :link_type
|
||||
|
||||
def initialize(*args)
|
||||
super
|
||||
@link_type = 'show'
|
||||
end
|
||||
|
||||
def self.apply_to(content)
|
||||
content.gsub!( self.pattern ) do |matched_text|
|
||||
chunk = self.new($~)
|
||||
if chunk.textile_url?
|
||||
# do not substitute
|
||||
matched_text
|
||||
else
|
||||
content.chunks << chunk
|
||||
chunk.mask(content)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def textile_url?
|
||||
not @textile_link_suffix.nil?
|
||||
end
|
||||
|
||||
# By default, no escaped text
|
||||
def escaped_text() nil end
|
||||
|
||||
# Replace link with a mask, but if the word is escaped, then don't replace it
|
||||
def mask(content)
|
||||
escaped_text || super(content)
|
||||
end
|
||||
|
||||
def revert(content) content.sub!(mask(content), text) end
|
||||
|
||||
# Do not keep this chunk if it is escaped.
|
||||
# Otherwise, pass the link procedure a page_name and link_text and
|
||||
# get back a string of HTML to replace the mask with.
|
||||
def unmask(content)
|
||||
if escaped_text
|
||||
return self
|
||||
else
|
||||
chunk_found = content.sub!(mask(content)) do |match|
|
||||
content.page_link(page_name, link_text, link_type)
|
||||
end
|
||||
if chunk_found
|
||||
return self
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# This chunk matches a WikiWord. WikiWords can be escaped
|
||||
# by prepending a '\'. When this is the case, the +escaped_text+
|
||||
# method will return the WikiWord instead of the usual +nil+.
|
||||
# The +page_name+ method returns the matched WikiWord.
|
||||
class Word < WikiLink
|
||||
unless defined? WIKI_LINK
|
||||
WIKI_WORD = Regexp.new('(":)?(\\\\)?(' + WikiWords::WIKI_WORD_PATTERN + ')\b', 0, "utf-8")
|
||||
end
|
||||
|
||||
def self.pattern
|
||||
WIKI_WORD
|
||||
end
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@textile_link_suffix, @escape, @page_name = match_data[1..3]
|
||||
end
|
||||
|
||||
def escaped_text
|
||||
page_name unless @escape.nil?
|
||||
end
|
||||
def link_text() WikiWords.separate(page_name) end
|
||||
end
|
||||
|
||||
# This chunk handles [[bracketted wiki words]] and
|
||||
# [[AliasedWords|aliased wiki words]]. The first part of an
|
||||
# aliased wiki word must be a WikiWord. If the WikiWord
|
||||
# is aliased, the +link_text+ field will contain the
|
||||
# alias, otherwise +link_text+ will contain the entire
|
||||
# contents within the double brackets.
|
||||
#
|
||||
# NOTE: This chunk must be tested before WikiWord since
|
||||
# a WikiWords can be a substring of a WikiLink.
|
||||
class Link < WikiLink
|
||||
|
||||
unless defined? WIKI_LINK
|
||||
WIKI_LINK = /(":)?\[\[([^\]]+)\]\]/
|
||||
LINK_TYPE_SEPARATION = Regexp.new('^(.+):((file)|(pic))$', 0, 'utf-8')
|
||||
ALIAS_SEPARATION = Regexp.new('^(.+)\|(.+)$', 0, 'utf-8')
|
||||
end
|
||||
|
||||
def self.pattern() WIKI_LINK end
|
||||
|
||||
def initialize(match_data)
|
||||
super(match_data)
|
||||
@textile_link_suffix, @page_name = match_data[1..2]
|
||||
@link_text = @page_name
|
||||
separate_link_type
|
||||
separate_alias
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# if link wihin the brackets has a form of [[filename:file]] or [[filename:pic]],
|
||||
# this means a link to a picture or a file
|
||||
def separate_link_type
|
||||
link_type_match = LINK_TYPE_SEPARATION.match(@page_name)
|
||||
if link_type_match
|
||||
@link_text = @page_name = link_type_match[1]
|
||||
@link_type = link_type_match[2..3].compact[0]
|
||||
end
|
||||
end
|
||||
|
||||
# link text may be different from page name. this will look like [[actual page|link text]]
|
||||
def separate_alias
|
||||
alias_match = ALIAS_SEPARATION.match(@page_name)
|
||||
if alias_match
|
||||
@page_name, @link_text = alias_match[1..2]
|
||||
end
|
||||
# note that [[filename|link text:file]] is also supported
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,45 +1,45 @@
|
|||
require 'instiki_errors'
|
||||
|
||||
class FileYard
|
||||
|
||||
attr_reader :files_path
|
||||
|
||||
def initialize(files_path)
|
||||
@files_path = files_path
|
||||
@files = Dir["#{files_path}/*"].collect{|path| File.basename(path) if File.file?(path) }.compact
|
||||
end
|
||||
|
||||
def upload_file(name, io)
|
||||
sanitize_file_name(name)
|
||||
if io.kind_of?(Tempfile)
|
||||
io.close
|
||||
FileUtils.mv(io.path, file_path(name))
|
||||
else
|
||||
File.open(file_path(name), 'wb') { |f| f.write(io.read) }
|
||||
end
|
||||
# just in case, restrict read access and prohibit write access to the uploaded file
|
||||
FileUtils.chmod(0440, file_path(name))
|
||||
end
|
||||
|
||||
def files
|
||||
Dir["#{files_path}/*"].collect{|path| File.basename(path) if File.file?(path)}.compact
|
||||
end
|
||||
|
||||
def has_file?(name)
|
||||
files.include?(name)
|
||||
end
|
||||
|
||||
def file_path(name)
|
||||
"#{files_path}/#{name}"
|
||||
end
|
||||
|
||||
SANE_FILE_NAME = /[-_\.A-Za-z0-9]{1,255}/
|
||||
|
||||
def sanitize_file_name(name)
|
||||
unless name =~ SANE_FILE_NAME
|
||||
raise Instiki::ValidationError.new("Invalid file name: '#{name}'.\n" +
|
||||
"Only latin characters, digits, dots, underscores and dashes are accepted.")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
require 'instiki_errors'
|
||||
|
||||
class FileYard
|
||||
|
||||
attr_reader :files_path
|
||||
|
||||
def initialize(files_path)
|
||||
@files_path = files_path
|
||||
@files = Dir["#{files_path}/*"].collect{|path| File.basename(path) if File.file?(path) }.compact
|
||||
end
|
||||
|
||||
def upload_file(name, io)
|
||||
sanitize_file_name(name)
|
||||
if io.kind_of?(Tempfile)
|
||||
io.close
|
||||
FileUtils.mv(io.path, file_path(name))
|
||||
else
|
||||
File.open(file_path(name), 'wb') { |f| f.write(io.read) }
|
||||
end
|
||||
# just in case, restrict read access and prohibit write access to the uploaded file
|
||||
FileUtils.chmod(0440, file_path(name))
|
||||
end
|
||||
|
||||
def files
|
||||
Dir["#{files_path}/*"].collect{|path| File.basename(path) if File.file?(path)}.compact
|
||||
end
|
||||
|
||||
def has_file?(name)
|
||||
files.include?(name)
|
||||
end
|
||||
|
||||
def file_path(name)
|
||||
"#{files_path}/#{name}"
|
||||
end
|
||||
|
||||
SANE_FILE_NAME = /[-_\.A-Za-z0-9]{1,255}/
|
||||
|
||||
def sanitize_file_name(name)
|
||||
unless name =~ SANE_FILE_NAME
|
||||
raise Instiki::ValidationError.new("Invalid file name: '#{name}'.\n" +
|
||||
"Only latin characters, digits, dots, underscores and dashes are accepted.")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
184
app/models/page.rb
Executable file → Normal file
184
app/models/page.rb
Executable file → Normal file
|
@ -1,92 +1,92 @@
|
|||
require 'date'
|
||||
require 'page_lock'
|
||||
require 'revision'
|
||||
require 'wiki_words'
|
||||
require 'chunks/wiki'
|
||||
|
||||
class Page
|
||||
include PageLock
|
||||
|
||||
attr_reader :name, :web
|
||||
attr_accessor :revisions
|
||||
|
||||
def initialize(web, name, content, created_at, author)
|
||||
@web, @name, @revisions = web, name, []
|
||||
revise(content, created_at, author)
|
||||
end
|
||||
|
||||
def revise(content, created_at, author)
|
||||
|
||||
if not @revisions.empty? and content == @revisions.last.content
|
||||
raise Instiki::ValidationError.new(
|
||||
"You have tried to save page '#{name}' without changing its content")
|
||||
end
|
||||
|
||||
# A user may change a page, look at it and make some more changes - several times.
|
||||
# Not to record every such iteration as a new revision, if the previous revision was done
|
||||
# by the same author, not more than 30 minutes ago, then update the last revision instead of
|
||||
# creating a new one
|
||||
if !@revisions.empty? && continous_revision?(created_at, author)
|
||||
@revisions.last.created_at = created_at
|
||||
@revisions.last.content = content
|
||||
@revisions.last.clear_display_cache
|
||||
else
|
||||
@revisions << Revision.new(self, @revisions.length, content, created_at, author)
|
||||
end
|
||||
|
||||
web.refresh_pages_with_references(name) if @revisions.length == 1
|
||||
end
|
||||
|
||||
def rollback(revision_number, created_at, author_ip = nil)
|
||||
roll_back_revision = @revisions[revision_number].dup
|
||||
revise(roll_back_revision.content, created_at, Author.new(roll_back_revision.author, author_ip))
|
||||
end
|
||||
|
||||
def revisions?
|
||||
revisions.length > 1
|
||||
end
|
||||
|
||||
def revised_on
|
||||
created_on
|
||||
end
|
||||
|
||||
def in_category?(cat)
|
||||
cat.nil? || cat.empty? || categories.include?(cat)
|
||||
end
|
||||
|
||||
def categories
|
||||
display_content.find_chunks(Category).map { |cat| cat.list }.flatten
|
||||
end
|
||||
|
||||
def authors
|
||||
revisions.collect { |rev| rev.author }
|
||||
end
|
||||
|
||||
def references
|
||||
web.select.pages_that_reference(name)
|
||||
end
|
||||
|
||||
# Returns the original wiki-word name as separate words, so "MyPage" becomes "My Page".
|
||||
def plain_name
|
||||
web.brackets_only ? name : WikiWords.separate(name)
|
||||
end
|
||||
|
||||
def link(options = {})
|
||||
web.make_link(name, nil, options)
|
||||
end
|
||||
|
||||
def author_link(options = {})
|
||||
web.make_link(author, nil, options)
|
||||
end
|
||||
|
||||
private
|
||||
def continous_revision?(created_at, author)
|
||||
@revisions.last.author == author && @revisions.last.created_at + 30.minutes > created_at
|
||||
end
|
||||
|
||||
# Forward method calls to the current revision, so the page responds to all revision calls
|
||||
def method_missing(method_symbol)
|
||||
revisions.last.send(method_symbol)
|
||||
end
|
||||
|
||||
end
|
||||
require 'date'
|
||||
require 'page_lock'
|
||||
require 'revision'
|
||||
require 'wiki_words'
|
||||
require 'chunks/wiki'
|
||||
|
||||
class Page
|
||||
include PageLock
|
||||
|
||||
attr_reader :name, :web
|
||||
attr_accessor :revisions
|
||||
|
||||
def initialize(web, name, content, created_at, author)
|
||||
@web, @name, @revisions = web, name, []
|
||||
revise(content, created_at, author)
|
||||
end
|
||||
|
||||
def revise(content, created_at, author)
|
||||
|
||||
if not @revisions.empty? and content == @revisions.last.content
|
||||
raise Instiki::ValidationError.new(
|
||||
"You have tried to save page '#{name}' without changing its content")
|
||||
end
|
||||
|
||||
# A user may change a page, look at it and make some more changes - several times.
|
||||
# Not to record every such iteration as a new revision, if the previous revision was done
|
||||
# by the same author, not more than 30 minutes ago, then update the last revision instead of
|
||||
# creating a new one
|
||||
if !@revisions.empty? && continous_revision?(created_at, author)
|
||||
@revisions.last.created_at = created_at
|
||||
@revisions.last.content = content
|
||||
@revisions.last.clear_display_cache
|
||||
else
|
||||
@revisions << Revision.new(self, @revisions.length, content, created_at, author)
|
||||
end
|
||||
|
||||
web.refresh_pages_with_references(name) if @revisions.length == 1
|
||||
end
|
||||
|
||||
def rollback(revision_number, created_at, author_ip = nil)
|
||||
roll_back_revision = @revisions[revision_number].dup
|
||||
revise(roll_back_revision.content, created_at, Author.new(roll_back_revision.author, author_ip))
|
||||
end
|
||||
|
||||
def revisions?
|
||||
revisions.length > 1
|
||||
end
|
||||
|
||||
def revised_on
|
||||
created_on
|
||||
end
|
||||
|
||||
def in_category?(cat)
|
||||
cat.nil? || cat.empty? || categories.include?(cat)
|
||||
end
|
||||
|
||||
def categories
|
||||
display_content.find_chunks(Category).map { |cat| cat.list }.flatten
|
||||
end
|
||||
|
||||
def authors
|
||||
revisions.collect { |rev| rev.author }
|
||||
end
|
||||
|
||||
def references
|
||||
web.select.pages_that_reference(name)
|
||||
end
|
||||
|
||||
# Returns the original wiki-word name as separate words, so "MyPage" becomes "My Page".
|
||||
def plain_name
|
||||
web.brackets_only ? name : WikiWords.separate(name)
|
||||
end
|
||||
|
||||
def link(options = {})
|
||||
web.make_link(name, nil, options)
|
||||
end
|
||||
|
||||
def author_link(options = {})
|
||||
web.make_link(author, nil, options)
|
||||
end
|
||||
|
||||
private
|
||||
def continous_revision?(created_at, author)
|
||||
@revisions.last.author == author && @revisions.last.created_at + 30.minutes > created_at
|
||||
end
|
||||
|
||||
# Forward method calls to the current revision, so the page responds to all revision calls
|
||||
def method_missing(method_symbol)
|
||||
revisions.last.send(method_symbol)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
46
app/models/page_lock.rb
Executable file → Normal file
46
app/models/page_lock.rb
Executable file → Normal file
|
@ -1,24 +1,24 @@
|
|||
# Contains all the lock methods to be mixed in with the page
|
||||
module PageLock
|
||||
LOCKING_PERIOD = 30 * 60 # 30 minutes
|
||||
|
||||
def lock(time, locked_by)
|
||||
@locked_at, @locked_by = time, locked_by
|
||||
end
|
||||
|
||||
def lock_duration(time)
|
||||
((time - @locked_at) / 60).to_i unless @locked_at.nil?
|
||||
end
|
||||
|
||||
def unlock
|
||||
@locked_at = nil
|
||||
end
|
||||
|
||||
def locked?(comparison_time)
|
||||
@locked_at + LOCKING_PERIOD > comparison_time unless @locked_at.nil?
|
||||
end
|
||||
|
||||
def locked_by_link
|
||||
web.make_link(@locked_by)
|
||||
end
|
||||
# Contains all the lock methods to be mixed in with the page
|
||||
module PageLock
|
||||
LOCKING_PERIOD = 30 * 60 # 30 minutes
|
||||
|
||||
def lock(time, locked_by)
|
||||
@locked_at, @locked_by = time, locked_by
|
||||
end
|
||||
|
||||
def lock_duration(time)
|
||||
((time - @locked_at) / 60).to_i unless @locked_at.nil?
|
||||
end
|
||||
|
||||
def unlock
|
||||
@locked_at = nil
|
||||
end
|
||||
|
||||
def locked?(comparison_time)
|
||||
@locked_at + LOCKING_PERIOD > comparison_time unless @locked_at.nil?
|
||||
end
|
||||
|
||||
def locked_by_link
|
||||
web.make_link(@locked_by)
|
||||
end
|
||||
end
|
144
app/models/page_set.rb
Executable file → Normal file
144
app/models/page_set.rb
Executable file → Normal file
|
@ -1,73 +1,73 @@
|
|||
# Container for a set of pages with methods for manipulation.
|
||||
|
||||
class PageSet < Array
|
||||
attr_reader :web
|
||||
|
||||
def initialize(web, pages = nil, condition = nil)
|
||||
@web = web
|
||||
# if pages is not specified, make a list of all pages in the web
|
||||
if pages.nil?
|
||||
super(web.pages.values)
|
||||
# otherwise use specified pages and condition to produce a set of pages
|
||||
elsif condition.nil?
|
||||
super(pages)
|
||||
else
|
||||
super(pages.select { |page| condition[page] })
|
||||
end
|
||||
end
|
||||
|
||||
def most_recent_revision
|
||||
self.map { |page| page.created_at }.max || Time.at(0)
|
||||
end
|
||||
|
||||
|
||||
def by_name
|
||||
PageSet.new(@web, sort_by { |page| page.name })
|
||||
end
|
||||
|
||||
alias :sort :by_name
|
||||
|
||||
def by_revision
|
||||
PageSet.new(@web, sort_by { |page| page.created_at }).reverse
|
||||
end
|
||||
|
||||
def pages_that_reference(page_name)
|
||||
self.select { |page| page.wiki_words.include?(page_name) }
|
||||
end
|
||||
|
||||
def pages_authored_by(author)
|
||||
self.select { |page| page.authors.include?(author) }
|
||||
end
|
||||
|
||||
def characters
|
||||
self.inject(0) { |chars,page| chars += page.content.size }
|
||||
end
|
||||
|
||||
# Returns all the orphaned pages in this page set. That is,
|
||||
# pages in this set for which there is no reference in the web.
|
||||
# The HomePage and author pages are always assumed to have
|
||||
# references and so cannot be orphans
|
||||
def orphaned_pages
|
||||
references = web.select.wiki_words + ["HomePage"] + web.select.authors
|
||||
self.reject { |page| references.include?(page.name) }
|
||||
end
|
||||
|
||||
# Returns all the wiki words in this page set for which
|
||||
# there are no pages in this page set's web
|
||||
def wanted_pages
|
||||
wiki_words - web.select.names
|
||||
end
|
||||
|
||||
def names
|
||||
self.map { |page| page.name }
|
||||
end
|
||||
|
||||
def wiki_words
|
||||
self.inject([]) { |wiki_words, page| wiki_words << page.wiki_words }.flatten.uniq
|
||||
end
|
||||
|
||||
def authors
|
||||
self.inject([]) { |authors, page| authors << page.authors }.flatten.uniq.sort
|
||||
end
|
||||
|
||||
# Container for a set of pages with methods for manipulation.
|
||||
|
||||
class PageSet < Array
|
||||
attr_reader :web
|
||||
|
||||
def initialize(web, pages = nil, condition = nil)
|
||||
@web = web
|
||||
# if pages is not specified, make a list of all pages in the web
|
||||
if pages.nil?
|
||||
super(web.pages.values)
|
||||
# otherwise use specified pages and condition to produce a set of pages
|
||||
elsif condition.nil?
|
||||
super(pages)
|
||||
else
|
||||
super(pages.select { |page| condition[page] })
|
||||
end
|
||||
end
|
||||
|
||||
def most_recent_revision
|
||||
self.map { |page| page.created_at }.max || Time.at(0)
|
||||
end
|
||||
|
||||
|
||||
def by_name
|
||||
PageSet.new(@web, sort_by { |page| page.name })
|
||||
end
|
||||
|
||||
alias :sort :by_name
|
||||
|
||||
def by_revision
|
||||
PageSet.new(@web, sort_by { |page| page.created_at }).reverse
|
||||
end
|
||||
|
||||
def pages_that_reference(page_name)
|
||||
self.select { |page| page.wiki_words.include?(page_name) }
|
||||
end
|
||||
|
||||
def pages_authored_by(author)
|
||||
self.select { |page| page.authors.include?(author) }
|
||||
end
|
||||
|
||||
def characters
|
||||
self.inject(0) { |chars,page| chars += page.content.size }
|
||||
end
|
||||
|
||||
# Returns all the orphaned pages in this page set. That is,
|
||||
# pages in this set for which there is no reference in the web.
|
||||
# The HomePage and author pages are always assumed to have
|
||||
# references and so cannot be orphans
|
||||
def orphaned_pages
|
||||
references = web.select.wiki_words + ["HomePage"] + web.select.authors
|
||||
self.reject { |page| references.include?(page.name) }
|
||||
end
|
||||
|
||||
# Returns all the wiki words in this page set for which
|
||||
# there are no pages in this page set's web
|
||||
def wanted_pages
|
||||
wiki_words - web.select.names
|
||||
end
|
||||
|
||||
def names
|
||||
self.map { |page| page.name }
|
||||
end
|
||||
|
||||
def wiki_words
|
||||
self.inject([]) { |wiki_words, page| wiki_words << page.wiki_words }.flatten.uniq
|
||||
end
|
||||
|
||||
def authors
|
||||
self.inject([]) { |authors, page| authors << page.authors }.flatten.uniq.sort
|
||||
end
|
||||
|
||||
end
|
166
app/models/revision.rb
Executable file → Normal file
166
app/models/revision.rb
Executable file → Normal file
|
@ -1,83 +1,83 @@
|
|||
require 'diff'
|
||||
require 'wiki_content'
|
||||
require 'chunks/wiki'
|
||||
require 'date'
|
||||
require 'author'
|
||||
require 'page'
|
||||
|
||||
class Revision
|
||||
|
||||
attr_accessor :page, :number, :content, :created_at, :author
|
||||
|
||||
def initialize(page, number, content, created_at, author)
|
||||
@page, @number, @created_at, @author = page, number, created_at, author
|
||||
self.content = content
|
||||
end
|
||||
|
||||
def created_on
|
||||
Date.new(@created_at.year, @created_at.mon, @created_at.day)
|
||||
end
|
||||
|
||||
def pretty_created_at
|
||||
# Must use DateTime because Time doesn't support %e on at least some platforms
|
||||
DateTime.new(
|
||||
@created_at.year, @created_at.mon, @created_at.day, @created_at.hour, @created_at.min
|
||||
).strftime "%B %e, %Y %H:%M"
|
||||
end
|
||||
|
||||
def next_revision
|
||||
page.revisions[number + 1]
|
||||
end
|
||||
|
||||
def previous_revision
|
||||
number > 0 ? page.revisions[number - 1] : nil
|
||||
end
|
||||
|
||||
# Returns an array of all the WikiWords present in the content of this revision.
|
||||
def wiki_words
|
||||
unless @wiki_words_cache
|
||||
wiki_chunks = display_content.find_chunks(WikiChunk::WikiLink)
|
||||
@wiki_words_cache = wiki_chunks.map { |c| ( c.escaped_text ? nil : c.page_name ) }.compact.uniq
|
||||
end
|
||||
@wiki_words_cache
|
||||
end
|
||||
|
||||
# Returns an array of all the WikiWords present in the content of this revision.
|
||||
# that already exists as a page in the web.
|
||||
def existing_pages
|
||||
wiki_words.select { |wiki_word| page.web.pages[wiki_word] }
|
||||
end
|
||||
|
||||
# Returns an array of all the WikiWords present in the content of this revision
|
||||
# that *doesn't* already exists as a page in the web.
|
||||
def unexisting_pages
|
||||
wiki_words - existing_pages
|
||||
end
|
||||
|
||||
# Explicit check for new type of display cache with find_chunks method.
|
||||
# Ensures new version works with older snapshots.
|
||||
def display_content
|
||||
unless @display_cache && @display_cache.respond_to?(:find_chunks)
|
||||
@display_cache = WikiContent.new(self)
|
||||
end
|
||||
@display_cache
|
||||
end
|
||||
|
||||
def display_diff
|
||||
previous_revision ? HTMLDiff.diff(previous_revision.display_content, display_content) : display_content
|
||||
end
|
||||
|
||||
def clear_display_cache
|
||||
@display_cache = @published_cache = @wiki_words_cache = nil
|
||||
end
|
||||
|
||||
def display_published
|
||||
@published_cache = WikiContent.new(self, {:mode => :publish}) if @published_cache.nil?
|
||||
@published_cache
|
||||
end
|
||||
|
||||
def display_content_for_export
|
||||
WikiContent.new(self, {:mode => :export} )
|
||||
end
|
||||
|
||||
end
|
||||
require 'diff'
|
||||
require 'wiki_content'
|
||||
require 'chunks/wiki'
|
||||
require 'date'
|
||||
require 'author'
|
||||
require 'page'
|
||||
|
||||
class Revision
|
||||
|
||||
attr_accessor :page, :number, :content, :created_at, :author
|
||||
|
||||
def initialize(page, number, content, created_at, author)
|
||||
@page, @number, @created_at, @author = page, number, created_at, author
|
||||
self.content = content
|
||||
end
|
||||
|
||||
def created_on
|
||||
Date.new(@created_at.year, @created_at.mon, @created_at.day)
|
||||
end
|
||||
|
||||
def pretty_created_at
|
||||
# Must use DateTime because Time doesn't support %e on at least some platforms
|
||||
DateTime.new(
|
||||
@created_at.year, @created_at.mon, @created_at.day, @created_at.hour, @created_at.min
|
||||
).strftime "%B %e, %Y %H:%M"
|
||||
end
|
||||
|
||||
def next_revision
|
||||
page.revisions[number + 1]
|
||||
end
|
||||
|
||||
def previous_revision
|
||||
number > 0 ? page.revisions[number - 1] : nil
|
||||
end
|
||||
|
||||
# Returns an array of all the WikiWords present in the content of this revision.
|
||||
def wiki_words
|
||||
unless @wiki_words_cache
|
||||
wiki_chunks = display_content.find_chunks(WikiChunk::WikiLink)
|
||||
@wiki_words_cache = wiki_chunks.map { |c| ( c.escaped_text ? nil : c.page_name ) }.compact.uniq
|
||||
end
|
||||
@wiki_words_cache
|
||||
end
|
||||
|
||||
# Returns an array of all the WikiWords present in the content of this revision.
|
||||
# that already exists as a page in the web.
|
||||
def existing_pages
|
||||
wiki_words.select { |wiki_word| page.web.pages[wiki_word] }
|
||||
end
|
||||
|
||||
# Returns an array of all the WikiWords present in the content of this revision
|
||||
# that *doesn't* already exists as a page in the web.
|
||||
def unexisting_pages
|
||||
wiki_words - existing_pages
|
||||
end
|
||||
|
||||
# Explicit check for new type of display cache with find_chunks method.
|
||||
# Ensures new version works with older snapshots.
|
||||
def display_content
|
||||
unless @display_cache && @display_cache.respond_to?(:find_chunks)
|
||||
@display_cache = WikiContent.new(self)
|
||||
end
|
||||
@display_cache
|
||||
end
|
||||
|
||||
def display_diff
|
||||
previous_revision ? HTMLDiff.diff(previous_revision.display_content, display_content) : display_content
|
||||
end
|
||||
|
||||
def clear_display_cache
|
||||
@display_cache = @published_cache = @wiki_words_cache = nil
|
||||
end
|
||||
|
||||
def display_published
|
||||
@published_cache = WikiContent.new(self, {:mode => :publish}) if @published_cache.nil?
|
||||
@published_cache
|
||||
end
|
||||
|
||||
def display_content_for_export
|
||||
WikiContent.new(self, {:mode => :export} )
|
||||
end
|
||||
|
||||
end
|
||||
|
|
308
app/models/web.rb
Executable file → Normal file
308
app/models/web.rb
Executable file → Normal file
|
@ -1,155 +1,155 @@
|
|||
require "cgi"
|
||||
require "page"
|
||||
require "page_set"
|
||||
require "wiki_words"
|
||||
require "zip/zip"
|
||||
|
||||
class Web
|
||||
attr_accessor :name, :address, :password, :markup, :color, :safe_mode, :pages
|
||||
attr_accessor :additional_style, :published, :brackets_only, :count_pages, :allow_uploads
|
||||
|
||||
def initialize(parent_wiki, name, address, password = nil)
|
||||
@wiki, @name, @address, @password = parent_wiki, name, address, password
|
||||
|
||||
# default values
|
||||
@markup = :textile
|
||||
@color = '008B26'
|
||||
@safe_mode = false
|
||||
@pages = {}
|
||||
@allow_uploads = true
|
||||
@additional_style = nil
|
||||
@published = false
|
||||
@brackets_only = false
|
||||
@count_pages = false
|
||||
@allow_uploads = true
|
||||
end
|
||||
|
||||
def add_page(page)
|
||||
@pages[page.name] = page
|
||||
end
|
||||
|
||||
def remove_pages(pages_to_be_removed)
|
||||
pages.delete_if { |page_name, page| pages_to_be_removed.include?(page) }
|
||||
end
|
||||
|
||||
def select(&condition)
|
||||
PageSet.new(self, @pages.values, condition)
|
||||
end
|
||||
|
||||
def revised_on
|
||||
select.most_recent_revision
|
||||
end
|
||||
|
||||
def authors
|
||||
select.authors
|
||||
end
|
||||
|
||||
def categories
|
||||
select.map { |page| page.categories }.flatten.uniq.sort
|
||||
end
|
||||
|
||||
# Create a link for the given page name and link text based
|
||||
# on the render mode in options and whether the page exists
|
||||
# in the this web.
|
||||
def make_link(name, text = nil, options = {})
|
||||
text = CGI.escapeHTML(text || WikiWords.separate(name))
|
||||
mode = options[:mode]
|
||||
link_type = options[:link_type] || 'show'
|
||||
case link_type
|
||||
when 'show'
|
||||
make_page_link(mode, name, text)
|
||||
when 'file'
|
||||
make_file_link(mode, name, text)
|
||||
when 'pic'
|
||||
make_pic_link(mode, name, text)
|
||||
else
|
||||
raise "Unknown link type: #{link_type}"
|
||||
end
|
||||
end
|
||||
|
||||
def make_page_link(mode, name, text)
|
||||
link = CGI.escape(name)
|
||||
case mode
|
||||
when :export
|
||||
if has_page?(name) then "<a class=\"existingWikiWord\" href=\"#{link}.html\">#{text}</a>"
|
||||
else "<span class=\"newWikiWord\">#{text}</span>" end
|
||||
when :publish
|
||||
if has_page?(name) then "<a class=\"existingWikiWord\" href=\"../published/#{link}\">#{text}</a>"
|
||||
else "<span class=\"newWikiWord\">#{text}</span>" end
|
||||
else
|
||||
if has_page?(name)
|
||||
"<a class=\"existingWikiWord\" href=\"../show/#{link}\">#{text}</a>"
|
||||
else
|
||||
"<span class=\"newWikiWord\">#{text}<a href=\"../show/#{link}\">?</a></span>"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def make_file_link(mode, name, text)
|
||||
link = CGI.escape(name)
|
||||
case mode
|
||||
when :export
|
||||
if has_file?(name) then "<a class=\"existingWikiWord\" href=\"#{link}.html\">#{text}</a>"
|
||||
else "<span class=\"newWikiWord\">#{text}</span>" end
|
||||
when :publish
|
||||
if has_file?(name) then "<a class=\"existingWikiWord\" href=\"../published/#{link}\">#{text}</a>"
|
||||
else "<span class=\"newWikiWord\">#{text}</span>" end
|
||||
else
|
||||
if has_file?(name)
|
||||
"<a class=\"existingWikiWord\" href=\"../file/#{link}\">#{text}</a>"
|
||||
else
|
||||
"<span class=\"newWikiWord\">#{text}<a href=\"../file/#{link}\">?</a></span>"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def make_pic_link(mode, name, text)
|
||||
link = CGI.escape(name)
|
||||
case mode
|
||||
when :export
|
||||
if has_file?(name) then "<img alt=\"#{text}\" src=\"#{link}\" />"
|
||||
else "<img alt=\"#{text}\" src=\"no image\" />" end
|
||||
when :publish
|
||||
if has_file?(name) then "<img alt=\"#{text}\" src=\"#{link}\" />"
|
||||
else "<span class=\"newWikiWord\">#{text}</span>" end
|
||||
else
|
||||
if has_file?(name) then "<img alt=\"#{text}\" src=\"../pic/#{link}\" />"
|
||||
else "<span class=\"newWikiWord\">#{text}<a href=\"../pic/#{link}\">?</a></span>" end
|
||||
end
|
||||
end
|
||||
|
||||
def has_page?(name)
|
||||
pages[name]
|
||||
end
|
||||
|
||||
def has_file?(name)
|
||||
wiki.file_yard(self).has_file?(name)
|
||||
end
|
||||
|
||||
# Clears the display cache for all the pages with references to
|
||||
def refresh_pages_with_references(page_name)
|
||||
select.pages_that_reference(page_name).each { |page|
|
||||
page.revisions.each { |revision| revision.clear_display_cache }
|
||||
}
|
||||
end
|
||||
|
||||
def refresh_revisions
|
||||
select.each { |page| page.revisions.each { |revision| revision.clear_display_cache } }
|
||||
end
|
||||
|
||||
private
|
||||
# Returns an array of all the wiki words in any current revision
|
||||
def wiki_words
|
||||
pages.values.inject([]) { |wiki_words, page| wiki_words << page.wiki_words }.flatten.uniq
|
||||
end
|
||||
|
||||
# Returns an array of all the page names on this web
|
||||
def page_names
|
||||
pages.keys
|
||||
end
|
||||
|
||||
# This ensures compatibility with 0.9 storages
|
||||
def wiki
|
||||
@wiki ||= WikiService.instance
|
||||
end
|
||||
require "cgi"
|
||||
require "page"
|
||||
require "page_set"
|
||||
require "wiki_words"
|
||||
require "zip/zip"
|
||||
|
||||
class Web
|
||||
attr_accessor :name, :address, :password, :markup, :color, :safe_mode, :pages
|
||||
attr_accessor :additional_style, :published, :brackets_only, :count_pages, :allow_uploads
|
||||
|
||||
def initialize(parent_wiki, name, address, password = nil)
|
||||
@wiki, @name, @address, @password = parent_wiki, name, address, password
|
||||
|
||||
# default values
|
||||
@markup = :textile
|
||||
@color = '008B26'
|
||||
@safe_mode = false
|
||||
@pages = {}
|
||||
@allow_uploads = true
|
||||
@additional_style = nil
|
||||
@published = false
|
||||
@brackets_only = false
|
||||
@count_pages = false
|
||||
@allow_uploads = true
|
||||
end
|
||||
|
||||
def add_page(page)
|
||||
@pages[page.name] = page
|
||||
end
|
||||
|
||||
def remove_pages(pages_to_be_removed)
|
||||
pages.delete_if { |page_name, page| pages_to_be_removed.include?(page) }
|
||||
end
|
||||
|
||||
def select(&condition)
|
||||
PageSet.new(self, @pages.values, condition)
|
||||
end
|
||||
|
||||
def revised_on
|
||||
select.most_recent_revision
|
||||
end
|
||||
|
||||
def authors
|
||||
select.authors
|
||||
end
|
||||
|
||||
def categories
|
||||
select.map { |page| page.categories }.flatten.uniq.sort
|
||||
end
|
||||
|
||||
# Create a link for the given page name and link text based
|
||||
# on the render mode in options and whether the page exists
|
||||
# in the this web.
|
||||
def make_link(name, text = nil, options = {})
|
||||
text = CGI.escapeHTML(text || WikiWords.separate(name))
|
||||
mode = options[:mode]
|
||||
link_type = options[:link_type] || 'show'
|
||||
case link_type
|
||||
when 'show'
|
||||
make_page_link(mode, name, text)
|
||||
when 'file'
|
||||
make_file_link(mode, name, text)
|
||||
when 'pic'
|
||||
make_pic_link(mode, name, text)
|
||||
else
|
||||
raise "Unknown link type: #{link_type}"
|
||||
end
|
||||
end
|
||||
|
||||
def make_page_link(mode, name, text)
|
||||
link = CGI.escape(name)
|
||||
case mode
|
||||
when :export
|
||||
if has_page?(name) then "<a class=\"existingWikiWord\" href=\"#{link}.html\">#{text}</a>"
|
||||
else "<span class=\"newWikiWord\">#{text}</span>" end
|
||||
when :publish
|
||||
if has_page?(name) then "<a class=\"existingWikiWord\" href=\"../published/#{link}\">#{text}</a>"
|
||||
else "<span class=\"newWikiWord\">#{text}</span>" end
|
||||
else
|
||||
if has_page?(name)
|
||||
"<a class=\"existingWikiWord\" href=\"../show/#{link}\">#{text}</a>"
|
||||
else
|
||||
"<span class=\"newWikiWord\">#{text}<a href=\"../show/#{link}\">?</a></span>"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def make_file_link(mode, name, text)
|
||||
link = CGI.escape(name)
|
||||
case mode
|
||||
when :export
|
||||
if has_file?(name) then "<a class=\"existingWikiWord\" href=\"#{link}.html\">#{text}</a>"
|
||||
else "<span class=\"newWikiWord\">#{text}</span>" end
|
||||
when :publish
|
||||
if has_file?(name) then "<a class=\"existingWikiWord\" href=\"../published/#{link}\">#{text}</a>"
|
||||
else "<span class=\"newWikiWord\">#{text}</span>" end
|
||||
else
|
||||
if has_file?(name)
|
||||
"<a class=\"existingWikiWord\" href=\"../file/#{link}\">#{text}</a>"
|
||||
else
|
||||
"<span class=\"newWikiWord\">#{text}<a href=\"../file/#{link}\">?</a></span>"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def make_pic_link(mode, name, text)
|
||||
link = CGI.escape(name)
|
||||
case mode
|
||||
when :export
|
||||
if has_file?(name) then "<img alt=\"#{text}\" src=\"#{link}\" />"
|
||||
else "<img alt=\"#{text}\" src=\"no image\" />" end
|
||||
when :publish
|
||||
if has_file?(name) then "<img alt=\"#{text}\" src=\"#{link}\" />"
|
||||
else "<span class=\"newWikiWord\">#{text}</span>" end
|
||||
else
|
||||
if has_file?(name) then "<img alt=\"#{text}\" src=\"../pic/#{link}\" />"
|
||||
else "<span class=\"newWikiWord\">#{text}<a href=\"../pic/#{link}\">?</a></span>" end
|
||||
end
|
||||
end
|
||||
|
||||
def has_page?(name)
|
||||
pages[name]
|
||||
end
|
||||
|
||||
def has_file?(name)
|
||||
wiki.file_yard(self).has_file?(name)
|
||||
end
|
||||
|
||||
# Clears the display cache for all the pages with references to
|
||||
def refresh_pages_with_references(page_name)
|
||||
select.pages_that_reference(page_name).each { |page|
|
||||
page.revisions.each { |revision| revision.clear_display_cache }
|
||||
}
|
||||
end
|
||||
|
||||
def refresh_revisions
|
||||
select.each { |page| page.revisions.each { |revision| revision.clear_display_cache } }
|
||||
end
|
||||
|
||||
private
|
||||
# Returns an array of all the wiki words in any current revision
|
||||
def wiki_words
|
||||
pages.values.inject([]) { |wiki_words, page| wiki_words << page.wiki_words }.flatten.uniq
|
||||
end
|
||||
|
||||
# Returns an array of all the page names on this web
|
||||
def page_names
|
||||
pages.keys
|
||||
end
|
||||
|
||||
# This ensures compatibility with 0.9 storages
|
||||
def wiki
|
||||
@wiki ||= WikiService.instance
|
||||
end
|
||||
end
|
192
app/models/wiki_content.rb
Executable file → Normal file
192
app/models/wiki_content.rb
Executable file → Normal file
|
@ -1,97 +1,97 @@
|
|||
require 'cgi'
|
||||
require 'chunks/engines'
|
||||
require 'chunks/category'
|
||||
require 'chunks/include'
|
||||
require 'chunks/wiki'
|
||||
require 'chunks/literal'
|
||||
require 'chunks/uri'
|
||||
require 'chunks/nowiki'
|
||||
|
||||
# Wiki content is just a string that can process itself with a chain of
|
||||
# actions. The actions can modify wiki content so that certain parts of
|
||||
# it are protected from being rendered by later actions.
|
||||
#
|
||||
# When wiki content is rendered, it can be interrogated to find out
|
||||
# which chunks were rendered. This means things like categories, wiki
|
||||
# links, can be determined.
|
||||
#
|
||||
# Exactly how wiki content is rendered is determined by a number of
|
||||
# settings that are optionally passed in to a constructor. The current
|
||||
# options are:
|
||||
# * :engine
|
||||
# => The structural markup engine to use (Textile, Markdown, RDoc)
|
||||
# * :engine_opts
|
||||
# => A list of options to pass to the markup engines (safe modes, etc)
|
||||
# * :pre_engine_actions
|
||||
# => A list of render actions or chunks to be processed before the
|
||||
# markup engine is applied. By default this is:
|
||||
# Category, Include, URIChunk, WikiChunk::Link, WikiChunk::Word
|
||||
# * :post_engine_actions
|
||||
# => A list of render actions or chunks to apply after the markup
|
||||
# engine. By default these are:
|
||||
# Literal::Pre, Literal::Tags
|
||||
# * :mode
|
||||
# => How should the content be rendered? For normal display (:display),
|
||||
# publishing (:publish) or export (:export)?
|
||||
#
|
||||
# AUTHOR: Mark Reid <mark @ threewordslong . com>
|
||||
# CREATED: 15th May 2004
|
||||
# UPDATED: 22nd May 2004
|
||||
class WikiContent < String
|
||||
|
||||
PRE_ENGINE_ACTIONS = [ NoWiki, Category, Include, WikiChunk::Link, URIChunk, LocalURIChunk,
|
||||
WikiChunk::Word ]
|
||||
POST_ENGINE_ACTIONS = [ Literal::Pre, Literal::Tags ]
|
||||
DEFAULT_OPTS = {
|
||||
:pre_engine_actions => PRE_ENGINE_ACTIONS,
|
||||
:post_engine_actions => POST_ENGINE_ACTIONS,
|
||||
:engine => Engines::Textile,
|
||||
:engine_opts => [],
|
||||
:mode => [:display]
|
||||
}
|
||||
|
||||
attr_reader :web, :options, :rendered, :chunks
|
||||
|
||||
# Create a new wiki content string from the given one.
|
||||
# The options are explained at the top of this file.
|
||||
def initialize(revision, options = {})
|
||||
@revision = revision
|
||||
@web = @revision.page.web
|
||||
|
||||
# Deep copy of DEFAULT_OPTS to ensure that changes to PRE/POST_ENGINE_ACTIONS stay local
|
||||
@options = Marshal.load(Marshal.dump(DEFAULT_OPTS)).update(options)
|
||||
@options[:engine] = Engines::MAP[@web.markup] || Engines::Textile
|
||||
@options[:engine_opts] = (@web.safe_mode ? [:filter_html, :filter_styles] : [])
|
||||
|
||||
@options[:pre_engine_actions].delete(WikiChunk::Word) if @web.brackets_only
|
||||
|
||||
super(@revision.content)
|
||||
|
||||
begin
|
||||
render!(@options[:pre_engine_actions] + [@options[:engine]] + @options[:post_engine_actions])
|
||||
# FIXME this is where all the parsing problems were shoved under the carpet
|
||||
# rescue => e
|
||||
# @rendered = e.message
|
||||
end
|
||||
end
|
||||
|
||||
# Call @web.page_link using current options.
|
||||
def page_link(name, text, link_type)
|
||||
@options[:link_type] = link_type || :show
|
||||
@web.make_link(name, text, @options)
|
||||
end
|
||||
|
||||
# Find all the chunks of the given types
|
||||
def find_chunks(chunk_type)
|
||||
rendered.select { |chunk| chunk.kind_of?(chunk_type) }
|
||||
end
|
||||
|
||||
# Render this content using the specified actions.
|
||||
def render!(chunk_types)
|
||||
@chunks = []
|
||||
chunk_types.each { |chunk_type| chunk_type.apply_to(self) }
|
||||
@rendered = @chunks.map { |chunk| chunk.unmask(self) }.compact
|
||||
(@chunks - @rendered).each { |chunk| chunk.revert(self) }
|
||||
end
|
||||
|
||||
require 'cgi'
|
||||
require 'chunks/engines'
|
||||
require 'chunks/category'
|
||||
require 'chunks/include'
|
||||
require 'chunks/wiki'
|
||||
require 'chunks/literal'
|
||||
require 'chunks/uri'
|
||||
require 'chunks/nowiki'
|
||||
|
||||
# Wiki content is just a string that can process itself with a chain of
|
||||
# actions. The actions can modify wiki content so that certain parts of
|
||||
# it are protected from being rendered by later actions.
|
||||
#
|
||||
# When wiki content is rendered, it can be interrogated to find out
|
||||
# which chunks were rendered. This means things like categories, wiki
|
||||
# links, can be determined.
|
||||
#
|
||||
# Exactly how wiki content is rendered is determined by a number of
|
||||
# settings that are optionally passed in to a constructor. The current
|
||||
# options are:
|
||||
# * :engine
|
||||
# => The structural markup engine to use (Textile, Markdown, RDoc)
|
||||
# * :engine_opts
|
||||
# => A list of options to pass to the markup engines (safe modes, etc)
|
||||
# * :pre_engine_actions
|
||||
# => A list of render actions or chunks to be processed before the
|
||||
# markup engine is applied. By default this is:
|
||||
# Category, Include, URIChunk, WikiChunk::Link, WikiChunk::Word
|
||||
# * :post_engine_actions
|
||||
# => A list of render actions or chunks to apply after the markup
|
||||
# engine. By default these are:
|
||||
# Literal::Pre, Literal::Tags
|
||||
# * :mode
|
||||
# => How should the content be rendered? For normal display (:display),
|
||||
# publishing (:publish) or export (:export)?
|
||||
#
|
||||
# AUTHOR: Mark Reid <mark @ threewordslong . com>
|
||||
# CREATED: 15th May 2004
|
||||
# UPDATED: 22nd May 2004
|
||||
class WikiContent < String
|
||||
|
||||
PRE_ENGINE_ACTIONS = [ NoWiki, Category, Include, WikiChunk::Link, URIChunk, LocalURIChunk,
|
||||
WikiChunk::Word ]
|
||||
POST_ENGINE_ACTIONS = [ Literal::Pre, Literal::Tags ]
|
||||
DEFAULT_OPTS = {
|
||||
:pre_engine_actions => PRE_ENGINE_ACTIONS,
|
||||
:post_engine_actions => POST_ENGINE_ACTIONS,
|
||||
:engine => Engines::Textile,
|
||||
:engine_opts => [],
|
||||
:mode => [:display]
|
||||
}
|
||||
|
||||
attr_reader :web, :options, :rendered, :chunks
|
||||
|
||||
# Create a new wiki content string from the given one.
|
||||
# The options are explained at the top of this file.
|
||||
def initialize(revision, options = {})
|
||||
@revision = revision
|
||||
@web = @revision.page.web
|
||||
|
||||
# Deep copy of DEFAULT_OPTS to ensure that changes to PRE/POST_ENGINE_ACTIONS stay local
|
||||
@options = Marshal.load(Marshal.dump(DEFAULT_OPTS)).update(options)
|
||||
@options[:engine] = Engines::MAP[@web.markup] || Engines::Textile
|
||||
@options[:engine_opts] = (@web.safe_mode ? [:filter_html, :filter_styles] : [])
|
||||
|
||||
@options[:pre_engine_actions].delete(WikiChunk::Word) if @web.brackets_only
|
||||
|
||||
super(@revision.content)
|
||||
|
||||
begin
|
||||
render!(@options[:pre_engine_actions] + [@options[:engine]] + @options[:post_engine_actions])
|
||||
# FIXME this is where all the parsing problems were shoved under the carpet
|
||||
# rescue => e
|
||||
# @rendered = e.message
|
||||
end
|
||||
end
|
||||
|
||||
# Call @web.page_link using current options.
|
||||
def page_link(name, text, link_type)
|
||||
@options[:link_type] = link_type || :show
|
||||
@web.make_link(name, text, @options)
|
||||
end
|
||||
|
||||
# Find all the chunks of the given types
|
||||
def find_chunks(chunk_type)
|
||||
rendered.select { |chunk| chunk.kind_of?(chunk_type) }
|
||||
end
|
||||
|
||||
# Render this content using the specified actions.
|
||||
def render!(chunk_types)
|
||||
@chunks = []
|
||||
chunk_types.each { |chunk_type| chunk_type.apply_to(self) }
|
||||
@rendered = @chunks.map { |chunk| chunk.unmask(self) }.compact
|
||||
(@chunks - @rendered).each { |chunk| chunk.revert(self) }
|
||||
end
|
||||
|
||||
end
|
444
app/models/wiki_service.rb
Executable file → Normal file
444
app/models/wiki_service.rb
Executable file → Normal file
|
@ -1,222 +1,222 @@
|
|||
require 'open-uri'
|
||||
require 'yaml'
|
||||
require 'madeleine'
|
||||
require 'madeleine/automatic'
|
||||
require 'madeleine/zmarshal'
|
||||
|
||||
require 'web'
|
||||
require 'page'
|
||||
require 'author'
|
||||
require 'file_yard'
|
||||
|
||||
module AbstractWikiService
|
||||
|
||||
attr_reader :webs, :system
|
||||
|
||||
def authenticate(password)
|
||||
password == (@system[:password] || 'instiki')
|
||||
end
|
||||
|
||||
def create_web(name, address, password = nil)
|
||||
@webs[address] = Web.new(self, name, address, password) unless @webs[address]
|
||||
end
|
||||
|
||||
def delete_web(address)
|
||||
@webs[address] = nil
|
||||
end
|
||||
|
||||
def file_yard(web)
|
||||
raise "Web #{@web.name} does not belong to this wiki service" unless @webs.values.include?(web)
|
||||
# TODO cache FileYards
|
||||
FileYard.new("#{self.storage_path}/#{web.address}")
|
||||
end
|
||||
|
||||
def init_wiki_service
|
||||
@webs = {}
|
||||
@system = {}
|
||||
end
|
||||
|
||||
def read_page(web_address, page_name)
|
||||
ApplicationController.logger.debug "Reading page '#{page_name}' from web '#{web_address}'"
|
||||
web = @webs[web_address]
|
||||
if web.nil?
|
||||
ApplicationController.logger.debug "Web '#{web_address}' not found"
|
||||
return nil
|
||||
else
|
||||
page = web.pages[page_name]
|
||||
ApplicationController.logger.debug "Page '#{page_name}' #{page.nil? ? 'not' : ''} found"
|
||||
return page
|
||||
end
|
||||
end
|
||||
|
||||
def remove_orphaned_pages(web_address)
|
||||
@webs[web_address].remove_pages(@webs[web_address].select.orphaned_pages)
|
||||
end
|
||||
|
||||
def revise_page(web_address, page_name, content, revised_on, author)
|
||||
page = read_page(web_address, page_name)
|
||||
page.revise(content, revised_on, author)
|
||||
page
|
||||
end
|
||||
|
||||
def rollback_page(web_address, page_name, revision_number, created_at, author_id = nil)
|
||||
page = read_page(web_address, page_name)
|
||||
page.rollback(revision_number, created_at, author_id)
|
||||
page
|
||||
end
|
||||
|
||||
def setup(password, web_name, web_address)
|
||||
@system[:password] = password
|
||||
create_web(web_name, web_address)
|
||||
end
|
||||
|
||||
def setup?
|
||||
not (@webs.empty?)
|
||||
end
|
||||
|
||||
def update_web(old_address, new_address, name, markup, color, additional_style, safe_mode = false,
|
||||
password = nil, published = false, brackets_only = false, count_pages = false,
|
||||
allow_uploads = true)
|
||||
if old_address != new_address
|
||||
@webs[new_address] = @webs[old_address]
|
||||
@webs.delete(old_address)
|
||||
@webs[new_address].address = new_address
|
||||
end
|
||||
|
||||
web = @webs[new_address]
|
||||
web.refresh_revisions if settings_changed?(web, markup, safe_mode, brackets_only)
|
||||
|
||||
web.name, web.markup, web.color, web.additional_style, web.safe_mode =
|
||||
name, markup, color, additional_style, safe_mode
|
||||
|
||||
web.password, web.published, web.brackets_only, web.count_pages, web.allow_uploads =
|
||||
password, published, brackets_only, count_pages, allow_uploads
|
||||
end
|
||||
|
||||
def write_page(web_address, page_name, content, written_on, author)
|
||||
page = Page.new(@webs[web_address], page_name, content, written_on, author)
|
||||
@webs[web_address].add_page(page)
|
||||
page
|
||||
end
|
||||
|
||||
def storage_path
|
||||
self.class.storage_path
|
||||
end
|
||||
|
||||
private
|
||||
def settings_changed?(web, markup, safe_mode, brackets_only)
|
||||
web.markup != markup ||
|
||||
web.safe_mode != safe_mode ||
|
||||
web.brackets_only != brackets_only
|
||||
end
|
||||
end
|
||||
|
||||
class WikiService
|
||||
|
||||
include AbstractWikiService
|
||||
include Madeleine::Automatic::Interceptor
|
||||
|
||||
# These methods do not change the state of persistent objects, and
|
||||
# should not be ogged by Madeleine
|
||||
automatic_read_only :authenticate, :read_page, :setup?, :webs, :storage_path, :file_yard
|
||||
|
||||
@@storage_path = './storage/'
|
||||
|
||||
class << self
|
||||
|
||||
def storage_path=(storage_path)
|
||||
@@storage_path = storage_path
|
||||
end
|
||||
|
||||
def storage_path
|
||||
@@storage_path
|
||||
end
|
||||
|
||||
def clean_storage
|
||||
MadeleineServer.clean_storage(self)
|
||||
end
|
||||
|
||||
def instance
|
||||
@madeleine ||= MadeleineServer.new(self)
|
||||
@system = @madeleine.system
|
||||
return @system
|
||||
end
|
||||
|
||||
def snapshot
|
||||
@madeleine.snapshot
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def initialize
|
||||
init_wiki_service
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class MadeleineServer
|
||||
|
||||
attr_reader :storage_path
|
||||
|
||||
# Clears all the command_log and snapshot files located in the storage directory, so the
|
||||
# database is essentially dropped and recreated as blank
|
||||
def self.clean_storage(service)
|
||||
begin
|
||||
Dir.foreach(service.storage_path) do |file|
|
||||
if file =~ /(command_log|snapshot)$/
|
||||
File.delete(File.join(service.storage_path, file))
|
||||
end
|
||||
end
|
||||
rescue
|
||||
Dir.mkdir(service.storage_path)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(service)
|
||||
@storage_path = service.storage_path
|
||||
@server = Madeleine::Automatic::AutomaticSnapshotMadeleine.new(service.storage_path,
|
||||
Madeleine::ZMarshal.new) {
|
||||
service.new
|
||||
}
|
||||
start_snapshot_thread
|
||||
end
|
||||
|
||||
def command_log_present?
|
||||
not Dir[storage_path + '/*.command_log'].empty?
|
||||
end
|
||||
|
||||
def snapshot
|
||||
@server.take_snapshot
|
||||
end
|
||||
|
||||
def start_snapshot_thread
|
||||
Thread.new(@server) {
|
||||
hours_since_last_snapshot = 0
|
||||
while true
|
||||
begin
|
||||
hours_since_last_snapshot += 1
|
||||
# Take a snapshot if there is a command log, or 24 hours
|
||||
# have passed since the last snapshot
|
||||
if command_log_present? or hours_since_last_snapshot >= 24
|
||||
ActionController::Base.logger.info "[#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}] " +
|
||||
'Taking a Madeleine snapshot'
|
||||
snapshot
|
||||
hours_since_last_snapshot = 0
|
||||
end
|
||||
sleep(1.hour)
|
||||
rescue => e
|
||||
ActionController::Base.logger.error(e)
|
||||
# wait for a minute (not to spoof the log with the same error)
|
||||
# and go back into the loop, to keep trying
|
||||
sleep(1.minute)
|
||||
ActionController::Base.logger.info("Retrying to save a snapshot")
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def system
|
||||
@server.system
|
||||
end
|
||||
|
||||
end
|
||||
require 'open-uri'
|
||||
require 'yaml'
|
||||
require 'madeleine'
|
||||
require 'madeleine/automatic'
|
||||
require 'madeleine/zmarshal'
|
||||
|
||||
require 'web'
|
||||
require 'page'
|
||||
require 'author'
|
||||
require 'file_yard'
|
||||
|
||||
module AbstractWikiService
|
||||
|
||||
attr_reader :webs, :system
|
||||
|
||||
def authenticate(password)
|
||||
password == (@system[:password] || 'instiki')
|
||||
end
|
||||
|
||||
def create_web(name, address, password = nil)
|
||||
@webs[address] = Web.new(self, name, address, password) unless @webs[address]
|
||||
end
|
||||
|
||||
def delete_web(address)
|
||||
@webs[address] = nil
|
||||
end
|
||||
|
||||
def file_yard(web)
|
||||
raise "Web #{@web.name} does not belong to this wiki service" unless @webs.values.include?(web)
|
||||
# TODO cache FileYards
|
||||
FileYard.new("#{self.storage_path}/#{web.address}")
|
||||
end
|
||||
|
||||
def init_wiki_service
|
||||
@webs = {}
|
||||
@system = {}
|
||||
end
|
||||
|
||||
def read_page(web_address, page_name)
|
||||
ApplicationController.logger.debug "Reading page '#{page_name}' from web '#{web_address}'"
|
||||
web = @webs[web_address]
|
||||
if web.nil?
|
||||
ApplicationController.logger.debug "Web '#{web_address}' not found"
|
||||
return nil
|
||||
else
|
||||
page = web.pages[page_name]
|
||||
ApplicationController.logger.debug "Page '#{page_name}' #{page.nil? ? 'not' : ''} found"
|
||||
return page
|
||||
end
|
||||
end
|
||||
|
||||
def remove_orphaned_pages(web_address)
|
||||
@webs[web_address].remove_pages(@webs[web_address].select.orphaned_pages)
|
||||
end
|
||||
|
||||
def revise_page(web_address, page_name, content, revised_on, author)
|
||||
page = read_page(web_address, page_name)
|
||||
page.revise(content, revised_on, author)
|
||||
page
|
||||
end
|
||||
|
||||
def rollback_page(web_address, page_name, revision_number, created_at, author_id = nil)
|
||||
page = read_page(web_address, page_name)
|
||||
page.rollback(revision_number, created_at, author_id)
|
||||
page
|
||||
end
|
||||
|
||||
def setup(password, web_name, web_address)
|
||||
@system[:password] = password
|
||||
create_web(web_name, web_address)
|
||||
end
|
||||
|
||||
def setup?
|
||||
not (@webs.empty?)
|
||||
end
|
||||
|
||||
def update_web(old_address, new_address, name, markup, color, additional_style, safe_mode = false,
|
||||
password = nil, published = false, brackets_only = false, count_pages = false,
|
||||
allow_uploads = true)
|
||||
if old_address != new_address
|
||||
@webs[new_address] = @webs[old_address]
|
||||
@webs.delete(old_address)
|
||||
@webs[new_address].address = new_address
|
||||
end
|
||||
|
||||
web = @webs[new_address]
|
||||
web.refresh_revisions if settings_changed?(web, markup, safe_mode, brackets_only)
|
||||
|
||||
web.name, web.markup, web.color, web.additional_style, web.safe_mode =
|
||||
name, markup, color, additional_style, safe_mode
|
||||
|
||||
web.password, web.published, web.brackets_only, web.count_pages, web.allow_uploads =
|
||||
password, published, brackets_only, count_pages, allow_uploads
|
||||
end
|
||||
|
||||
def write_page(web_address, page_name, content, written_on, author)
|
||||
page = Page.new(@webs[web_address], page_name, content, written_on, author)
|
||||
@webs[web_address].add_page(page)
|
||||
page
|
||||
end
|
||||
|
||||
def storage_path
|
||||
self.class.storage_path
|
||||
end
|
||||
|
||||
private
|
||||
def settings_changed?(web, markup, safe_mode, brackets_only)
|
||||
web.markup != markup ||
|
||||
web.safe_mode != safe_mode ||
|
||||
web.brackets_only != brackets_only
|
||||
end
|
||||
end
|
||||
|
||||
class WikiService
|
||||
|
||||
include AbstractWikiService
|
||||
include Madeleine::Automatic::Interceptor
|
||||
|
||||
# These methods do not change the state of persistent objects, and
|
||||
# should not be ogged by Madeleine
|
||||
automatic_read_only :authenticate, :read_page, :setup?, :webs, :storage_path, :file_yard
|
||||
|
||||
@@storage_path = './storage/'
|
||||
|
||||
class << self
|
||||
|
||||
def storage_path=(storage_path)
|
||||
@@storage_path = storage_path
|
||||
end
|
||||
|
||||
def storage_path
|
||||
@@storage_path
|
||||
end
|
||||
|
||||
def clean_storage
|
||||
MadeleineServer.clean_storage(self)
|
||||
end
|
||||
|
||||
def instance
|
||||
@madeleine ||= MadeleineServer.new(self)
|
||||
@system = @madeleine.system
|
||||
return @system
|
||||
end
|
||||
|
||||
def snapshot
|
||||
@madeleine.snapshot
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def initialize
|
||||
init_wiki_service
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class MadeleineServer
|
||||
|
||||
attr_reader :storage_path
|
||||
|
||||
# Clears all the command_log and snapshot files located in the storage directory, so the
|
||||
# database is essentially dropped and recreated as blank
|
||||
def self.clean_storage(service)
|
||||
begin
|
||||
Dir.foreach(service.storage_path) do |file|
|
||||
if file =~ /(command_log|snapshot)$/
|
||||
File.delete(File.join(service.storage_path, file))
|
||||
end
|
||||
end
|
||||
rescue
|
||||
Dir.mkdir(service.storage_path)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(service)
|
||||
@storage_path = service.storage_path
|
||||
@server = Madeleine::Automatic::AutomaticSnapshotMadeleine.new(service.storage_path,
|
||||
Madeleine::ZMarshal.new) {
|
||||
service.new
|
||||
}
|
||||
start_snapshot_thread
|
||||
end
|
||||
|
||||
def command_log_present?
|
||||
not Dir[storage_path + '/*.command_log'].empty?
|
||||
end
|
||||
|
||||
def snapshot
|
||||
@server.take_snapshot
|
||||
end
|
||||
|
||||
def start_snapshot_thread
|
||||
Thread.new(@server) {
|
||||
hours_since_last_snapshot = 0
|
||||
while true
|
||||
begin
|
||||
hours_since_last_snapshot += 1
|
||||
# Take a snapshot if there is a command log, or 24 hours
|
||||
# have passed since the last snapshot
|
||||
if command_log_present? or hours_since_last_snapshot >= 24
|
||||
ActionController::Base.logger.info "[#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}] " +
|
||||
'Taking a Madeleine snapshot'
|
||||
snapshot
|
||||
hours_since_last_snapshot = 0
|
||||
end
|
||||
sleep(1.hour)
|
||||
rescue => e
|
||||
ActionController::Base.logger.error(e)
|
||||
# wait for a minute (not to spoof the log with the same error)
|
||||
# and go back into the loop, to keep trying
|
||||
sleep(1.minute)
|
||||
ActionController::Base.logger.info("Retrying to save a snapshot")
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def system
|
||||
@server.system
|
||||
end
|
||||
|
||||
end
|
||||
|
|
46
app/models/wiki_words.rb
Executable file → Normal file
46
app/models/wiki_words.rb
Executable file → Normal file
|
@ -1,23 +1,23 @@
|
|||
# Contains all the methods for finding and replacing wiki words
|
||||
module WikiWords
|
||||
# In order of appearance: Latin, greek, cyrillian, armenian
|
||||
I18N_HIGHER_CASE_LETTERS =
|
||||
"À<EFBFBD>?ÂÃÄÅĀĄĂÆÇĆČĈĊĎ<C48A>?ÈÉÊËĒĘĚĔĖĜĞĠĢĤĦÌ<C4A6>?Î<>?ĪĨĬĮİIJĴĶ<C4B4>?ĽĹĻĿÑŃŇŅŊÒÓÔÕÖØŌ<C398>?ŎŒŔŘŖŚŠŞŜȘŤŢŦȚÙÚÛÜŪŮŰŬŨŲŴ<C5B2>?ŶŸŹŽŻ" +
|
||||
"ΑΒΓΔΕΖΗΘΙΚΛΜ<EFBFBD>?ΞΟΠΡΣΤΥΦΧΨΩ" +
|
||||
"ΆΈΉΊΌΎ<EFBFBD>?ѠѢѤѦѨѪѬѮѰѲѴѶѸѺѼѾҀҊҌҎ<D28C>?ҒҔҖҘҚҜҞҠҢҤҦҨҪҬҮҰҲҴҶҸҺҼҾ<D2BC>?ӃӅӇӉӋ<D389>?<3F>?ӒӔӖӘӚӜӞӠӢӤӦӨӪӬӮӰӲӴӸЖ" +
|
||||
"ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀ<EFBFBD>?ՂՃՄՅՆՇՈՉՊՋՌ<D58B>?<3F>?<3F>?ՑՒՓՔՕՖ"
|
||||
|
||||
I18N_LOWER_CASE_LETTERS =
|
||||
"àáâãäå<EFBFBD>?ąăæçć<C3A7>?ĉċ<C489>?đèéêëēęěĕėƒ<C497>?ğġģĥħìíîïīĩĭįıijĵķĸłľĺļŀñńňņʼnŋòóôõöø<C3B6>?ő<>?œŕřŗśšş<C5A1>?șťţŧțùúûüūůűŭũųŵýÿŷžżźÞþßſ<C39F>?ð" +
|
||||
"άέήίΰαβγδεζηθικλμνξοπ<EFBFBD>?ςστυφχψωϊϋό<CF8B>?ώ<>?" +
|
||||
"абвгдежзийклмнопр<EFBFBD>?туфхцчшщъыь<D18B>?ю<>?<3F>?ёђѓєѕіїјљћќ<D19B>?ўџѡѣѥѧѩѫѭѯѱѳѵѷѹѻѽѿ<D1BD>?ҋ<>?<3F>?ґғҕҗҙқ<D299>?ҟҡңҥҧҩҫҭүұҳҵҷҹһҽҿӀӂӄӆӈӊӌӎӑӓӕӗәӛ<D399>?ӟӡӣӥӧөӫӭӯӱӳӵӹ" +
|
||||
"աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտր<EFBFBD>?ւփքօֆև"
|
||||
|
||||
WIKI_WORD_PATTERN = '[A-Z' + I18N_HIGHER_CASE_LETTERS + '][a-z' + I18N_LOWER_CASE_LETTERS + ']+[A-Z' + I18N_HIGHER_CASE_LETTERS + ']\w+'
|
||||
CAMEL_CASED_WORD_BORDER = /([a-z#{I18N_LOWER_CASE_LETTERS}])([A-Z#{I18N_HIGHER_CASE_LETTERS}])/u
|
||||
|
||||
def self.separate(wiki_word)
|
||||
wiki_word.gsub(CAMEL_CASED_WORD_BORDER, '\1 \2')
|
||||
end
|
||||
|
||||
end
|
||||
# Contains all the methods for finding and replacing wiki words
|
||||
module WikiWords
|
||||
# In order of appearance: Latin, greek, cyrillian, armenian
|
||||
I18N_HIGHER_CASE_LETTERS =
|
||||
"À<EFBFBD>?ÂÃÄÅĀĄĂÆÇĆČĈĊĎ<C48A>?ÈÉÊËĒĘĚĔĖĜĞĠĢĤĦÌ<C4A6>?Î<>?ĪĨĬĮİIJĴĶ<C4B4>?ĽĹĻĿÑŃŇŅŊÒÓÔÕÖØŌ<C398>?ŎŒŔŘŖŚŠŞŜȘŤŢŦȚÙÚÛÜŪŮŰŬŨŲŴ<C5B2>?ŶŸŹŽŻ" +
|
||||
"ΑΒΓΔΕΖΗΘΙΚΛΜ<EFBFBD>?ΞΟΠΡΣΤΥΦΧΨΩ" +
|
||||
"ΆΈΉΊΌΎ<EFBFBD>?ѠѢѤѦѨѪѬѮѰѲѴѶѸѺѼѾҀҊҌҎ<D28C>?ҒҔҖҘҚҜҞҠҢҤҦҨҪҬҮҰҲҴҶҸҺҼҾ<D2BC>?ӃӅӇӉӋ<D389>?<3F>?ӒӔӖӘӚӜӞӠӢӤӦӨӪӬӮӰӲӴӸЖ" +
|
||||
"ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀ<EFBFBD>?ՂՃՄՅՆՇՈՉՊՋՌ<D58B>?<3F>?<3F>?ՑՒՓՔՕՖ"
|
||||
|
||||
I18N_LOWER_CASE_LETTERS =
|
||||
"àáâãäå<EFBFBD>?ąăæçć<C3A7>?ĉċ<C489>?đèéêëēęěĕėƒ<C497>?ğġģĥħìíîïīĩĭįıijĵķĸłľĺļŀñńňņʼnŋòóôõöø<C3B6>?ő<>?œŕřŗśšş<C5A1>?șťţŧțùúûüūůűŭũųŵýÿŷžżźÞþßſ<C39F>?ð" +
|
||||
"άέήίΰαβγδεζηθικλμνξοπ<EFBFBD>?ςστυφχψωϊϋό<CF8B>?ώ<>?" +
|
||||
"абвгдежзийклмнопр<EFBFBD>?туфхцчшщъыь<D18B>?ю<>?<3F>?ёђѓєѕіїјљћќ<D19B>?ўџѡѣѥѧѩѫѭѯѱѳѵѷѹѻѽѿ<D1BD>?ҋ<>?<3F>?ґғҕҗҙқ<D299>?ҟҡңҥҧҩҫҭүұҳҵҷҹһҽҿӀӂӄӆӈӊӌӎӑӓӕӗәӛ<D399>?ӟӡӣӥӧөӫӭӯӱӳӵӹ" +
|
||||
"աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտր<EFBFBD>?ւփքօֆև"
|
||||
|
||||
WIKI_WORD_PATTERN = '[A-Z' + I18N_HIGHER_CASE_LETTERS + '][a-z' + I18N_LOWER_CASE_LETTERS + ']+[A-Z' + I18N_HIGHER_CASE_LETTERS + ']\w+'
|
||||
CAMEL_CASED_WORD_BORDER = /([a-z#{I18N_LOWER_CASE_LETTERS}])([A-Z#{I18N_HIGHER_CASE_LETTERS}])/u
|
||||
|
||||
def self.separate(wiki_word)
|
||||
wiki_word.gsub(CAMEL_CASED_WORD_BORDER, '\1 \2')
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
<%
|
||||
@title = "Upload #{@file_name}"
|
||||
@hide_navigatio = false
|
||||
%>
|
||||
|
||||
<p>
|
||||
<%= form_tag({}, {:multipart => true}) %>
|
||||
<p>
|
||||
File to upload:
|
||||
<br/>
|
||||
<input type="file" name="file" size="40" />
|
||||
</p>
|
||||
<p>
|
||||
<input type="submit" value="Update" /> as
|
||||
<input type="text" name="author" id="authorName" value="<%= @author %>"
|
||||
onClick="this.value == 'AnonymousCoward' ? this.value = '' : true" />
|
||||
<% if @page %>
|
||||
| <a href="../file/">Cancel</a> <small>(unlocks page)</small>
|
||||
<% end %>
|
||||
</p>
|
||||
<%= end_form_tag %>
|
||||
<%
|
||||
@title = "Upload #{@file_name}"
|
||||
@hide_navigatio = false
|
||||
%>
|
||||
|
||||
<p>
|
||||
<%= form_tag({}, {:multipart => true}) %>
|
||||
<p>
|
||||
File to upload:
|
||||
<br/>
|
||||
<input type="file" name="file" size="40" />
|
||||
</p>
|
||||
<p>
|
||||
<input type="submit" value="Update" /> as
|
||||
<input type="text" name="author" id="authorName" value="<%= @author %>"
|
||||
onClick="this.value == 'AnonymousCoward' ? this.value = '' : true" />
|
||||
<% if @page %>
|
||||
| <a href="../file/">Cancel</a> <small>(unlocks page)</small>
|
||||
<% end %>
|
||||
</p>
|
||||
<%= end_form_tag %>
|
||||
</p>
|
|
@ -1,72 +1,72 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>
|
||||
<% if @page and (@page.name == 'HomePage') and (%w( show published print ).include?(@action_name)) %>
|
||||
<%= @web.name %>
|
||||
<% elsif @web %>
|
||||
<%= @title %> in <%= @web.name %>
|
||||
<% else %>
|
||||
<%= @title %>
|
||||
<% end %>
|
||||
</title>
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
|
||||
<style type="text/css">
|
||||
h1#pageName, .newWikiWord a, a.existingWikiWord, .newWikiWord a:hover, #TextileHelp h3 {
|
||||
color: #<%= @web ? @web.color : "393" %>;
|
||||
}
|
||||
|
||||
#Container, #Content {
|
||||
width: <%= @content_width || "600" %>px;
|
||||
}
|
||||
<%= File.read(RAILS_ROOT + '/public/stylesheets/instiki.css') if @inline_style %>
|
||||
</style>
|
||||
|
||||
<link rel="Stylesheet" href="/stylesheets/instiki.css" type="text/css" media="screen" />
|
||||
|
||||
<style type="text/css">
|
||||
<%= @style_additions %>
|
||||
<%= @web ? @web.additional_style : '' %>
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="Container">
|
||||
<div id="Content">
|
||||
|
||||
<h1 id="pageName">
|
||||
<% if @page and (@page.name == 'HomePage') and %w( show published print ).include?(@action_name) %>
|
||||
<%= @web.name %>
|
||||
<% elsif @web %>
|
||||
<small><%= @web.name %></small><br />
|
||||
<%= @title %>
|
||||
<% else %>
|
||||
<%= @title %>
|
||||
<% end %>
|
||||
</h1>
|
||||
|
||||
<% if @flash[:error] %> <div id="error">
|
||||
<hr/><p><%= @flash[:error].to_s %></p><hr/></div>
|
||||
<% end %>
|
||||
|
||||
<% if @flash[:info] %> <div id="info">
|
||||
<hr/><p><%= @flash[:info].to_s %></p><hr/></div>
|
||||
<% end %>
|
||||
|
||||
<%= render 'navigation' unless @web.nil? || @hide_navigation %>
|
||||
<%= @content_for_layout %>
|
||||
|
||||
<div id="footer">
|
||||
<hr/>
|
||||
<p>This site is running on <a href="http://instiki.org/">Instiki</a></p>
|
||||
<br/>
|
||||
<p>Powered by <a href="http://rubyonrails.com/">Ruby on Rails</a></p>
|
||||
</div>
|
||||
|
||||
</div> <!-- Content -->
|
||||
</div> <!-- Container -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>
|
||||
<% if @page and (@page.name == 'HomePage') and (%w( show published print ).include?(@action_name)) %>
|
||||
<%= @web.name %>
|
||||
<% elsif @web %>
|
||||
<%= @title %> in <%= @web.name %>
|
||||
<% else %>
|
||||
<%= @title %>
|
||||
<% end %>
|
||||
</title>
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
|
||||
<style type="text/css">
|
||||
h1#pageName, .newWikiWord a, a.existingWikiWord, .newWikiWord a:hover, #TextileHelp h3 {
|
||||
color: #<%= @web ? @web.color : "393" %>;
|
||||
}
|
||||
|
||||
#Container, #Content {
|
||||
width: <%= @content_width || "600" %>px;
|
||||
}
|
||||
<%= File.read(RAILS_ROOT + '/public/stylesheets/instiki.css') if @inline_style %>
|
||||
</style>
|
||||
|
||||
<link rel="Stylesheet" href="/stylesheets/instiki.css" type="text/css" media="screen" />
|
||||
|
||||
<style type="text/css">
|
||||
<%= @style_additions %>
|
||||
<%= @web ? @web.additional_style : '' %>
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="Container">
|
||||
<div id="Content">
|
||||
|
||||
<h1 id="pageName">
|
||||
<% if @page and (@page.name == 'HomePage') and %w( show published print ).include?(@action_name) %>
|
||||
<%= @web.name %>
|
||||
<% elsif @web %>
|
||||
<small><%= @web.name %></small><br />
|
||||
<%= @title %>
|
||||
<% else %>
|
||||
<%= @title %>
|
||||
<% end %>
|
||||
</h1>
|
||||
|
||||
<% if @flash[:error] %> <div id="error">
|
||||
<hr/><p><%= @flash[:error].to_s %></p><hr/></div>
|
||||
<% end %>
|
||||
|
||||
<% if @flash[:info] %> <div id="info">
|
||||
<hr/><p><%= @flash[:info].to_s %></p><hr/></div>
|
||||
<% end %>
|
||||
|
||||
<%= render 'navigation' unless @web.nil? || @hide_navigation %>
|
||||
<%= @content_for_layout %>
|
||||
|
||||
<div id="footer">
|
||||
<hr/>
|
||||
<p>This site is running on <a href="http://instiki.org/">Instiki</a></p>
|
||||
<br/>
|
||||
<p>Powered by <a href="http://rubyonrails.com/">Ruby on Rails</a></p>
|
||||
</div>
|
||||
|
||||
</div> <!-- Content -->
|
||||
</div> <!-- Container -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
30
app/views/markdown_help.rhtml
Executable file → Normal file
30
app/views/markdown_help.rhtml
Executable file → Normal file
|
@ -1,16 +1,16 @@
|
|||
<div id="TextileHelp" style="float: right; width: 250px; margin-top: 5px">
|
||||
<h3>Markdown formatting tips (<a target="_new" href="http://daringfireball.net/projects/markdown/syntax">advanced</a>)</h3>
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tr><td>_your text_</td><td class="arrow">→</td><td><em>your text</em></td></tr>
|
||||
<tr><td>**your text**</td><td class="arrow">→</td><td><strong>your text</strong></td></tr>
|
||||
<tr><td>`my code`</td><td class="arrow">→</td><td><code>my code</code></td></tr>
|
||||
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">→</td><td>• Bulleted list<br />• Second item</td></tr>
|
||||
<tr><td>1. Numbered list<br />1. Second item</td><td class="arrow">→</td><td>1. Numbered list<br />2. Second item</td></tr>
|
||||
<tr><td>[link name](URL)</td><td class="arrow">→</td><td><a href="URL">link name</a></td></tr>
|
||||
<tr><td>***</td><td class="arrow">→</td><td>Horizontal ruler</td></tr>
|
||||
<tr><td><http://url><br /><email@add.com></td><td class="arrow">→</td><td>Auto-linked</td></tr>
|
||||
<tr><td></td><td class="arrow">→</td><td>Image</td></tr>
|
||||
</table>
|
||||
|
||||
<%= render 'wiki_words_help' %>
|
||||
<div id="TextileHelp" style="float: right; width: 250px; margin-top: 5px">
|
||||
<h3>Markdown formatting tips (<a target="_new" href="http://daringfireball.net/projects/markdown/syntax">advanced</a>)</h3>
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tr><td>_your text_</td><td class="arrow">→</td><td><em>your text</em></td></tr>
|
||||
<tr><td>**your text**</td><td class="arrow">→</td><td><strong>your text</strong></td></tr>
|
||||
<tr><td>`my code`</td><td class="arrow">→</td><td><code>my code</code></td></tr>
|
||||
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">→</td><td>• Bulleted list<br />• Second item</td></tr>
|
||||
<tr><td>1. Numbered list<br />1. Second item</td><td class="arrow">→</td><td>1. Numbered list<br />2. Second item</td></tr>
|
||||
<tr><td>[link name](URL)</td><td class="arrow">→</td><td><a href="URL">link name</a></td></tr>
|
||||
<tr><td>***</td><td class="arrow">→</td><td>Horizontal ruler</td></tr>
|
||||
<tr><td><http://url><br /><email@add.com></td><td class="arrow">→</td><td>Auto-linked</td></tr>
|
||||
<tr><td></td><td class="arrow">→</td><td>Image</td></tr>
|
||||
</table>
|
||||
|
||||
<%= render 'wiki_words_help' %>
|
||||
</div>
|
50
app/views/navigation.rhtml
Executable file → Normal file
50
app/views/navigation.rhtml
Executable file → Normal file
|
@ -1,25 +1,25 @@
|
|||
<%
|
||||
def list_item(title, url, description, accesskey = nil)
|
||||
if @title == title
|
||||
"<b class=\"navOn\" title=\"#{description}\" accesskey=\"#{accesskey}\">#{title}</b>"
|
||||
else
|
||||
"<a href=\"#{url}\" title=\"#{description}\" accesskey=\"#{accesskey}\">#{title}</a>"
|
||||
end
|
||||
end
|
||||
%>
|
||||
|
||||
<form id="navigationForm" class="navigation" action="../search/" method="get" style="font-size: 10px">
|
||||
|
||||
<% if @action_name != "published" then %>
|
||||
<%= list_item "Home Page", "../show/HomePage", "Home, Sweet Home", "H" %> |
|
||||
<%= list_item "All Pages", "../list/", "Alphabetically sorted list of pages", "A" %> |
|
||||
<%= list_item "Recently Revised", "../recently_revised/", "Pages sorted by when they were last changed", "U" %> |
|
||||
<%= list_item "Authors", "../authors/", "Who wrote what" %> |
|
||||
<%= list_item "Feeds", "../feeds/", "Subscribe to changes by RSS" %> |
|
||||
<%= list_item "Export", "../export/", "Download a zip with all the pages in this wiki", "X" %> |
|
||||
<input type="text" id="searchField" name="query" style="font-size: 10px" value="Search" onClick="this.value == 'Search' ? this.value = '' : true" />
|
||||
<% else %>
|
||||
<%= list_item "Home Page", "../published/HomePage", "Home, Sweet Home", "H" %> |
|
||||
<% end%>
|
||||
|
||||
</form>
|
||||
<%
|
||||
def list_item(title, url, description, accesskey = nil)
|
||||
if @title == title
|
||||
"<b class=\"navOn\" title=\"#{description}\" accesskey=\"#{accesskey}\">#{title}</b>"
|
||||
else
|
||||
"<a href=\"#{url}\" title=\"#{description}\" accesskey=\"#{accesskey}\">#{title}</a>"
|
||||
end
|
||||
end
|
||||
%>
|
||||
|
||||
<form id="navigationForm" class="navigation" action="../search/" method="get" style="font-size: 10px">
|
||||
|
||||
<% if @action_name != "published" then %>
|
||||
<%= list_item "Home Page", "../show/HomePage", "Home, Sweet Home", "H" %> |
|
||||
<%= list_item "All Pages", "../list/", "Alphabetically sorted list of pages", "A" %> |
|
||||
<%= list_item "Recently Revised", "../recently_revised/", "Pages sorted by when they were last changed", "U" %> |
|
||||
<%= list_item "Authors", "../authors/", "Who wrote what" %> |
|
||||
<%= list_item "Feeds", "../feeds/", "Subscribe to changes by RSS" %> |
|
||||
<%= list_item "Export", "../export/", "Download a zip with all the pages in this wiki", "X" %> |
|
||||
<input type="text" id="searchField" name="query" style="font-size: 10px" value="Search" onClick="this.value == 'Search' ? this.value = '' : true" />
|
||||
<% else %>
|
||||
<%= list_item "Home Page", "../published/HomePage", "Home, Sweet Home", "H" %> |
|
||||
<% end%>
|
||||
|
||||
</form>
|
||||
|
|
30
app/views/rdoc_help.rhtml
Executable file → Normal file
30
app/views/rdoc_help.rhtml
Executable file → Normal file
|
@ -1,16 +1,16 @@
|
|||
<div id="TextileHelp" style="float: right; width: 250px; margin-top: 5px">
|
||||
<h3>RDoc formatting tips (<a target="_new" href="http://rdoc.sourceforge.net/doc/files/markup/simple_markup_rb.html">advanced</a>)</h3>
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tr><td>_your text_</td><td class="arrow">→</td><td><em>your text</em></td></tr>
|
||||
<tr><td>*your text*</td><td class="arrow">→</td><td><strong>your text</strong></td></tr>
|
||||
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">→</td><td>• Bulleted list<br />• Second item</td></tr>
|
||||
<tr><td>1. Numbered list<br />2. Second item</td><td class="arrow">→</td><td>1. Numbered list<br />2. Second item</td></tr>
|
||||
<tr><td>+my_code+</td><td class="arrow">→</td><td><code>my_code</code></td></tr>
|
||||
<tr><td>---</td><td class="arrow">→</td><td>Horizontal ruler</td></tr>
|
||||
<tr><td>[[URL linkname]]</td><td class="arrow">→</td><td><a href="URL">linkname</a></td></tr>
|
||||
<tr><td>http://url<br />mailto:e@add.com</td><td class="arrow">→</td><td>Auto-linked</td></tr>
|
||||
<tr><td>imageURL</td><td class="arrow">→</td><td>Image</td></tr>
|
||||
</table>
|
||||
|
||||
<%= render 'wiki_words_help' %>
|
||||
<div id="TextileHelp" style="float: right; width: 250px; margin-top: 5px">
|
||||
<h3>RDoc formatting tips (<a target="_new" href="http://rdoc.sourceforge.net/doc/files/markup/simple_markup_rb.html">advanced</a>)</h3>
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tr><td>_your text_</td><td class="arrow">→</td><td><em>your text</em></td></tr>
|
||||
<tr><td>*your text*</td><td class="arrow">→</td><td><strong>your text</strong></td></tr>
|
||||
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">→</td><td>• Bulleted list<br />• Second item</td></tr>
|
||||
<tr><td>1. Numbered list<br />2. Second item</td><td class="arrow">→</td><td>1. Numbered list<br />2. Second item</td></tr>
|
||||
<tr><td>+my_code+</td><td class="arrow">→</td><td><code>my_code</code></td></tr>
|
||||
<tr><td>---</td><td class="arrow">→</td><td>Horizontal ruler</td></tr>
|
||||
<tr><td>[[URL linkname]]</td><td class="arrow">→</td><td><a href="URL">linkname</a></td></tr>
|
||||
<tr><td>http://url<br />mailto:e@add.com</td><td class="arrow">→</td><td>Auto-linked</td></tr>
|
||||
<tr><td>imageURL</td><td class="arrow">→</td><td>Image</td></tr>
|
||||
</table>
|
||||
|
||||
<%= render 'wiki_words_help' %>
|
||||
</div>
|
54
app/views/textile_help.rhtml
Executable file → Normal file
54
app/views/textile_help.rhtml
Executable file → Normal file
|
@ -1,28 +1,28 @@
|
|||
<div id="TextileHelp" style="float: right; width: 250px; margin-top: 5px">
|
||||
<h3>Textile formatting tips (<a href="#" onClick="quickRedReference(); return false;">advanced</a>)</h3>
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tr><td>_your text_</td><td class="arrow">→</td><td><em>your text</em></td></tr>
|
||||
<tr><td>*your text*</td><td class="arrow">→</td><td><strong>your text</strong></td></tr>
|
||||
<tr><td>%{color:red}hello%</td><td class="arrow">→</td><td><span style="color: red;">hello</span></td></tr>
|
||||
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">→</td><td>• Bulleted list<br />• Second item</td></tr>
|
||||
<tr><td># Numbered list<br /># Second item</td><td class="arrow">→</td><td>1. Numbered list<br />2. Second item</td></tr>
|
||||
<tr><td>"linkname":URL</td><td class="arrow">→</td><td><a href="URL">linkname</a></td></tr>
|
||||
<tr><td>|a|table|row|<br />|b|table|row|</td><td class="arrow">→</td><td>Table</td></tr>
|
||||
<tr><td>http://url<br />email@address.com</td><td class="arrow">→</td><td>Auto-linked</td></tr>
|
||||
<tr><td>!imageURL!</td><td class="arrow">→</td><td>Image</td></tr>
|
||||
</table>
|
||||
|
||||
<%= render 'wiki_words_help' %>
|
||||
</div>
|
||||
|
||||
<script language="JavaScript">
|
||||
function quickRedReference() {
|
||||
window.open(
|
||||
"http://hobix.com/textile/quick.html",
|
||||
"redRef",
|
||||
"height=600,width=550,channelmode=0,dependent=0," +
|
||||
"directories=0,fullscreen=0,location=0,menubar=0," +
|
||||
"resizable=0,scrollbars=1,status=1,toolbar=0"
|
||||
);
|
||||
}
|
||||
<div id="TextileHelp" style="float: right; width: 250px; margin-top: 5px">
|
||||
<h3>Textile formatting tips (<a href="#" onClick="quickRedReference(); return false;">advanced</a>)</h3>
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tr><td>_your text_</td><td class="arrow">→</td><td><em>your text</em></td></tr>
|
||||
<tr><td>*your text*</td><td class="arrow">→</td><td><strong>your text</strong></td></tr>
|
||||
<tr><td>%{color:red}hello%</td><td class="arrow">→</td><td><span style="color: red;">hello</span></td></tr>
|
||||
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">→</td><td>• Bulleted list<br />• Second item</td></tr>
|
||||
<tr><td># Numbered list<br /># Second item</td><td class="arrow">→</td><td>1. Numbered list<br />2. Second item</td></tr>
|
||||
<tr><td>"linkname":URL</td><td class="arrow">→</td><td><a href="URL">linkname</a></td></tr>
|
||||
<tr><td>|a|table|row|<br />|b|table|row|</td><td class="arrow">→</td><td>Table</td></tr>
|
||||
<tr><td>http://url<br />email@address.com</td><td class="arrow">→</td><td>Auto-linked</td></tr>
|
||||
<tr><td>!imageURL!</td><td class="arrow">→</td><td>Image</td></tr>
|
||||
</table>
|
||||
|
||||
<%= render 'wiki_words_help' %>
|
||||
</div>
|
||||
|
||||
<script language="JavaScript">
|
||||
function quickRedReference() {
|
||||
window.open(
|
||||
"http://hobix.com/textile/quick.html",
|
||||
"redRef",
|
||||
"height=600,width=550,channelmode=0,dependent=0," +
|
||||
"directories=0,fullscreen=0,location=0,menubar=0," +
|
||||
"resizable=0,scrollbars=1,status=1,toolbar=0"
|
||||
);
|
||||
}
|
||||
</script>
|
22
app/views/wiki/authors.rhtml
Executable file → Normal file
22
app/views/wiki/authors.rhtml
Executable file → Normal file
|
@ -1,11 +1,11 @@
|
|||
<% @title = 'Authors' %>
|
||||
|
||||
<ul id="authorList">
|
||||
<% for author in @authors %>
|
||||
<li>
|
||||
<%= @web.make_link(author) %>
|
||||
co- or authored:
|
||||
<%= @web.select.pages_authored_by(author).collect { |page| page.link }.join ', ' %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% @title = 'Authors' %>
|
||||
|
||||
<ul id="authorList">
|
||||
<% for author in @authors %>
|
||||
<li>
|
||||
<%= @web.make_link(author) %>
|
||||
co- or authored:
|
||||
<%= @web.select.pages_authored_by(author).collect { |page| page.link }.join ', ' %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
|
58
app/views/wiki/edit.rhtml
Executable file → Normal file
58
app/views/wiki/edit.rhtml
Executable file → Normal file
|
@ -1,29 +1,29 @@
|
|||
<%
|
||||
@title = "Editing #{@page.name}"
|
||||
@content_width = 720
|
||||
@hide_navigation = true
|
||||
%>
|
||||
|
||||
<%= "<p style='color:red'>Please correct the error that caused this error in rendering:<br/><small>#{@params["msg"]}</small></p>" if @params["msg"] %>
|
||||
|
||||
<%= render("#{@web.markup}_help") if @web %>
|
||||
|
||||
<form id="editForm" action="../save/<%= @page.name %>" method="post" onSubmit="cleanAuthorName();">
|
||||
<p>
|
||||
<textarea name="content" style="width: 450px; height: 500px"><%= @page.content %></textarea>
|
||||
</p>
|
||||
<p>
|
||||
<input type="submit" value="Update" /> as
|
||||
<input type="text" name="author" id="authorName" value="<%= @author %>"
|
||||
onClick="this.value == 'AnonymousCoward' ? this.value = '' : true" />
|
||||
| <a href="../cancel_edit/<%= @page.name %>">Cancel</a> <small>(unlocks page)</small>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<script language="JavaScript1.2">
|
||||
function cleanAuthorName() {
|
||||
if (document.getElementById('authorName').value == "") {
|
||||
document.getElementById('authorName').value = 'AnonymousCoward';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<%
|
||||
@title = "Editing #{@page.name}"
|
||||
@content_width = 720
|
||||
@hide_navigation = true
|
||||
%>
|
||||
|
||||
<%= "<p style='color:red'>Please correct the error that caused this error in rendering:<br/><small>#{@params["msg"]}</small></p>" if @params["msg"] %>
|
||||
|
||||
<%= render("#{@web.markup}_help") if @web %>
|
||||
|
||||
<form id="editForm" action="../save/<%= @page.name %>" method="post" onSubmit="cleanAuthorName();">
|
||||
<p>
|
||||
<textarea name="content" style="width: 450px; height: 500px"><%= @page.content %></textarea>
|
||||
</p>
|
||||
<p>
|
||||
<input type="submit" value="Update" /> as
|
||||
<input type="text" name="author" id="authorName" value="<%= @author %>"
|
||||
onClick="this.value == 'AnonymousCoward' ? this.value = '' : true" />
|
||||
| <a href="../cancel_edit/<%= @page.name %>">Cancel</a> <small>(unlocks page)</small>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<script language="JavaScript1.2">
|
||||
function cleanAuthorName() {
|
||||
if (document.getElementById('authorName').value == "") {
|
||||
document.getElementById('authorName').value = 'AnonymousCoward';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
234
app/views/wiki/edit_web.rhtml
Executable file → Normal file
234
app/views/wiki/edit_web.rhtml
Executable file → Normal file
|
@ -1,117 +1,117 @@
|
|||
<% @title = "Edit Web" %>
|
||||
|
||||
<form action="../update_web" id="setup" method="post" onSubmit="cleanAddress(); return validateSetup()">
|
||||
<h2 style="margin-bottom: 3px">Name and address</h2>
|
||||
<div class="help">
|
||||
The name of the web is included in the title on all pages.
|
||||
The address is the base path that all pages within the web live beneath.
|
||||
Ex: the address "rails" gives URLs like <i>/rails/show/HomePage</i>.
|
||||
</div>
|
||||
|
||||
<div class="inputBox, disableAutoComplete">
|
||||
Name: <input type="text" id="name" name="name" value="<%= @web.name %>"
|
||||
onChange="proposeAddress();" />
|
||||
Address: <input type="text" id="address" name="address" value="<%= @web.address %>"
|
||||
onChange="cleanAddress();" />
|
||||
<i>(Letters and digits only)</i>
|
||||
</div>
|
||||
|
||||
<h2 style="margin-bottom: 3px">Specialize</h2>
|
||||
<div class="help">
|
||||
Turning safe mode on will strip HTML tags and stylesheet options from the content of all pages.
|
||||
Turning on "brackets only" will require all wiki words to be as [[wiki word]] and WikiWord
|
||||
won't work.
|
||||
Turning "allow uploads" on will let wiki users to upload pictures and other files to the wiki
|
||||
and include them on wiki pages.
|
||||
Additions to the stylesheet take precedence over the existing styles.
|
||||
<i>Hint:</i> View source on a page you want to style to find ID names on individual tags.
|
||||
<a href="#" onClick="document.getElementById('additionalStyle').style.display='block';return false;">
|
||||
See styles >>
|
||||
</a>
|
||||
</div>
|
||||
<div class="inputBox, disableAutoComplete">
|
||||
Markup:
|
||||
<select name="markup">
|
||||
<%= html_options({"Textile" => :textile, "Markdown" => :markdown, "RDoc" => :rdoc },
|
||||
@web.markup) %>
|
||||
</select>
|
||||
|
||||
|
||||
|
||||
Color:
|
||||
<select name="color">
|
||||
<%= html_options({ "Green" => "008B26", "Purple" => "504685", "Red" => "DA0006",
|
||||
"Orange" => "FA6F00", "Grey" => "8BA2B0" }, @web.color) %>
|
||||
</select>
|
||||
|
||||
|
||||
|
||||
<small>
|
||||
<input type="checkbox" name="safe_mode" <%= 'checked="on"' if @web.safe_mode %> /> Safe mode
|
||||
|
||||
<input type="checkbox" name="brackets_only" <%= 'checked="on"' if @web.brackets_only %> />
|
||||
Brackets only
|
||||
|
||||
<input type="checkbox" name="count_pages" <%= 'checked="on"' if @web.count_pages %> /> Count pages
|
||||
|
||||
<input type="checkbox" name="allow_uploads" <%= 'checked="on"' if @web.allow_uploads %> /> Allow uploads
|
||||
</small>
|
||||
|
||||
<textarea id="additionalStyle"
|
||||
style="display: none; margin-top: 10px; margin-bottom: 5px; width: 560px; height: 200px"
|
||||
name="additional_style"><%= @web.additional_style %>
|
||||
</textarea>
|
||||
</div>
|
||||
|
||||
<h2 style="margin-bottom: 3px">Password protection for this web (<%= @web.name %>)</h2>
|
||||
<div class="help">
|
||||
This is the password that visitors need to view and edit this web.
|
||||
Setting the password to nothing will remove the password protection.
|
||||
</div>
|
||||
<div class="inputBox">
|
||||
Password: <input class="disableAutoComplete" type="password" id="password"
|
||||
name="password" value="<%= @web.password %>" />
|
||||
|
||||
Verify: <input class="disableAutoComplete" type="password" id="password_check"
|
||||
value="<%= @web.password %>" name="password_check" />
|
||||
</div>
|
||||
|
||||
<h2 style="margin-bottom: 3px">Publish read-only version of this web (<%= @web.name %>)</h2>
|
||||
<div class="help">
|
||||
You can turn on a read-only version of this web that's accessible even when the regular web
|
||||
is password protected.
|
||||
The published version is accessible through URLs like /wiki/published/HomePage.
|
||||
</div>
|
||||
<div class="inputBox">
|
||||
<input type="checkbox" name="published" <%= 'checked="on"' if @web.published %> /> Publish this web
|
||||
</div>
|
||||
|
||||
<p align="right">
|
||||
<small>
|
||||
Enter system password
|
||||
<input type="password" class="disableAutoComplete" id="system_password" name="system_password" />
|
||||
and
|
||||
<input type="submit" value="Update Web" />
|
||||
<br/><br/>
|
||||
...or forget changes and <a href="/new_web/">create a new web</a>
|
||||
</small>
|
||||
</p>
|
||||
|
||||
</form>
|
||||
|
||||
<br/>
|
||||
<h1>Other administrative tasks</h1>
|
||||
|
||||
<form action="../remove_orphaned_pages" id="remove_orphaned_pages" method="post">
|
||||
<p align="right">
|
||||
<small>
|
||||
Clean up by entering system password
|
||||
<input type="password" id="system_password" name="system_password" />
|
||||
and
|
||||
<input type="submit" value="Delete Orphan Pages" />
|
||||
</small>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript" src="/javascripts/edit_web.js" />
|
||||
<script type="text/javascript">overrideAutocomplete()</script>
|
||||
<% @title = "Edit Web" %>
|
||||
|
||||
<form action="../update_web" id="setup" method="post" onSubmit="cleanAddress(); return validateSetup()">
|
||||
<h2 style="margin-bottom: 3px">Name and address</h2>
|
||||
<div class="help">
|
||||
The name of the web is included in the title on all pages.
|
||||
The address is the base path that all pages within the web live beneath.
|
||||
Ex: the address "rails" gives URLs like <i>/rails/show/HomePage</i>.
|
||||
</div>
|
||||
|
||||
<div class="inputBox, disableAutoComplete">
|
||||
Name: <input type="text" id="name" name="name" value="<%= @web.name %>"
|
||||
onChange="proposeAddress();" />
|
||||
Address: <input type="text" id="address" name="address" value="<%= @web.address %>"
|
||||
onChange="cleanAddress();" />
|
||||
<i>(Letters and digits only)</i>
|
||||
</div>
|
||||
|
||||
<h2 style="margin-bottom: 3px">Specialize</h2>
|
||||
<div class="help">
|
||||
Turning safe mode on will strip HTML tags and stylesheet options from the content of all pages.
|
||||
Turning on "brackets only" will require all wiki words to be as [[wiki word]] and WikiWord
|
||||
won't work.
|
||||
Turning "allow uploads" on will let wiki users to upload pictures and other files to the wiki
|
||||
and include them on wiki pages.
|
||||
Additions to the stylesheet take precedence over the existing styles.
|
||||
<i>Hint:</i> View source on a page you want to style to find ID names on individual tags.
|
||||
<a href="#" onClick="document.getElementById('additionalStyle').style.display='block';return false;">
|
||||
See styles >>
|
||||
</a>
|
||||
</div>
|
||||
<div class="inputBox, disableAutoComplete">
|
||||
Markup:
|
||||
<select name="markup">
|
||||
<%= html_options({"Textile" => :textile, "Markdown" => :markdown, "RDoc" => :rdoc },
|
||||
@web.markup) %>
|
||||
</select>
|
||||
|
||||
|
||||
|
||||
Color:
|
||||
<select name="color">
|
||||
<%= html_options({ "Green" => "008B26", "Purple" => "504685", "Red" => "DA0006",
|
||||
"Orange" => "FA6F00", "Grey" => "8BA2B0" }, @web.color) %>
|
||||
</select>
|
||||
|
||||
|
||||
|
||||
<small>
|
||||
<input type="checkbox" name="safe_mode" <%= 'checked="on"' if @web.safe_mode %> /> Safe mode
|
||||
|
||||
<input type="checkbox" name="brackets_only" <%= 'checked="on"' if @web.brackets_only %> />
|
||||
Brackets only
|
||||
|
||||
<input type="checkbox" name="count_pages" <%= 'checked="on"' if @web.count_pages %> /> Count pages
|
||||
|
||||
<input type="checkbox" name="allow_uploads" <%= 'checked="on"' if @web.allow_uploads %> /> Allow uploads
|
||||
</small>
|
||||
|
||||
<textarea id="additionalStyle"
|
||||
style="display: none; margin-top: 10px; margin-bottom: 5px; width: 560px; height: 200px"
|
||||
name="additional_style"><%= @web.additional_style %>
|
||||
</textarea>
|
||||
</div>
|
||||
|
||||
<h2 style="margin-bottom: 3px">Password protection for this web (<%= @web.name %>)</h2>
|
||||
<div class="help">
|
||||
This is the password that visitors need to view and edit this web.
|
||||
Setting the password to nothing will remove the password protection.
|
||||
</div>
|
||||
<div class="inputBox">
|
||||
Password: <input class="disableAutoComplete" type="password" id="password"
|
||||
name="password" value="<%= @web.password %>" />
|
||||
|
||||
Verify: <input class="disableAutoComplete" type="password" id="password_check"
|
||||
value="<%= @web.password %>" name="password_check" />
|
||||
</div>
|
||||
|
||||
<h2 style="margin-bottom: 3px">Publish read-only version of this web (<%= @web.name %>)</h2>
|
||||
<div class="help">
|
||||
You can turn on a read-only version of this web that's accessible even when the regular web
|
||||
is password protected.
|
||||
The published version is accessible through URLs like /wiki/published/HomePage.
|
||||
</div>
|
||||
<div class="inputBox">
|
||||
<input type="checkbox" name="published" <%= 'checked="on"' if @web.published %> /> Publish this web
|
||||
</div>
|
||||
|
||||
<p align="right">
|
||||
<small>
|
||||
Enter system password
|
||||
<input type="password" class="disableAutoComplete" id="system_password" name="system_password" />
|
||||
and
|
||||
<input type="submit" value="Update Web" />
|
||||
<br/><br/>
|
||||
...or forget changes and <a href="/new_web/">create a new web</a>
|
||||
</small>
|
||||
</p>
|
||||
|
||||
</form>
|
||||
|
||||
<br/>
|
||||
<h1>Other administrative tasks</h1>
|
||||
|
||||
<form action="../remove_orphaned_pages" id="remove_orphaned_pages" method="post">
|
||||
<p align="right">
|
||||
<small>
|
||||
Clean up by entering system password
|
||||
<input type="password" id="system_password" name="system_password" />
|
||||
and
|
||||
<input type="submit" value="Delete Orphan Pages" />
|
||||
</small>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript" src="/javascripts/edit_web.js" />
|
||||
<script type="text/javascript">overrideAutocomplete()</script>
|
||||
|
|
24
app/views/wiki/export.rhtml
Executable file → Normal file
24
app/views/wiki/export.rhtml
Executable file → Normal file
|
@ -1,12 +1,12 @@
|
|||
<% @title = "Export" %>
|
||||
|
||||
<p>You can export all the pages in this web as a zip file in either HTML (with working links and all) or the pure markup (to import in another wiki).</p>
|
||||
|
||||
<ul id="feedsList">
|
||||
<li><a href="../export_html">HTML</a>
|
||||
<li><a href="../export_markup">Markup (<%= @web.markup %>)</a>
|
||||
<% if OPTIONS[:pdflatex] && @web.markup == :textile %>
|
||||
<li><a href="../export_tex">TeX</a>
|
||||
<li><a href="../export_pdf">PDF</a>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% @title = "Export" %>
|
||||
|
||||
<p>You can export all the pages in this web as a zip file in either HTML (with working links and all) or the pure markup (to import in another wiki).</p>
|
||||
|
||||
<ul id="feedsList">
|
||||
<li><a href="../export_html">HTML</a>
|
||||
<li><a href="../export_markup">Markup (<%= @web.markup %>)</a>
|
||||
<% if OPTIONS[:pdflatex] && @web.markup == :textile %>
|
||||
<li><a href="../export_tex">TeX</a>
|
||||
<li><a href="../export_pdf">PDF</a>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
|
16
app/views/wiki/feeds.rhtml
Executable file → Normal file
16
app/views/wiki/feeds.rhtml
Executable file → Normal file
|
@ -1,8 +1,8 @@
|
|||
<% @title = "Feeds" %>
|
||||
|
||||
<p>You can subscribe to this wiki by RSS and get either just the headlines of the pages that change or the entire page.</p>
|
||||
|
||||
<ul id="feedsList">
|
||||
<li><a href="../rss_with_content<%= "?password=#{web.password}" if @web.password %>">Full content (RSS 2.0)</a>
|
||||
<li><a href="../rss_with_headlines<%= "?password=#{web.password}" if @web.password %>">Headlines (RSS 2.0)</a>
|
||||
</ul>
|
||||
<% @title = "Feeds" %>
|
||||
|
||||
<p>You can subscribe to this wiki by RSS and get either just the headlines of the pages that change or the entire page.</p>
|
||||
|
||||
<ul id="feedsList">
|
||||
<li><a href="../rss_with_content<%= "?password=#{web.password}" if @web.password %>">Full content (RSS 2.0)</a>
|
||||
<li><a href="../rss_with_headlines<%= "?password=#{web.password}" if @web.password %>">Headlines (RSS 2.0)</a>
|
||||
</ul>
|
||||
|
|
114
app/views/wiki/list.rhtml
Executable file → Normal file
114
app/views/wiki/list.rhtml
Executable file → Normal file
|
@ -1,57 +1,57 @@
|
|||
<% @title = "All Pages" %>
|
||||
|
||||
<% unless @categories.empty? %>
|
||||
<div id="categories">
|
||||
<strong>Categories</strong>:
|
||||
[<a href=".">Any</a>]
|
||||
<%= @category_links.join(', ') %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div id="allPages" style="float: left; width: 280px; margin-right: 30px">
|
||||
<% unless @pages_that_are_orphaned.empty? && @page_names_that_are_wanted.empty? %>
|
||||
<h2>
|
||||
All Pages
|
||||
<br/><small style="font-size: 12px"><i>All pages in <%= @set_name %> listed alphabetically</i></small>
|
||||
</h2>
|
||||
<% end %>
|
||||
|
||||
<ul><% for page in @pages_by_name %>
|
||||
<li><a href="../show/<%= page.name %>"><%= truncate(page.plain_name, 35) %></a></li>
|
||||
<% end %></ul>
|
||||
|
||||
<% if @web.count_pages %>
|
||||
<% total_chars = @pages_in_category.characters %>
|
||||
<p><small>All content: <%= total_chars %> chars / <%= sprintf("%-.1f", (total_chars / 2275 )) %> pages</small></p>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div style="float: left; width: 280px">
|
||||
<% unless @page_names_that_are_wanted.empty? %>
|
||||
<h2>
|
||||
Wanted Pages
|
||||
<br/><small style="font-size: 12px"><i>Unexisting pages that other pages in <%= @set_name %> reference</i></small>
|
||||
</h2>
|
||||
|
||||
<ul style="margin-bottom: 10px">
|
||||
<% for page_name in @page_names_that_are_wanted %>
|
||||
<li>
|
||||
<a href="../show/<%= page_name %>"><%= truncate(WikiWords.separate(page_name), 35) %></a>
|
||||
wanted by
|
||||
<%= @web.select.pages_that_reference(page_name).collect { |page| page.link }.join(", ") %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% end %>
|
||||
|
||||
<% unless @pages_that_are_orphaned.empty? %>
|
||||
<h2>
|
||||
Orphaned Pages
|
||||
<br/><small style="font-size: 12px"><i>Pages in <%= @set_name %> that no other page reference</i></small>
|
||||
</h2>
|
||||
|
||||
<ul style="margin-bottom: 35px">
|
||||
<% for page in @pages_that_are_orphaned %><li><a href="../show/<%= page.name %>"><%= truncate(page.plain_name, 35) %></a></li><% end %>
|
||||
</ul>
|
||||
<% end %>
|
||||
</div>
|
||||
<% @title = "All Pages" %>
|
||||
|
||||
<% unless @categories.empty? %>
|
||||
<div id="categories">
|
||||
<strong>Categories</strong>:
|
||||
[<a href=".">Any</a>]
|
||||
<%= @category_links.join(', ') %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div id="allPages" style="float: left; width: 280px; margin-right: 30px">
|
||||
<% unless @pages_that_are_orphaned.empty? && @page_names_that_are_wanted.empty? %>
|
||||
<h2>
|
||||
All Pages
|
||||
<br/><small style="font-size: 12px"><i>All pages in <%= @set_name %> listed alphabetically</i></small>
|
||||
</h2>
|
||||
<% end %>
|
||||
|
||||
<ul><% for page in @pages_by_name %>
|
||||
<li><a href="../show/<%= page.name %>"><%= truncate(page.plain_name, 35) %></a></li>
|
||||
<% end %></ul>
|
||||
|
||||
<% if @web.count_pages %>
|
||||
<% total_chars = @pages_in_category.characters %>
|
||||
<p><small>All content: <%= total_chars %> chars / <%= sprintf("%-.1f", (total_chars / 2275 )) %> pages</small></p>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div style="float: left; width: 280px">
|
||||
<% unless @page_names_that_are_wanted.empty? %>
|
||||
<h2>
|
||||
Wanted Pages
|
||||
<br/><small style="font-size: 12px"><i>Unexisting pages that other pages in <%= @set_name %> reference</i></small>
|
||||
</h2>
|
||||
|
||||
<ul style="margin-bottom: 10px">
|
||||
<% for page_name in @page_names_that_are_wanted %>
|
||||
<li>
|
||||
<a href="../show/<%= page_name %>"><%= truncate(WikiWords.separate(page_name), 35) %></a>
|
||||
wanted by
|
||||
<%= @web.select.pages_that_reference(page_name).collect { |page| page.link }.join(", ") %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% end %>
|
||||
|
||||
<% unless @pages_that_are_orphaned.empty? %>
|
||||
<h2>
|
||||
Orphaned Pages
|
||||
<br/><small style="font-size: 12px"><i>Pages in <%= @set_name %> that no other page reference</i></small>
|
||||
</h2>
|
||||
|
||||
<ul style="margin-bottom: 35px">
|
||||
<% for page in @pages_that_are_orphaned %><li><a href="../show/<%= page.name %>"><%= truncate(page.plain_name, 35) %></a></li><% end %>
|
||||
</ul>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
40
app/views/wiki/locked.rhtml
Executable file → Normal file
40
app/views/wiki/locked.rhtml
Executable file → Normal file
|
@ -1,20 +1,20 @@
|
|||
<% @title = "#{@page.plain_name} is locked" %>
|
||||
|
||||
<% if @page.lock_duration(Time.now) == 0 %>
|
||||
<p><%= @page.locked_by_link %> just started editing this page.</p>
|
||||
<% else %>
|
||||
<p><%= @page.locked_by_link %> has been editing this page for <%= @page.lock_duration(Time.now) %> minutes.</p>
|
||||
<% end %>
|
||||
|
||||
<p>
|
||||
<%= link_to 'Edit the page anyway',
|
||||
{:web => @web_name, :action => 'edit', :id => @page.name, :params => {'break_lock' => '1'} },
|
||||
{:accesskey => 'E'}
|
||||
%>
|
||||
|
||||
<%= link_to 'Cancel',
|
||||
{:web => @web_name, :action => 'show', :id => @page.name},
|
||||
{:accesskey => 'C'}
|
||||
%>
|
||||
|
||||
</p>
|
||||
<% @title = "#{@page.plain_name} is locked" %>
|
||||
|
||||
<% if @page.lock_duration(Time.now) == 0 %>
|
||||
<p><%= @page.locked_by_link %> just started editing this page.</p>
|
||||
<% else %>
|
||||
<p><%= @page.locked_by_link %> has been editing this page for <%= @page.lock_duration(Time.now) %> minutes.</p>
|
||||
<% end %>
|
||||
|
||||
<p>
|
||||
<%= link_to 'Edit the page anyway',
|
||||
{:web => @web_name, :action => 'edit', :id => @page.name, :params => {'break_lock' => '1'} },
|
||||
{:accesskey => 'E'}
|
||||
%>
|
||||
|
||||
<%= link_to 'Cancel',
|
||||
{:web => @web_name, :action => 'show', :id => @page.name},
|
||||
{:accesskey => 'C'}
|
||||
%>
|
||||
|
||||
</p>
|
||||
|
|
16
app/views/wiki/login.rhtml
Executable file → Normal file
16
app/views/wiki/login.rhtml
Executable file → Normal file
|
@ -1,8 +1,8 @@
|
|||
<% @title = "#{@web_name} Login" %><% @hide_navigation = true %>
|
||||
|
||||
<form action="authenticate" method="post">
|
||||
<p>
|
||||
<b>Password</b><br />
|
||||
<input type="password" name="password" />
|
||||
</p>
|
||||
</form>
|
||||
<% @title = "#{@web_name} Login" %><% @hide_navigation = true %>
|
||||
|
||||
<form action="authenticate" method="post">
|
||||
<p>
|
||||
<b>Password</b><br />
|
||||
<input type="password" name="password" />
|
||||
</p>
|
||||
</form>
|
||||
|
|
50
app/views/wiki/new.rhtml
Executable file → Normal file
50
app/views/wiki/new.rhtml
Executable file → Normal file
|
@ -1,25 +1,25 @@
|
|||
<%
|
||||
@title = "Creating #{WikiWords.separate(CGI.unescape(@page_name))}"
|
||||
@content_width = 720
|
||||
@hide_navigation = true
|
||||
%>
|
||||
|
||||
<%= render("#{@web.markup}_help") if @web %>
|
||||
|
||||
<form action="../save/<%= @page_name %>" method="post" onSubmit="cleanAuthorName();">
|
||||
<p>
|
||||
<textarea name="content" style="width: 450px; height: 500px"></textarea>
|
||||
</p>
|
||||
<p>
|
||||
<input type="submit" value="Create" /> as
|
||||
<input type="text" name="author" id="authorName" value="<%= @author %>" onClick="this.value == 'AnonymousCoward' ? this.value = '' : true" />
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<script language="JavaScript1.2">
|
||||
function cleanAuthorName() {
|
||||
if (document.getElementById('authorName').value == "") {
|
||||
document.getElementById('authorName').value = 'AnonymousCoward';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<%
|
||||
@title = "Creating #{WikiWords.separate(CGI.unescape(@page_name))}"
|
||||
@content_width = 720
|
||||
@hide_navigation = true
|
||||
%>
|
||||
|
||||
<%= render("#{@web.markup}_help") if @web %>
|
||||
|
||||
<form action="../save/<%= @page_name %>" method="post" onSubmit="cleanAuthorName();">
|
||||
<p>
|
||||
<textarea name="content" style="width: 450px; height: 500px"></textarea>
|
||||
</p>
|
||||
<p>
|
||||
<input type="submit" value="Create" /> as
|
||||
<input type="text" name="author" id="authorName" value="<%= @author %>" onClick="this.value == 'AnonymousCoward' ? this.value = '' : true" />
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<script language="JavaScript1.2">
|
||||
function cleanAuthorName() {
|
||||
if (document.getElementById('authorName').value == "") {
|
||||
document.getElementById('authorName').value = 'AnonymousCoward';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
166
app/views/wiki/new_system.rhtml
Executable file → Normal file
166
app/views/wiki/new_system.rhtml
Executable file → Normal file
|
@ -1,83 +1,83 @@
|
|||
<% @title = "Instiki Setup"; @content_width = 500 %>
|
||||
|
||||
<p>
|
||||
Congratulations on succesfully installing and starting Instiki.
|
||||
Since this is the first time Instiki has been run on this port,
|
||||
you'll need to do a brief one-time setup.
|
||||
</p>
|
||||
|
||||
<form action="../create_system" id="setup" method="post" onSubmit="return validateSetup()">
|
||||
<ol class="setup">
|
||||
<li>
|
||||
|
||||
<h2 style="margin-bottom: 3px">Name and address for your first web</h2>
|
||||
<div class="help">
|
||||
The name of the web is included in the title on all pages.
|
||||
The address is the base path that all pages within the web live beneath.
|
||||
Ex: the address "rails" gives URLs like <i>/rails/show/HomePage</i>.
|
||||
The address can only consist of letters and digits.
|
||||
</div>
|
||||
<div class="inputBox">
|
||||
Name: <input type="text" id="web_name" name="web_name" value="Wiki"
|
||||
onChange="proposeAddress();" onClick="this.value == 'Wiki' ? this.value = '' : true" />
|
||||
|
||||
Address: <input type="text" id="web_address" name="web_address" onChange="cleanAddress();"
|
||||
value="wiki" />
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<h2 style="margin-bottom: 3px">Password for creating and changing webs</h2>
|
||||
<div class="help">
|
||||
Administrative access allows you to make new webs and change existing ones.<br/>
|
||||
Everyone with this password will be able to do this, so pick it carefully.
|
||||
</div>
|
||||
<div class="inputBox">
|
||||
Password: <input type="password" id="password" name="password" />
|
||||
|
||||
Verify: <input type="password" id="password_check" name="password_check" />
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<p align="right">
|
||||
<input type="submit" value="Setup" style="margin-left: 40px" />
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function proposeAddress() {
|
||||
document.getElementById('web_address').value =
|
||||
document.getElementById('web_name').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
||||
}
|
||||
|
||||
function cleanAddress() {
|
||||
document.getElementById('web_address').value =
|
||||
document.getElementById('web_address').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
||||
}
|
||||
|
||||
function validateSetup() {
|
||||
if (document.getElementById('web_name').value == "") {
|
||||
alert("You must pick a name for the first web");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('web_address').value == "") {
|
||||
alert("You must pick an address for the first web");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('password').value == "") {
|
||||
alert("You must pick a system password");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('password_check').value == "" ||
|
||||
document.getElementById('password').value != document.getElementById('password_check').value) {
|
||||
alert("The password and its verification doesn't match");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
</script>
|
||||
<% @title = "Instiki Setup"; @content_width = 500 %>
|
||||
|
||||
<p>
|
||||
Congratulations on succesfully installing and starting Instiki.
|
||||
Since this is the first time Instiki has been run on this port,
|
||||
you'll need to do a brief one-time setup.
|
||||
</p>
|
||||
|
||||
<form action="../create_system" id="setup" method="post" onSubmit="return validateSetup()">
|
||||
<ol class="setup">
|
||||
<li>
|
||||
|
||||
<h2 style="margin-bottom: 3px">Name and address for your first web</h2>
|
||||
<div class="help">
|
||||
The name of the web is included in the title on all pages.
|
||||
The address is the base path that all pages within the web live beneath.
|
||||
Ex: the address "rails" gives URLs like <i>/rails/show/HomePage</i>.
|
||||
The address can only consist of letters and digits.
|
||||
</div>
|
||||
<div class="inputBox">
|
||||
Name: <input type="text" id="web_name" name="web_name" value="Wiki"
|
||||
onChange="proposeAddress();" onClick="this.value == 'Wiki' ? this.value = '' : true" />
|
||||
|
||||
Address: <input type="text" id="web_address" name="web_address" onChange="cleanAddress();"
|
||||
value="wiki" />
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<h2 style="margin-bottom: 3px">Password for creating and changing webs</h2>
|
||||
<div class="help">
|
||||
Administrative access allows you to make new webs and change existing ones.<br/>
|
||||
Everyone with this password will be able to do this, so pick it carefully.
|
||||
</div>
|
||||
<div class="inputBox">
|
||||
Password: <input type="password" id="password" name="password" />
|
||||
|
||||
Verify: <input type="password" id="password_check" name="password_check" />
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<p align="right">
|
||||
<input type="submit" value="Setup" style="margin-left: 40px" />
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function proposeAddress() {
|
||||
document.getElementById('web_address').value =
|
||||
document.getElementById('web_name').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
||||
}
|
||||
|
||||
function cleanAddress() {
|
||||
document.getElementById('web_address').value =
|
||||
document.getElementById('web_address').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
||||
}
|
||||
|
||||
function validateSetup() {
|
||||
if (document.getElementById('web_name').value == "") {
|
||||
alert("You must pick a name for the first web");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('web_address').value == "") {
|
||||
alert("You must pick an address for the first web");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('password').value == "") {
|
||||
alert("You must pick a system password");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('password_check').value == "" ||
|
||||
document.getElementById('password').value != document.getElementById('password_check').value) {
|
||||
alert("The password and its verification doesn't match");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
</script>
|
||||
|
|
138
app/views/wiki/new_web.rhtml
Executable file → Normal file
138
app/views/wiki/new_web.rhtml
Executable file → Normal file
|
@ -1,69 +1,69 @@
|
|||
<% @title = "New Wiki Web"; @content_width = 500 %>
|
||||
|
||||
<p>
|
||||
Each web serves as an isolated name space for wiki pages,
|
||||
so different subjects or projects can write about different <i>MuppetShows</i>.
|
||||
</p>
|
||||
|
||||
<form action="../create_web" id="setup" method="post" onSubmit="cleanAddress();
|
||||
return validateSetup()">
|
||||
|
||||
<ol class="setup">
|
||||
<li>
|
||||
<h2 style="margin-bottom: 3px">Name and address for your new web</h2>
|
||||
<div class="help">
|
||||
The name of the web is included in the title on all pages.
|
||||
The address is the base path that all pages within the web live beneath.
|
||||
Ex: the address "rails" gives URLs like <i>/rails/show/HomePage</i>.
|
||||
The address can only consist of letters and digits.
|
||||
</div>
|
||||
<div class="inputBox">
|
||||
Name: <input type="text" id="web_name" name="name" onChange="proposeAddress();" />
|
||||
|
||||
Address: <input type="text" id="web_address" name="address" onChange="cleanAddress();" />
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
|
||||
<p align="right">
|
||||
<small>
|
||||
Enter system password
|
||||
<input type="password" id="system_password" name="system_password" />
|
||||
and
|
||||
<input type="submit" value="Create Web" />
|
||||
</small>
|
||||
</p>
|
||||
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function proposeAddress() {
|
||||
document.getElementById('web_address').value =
|
||||
document.getElementById('web_name').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
||||
}
|
||||
|
||||
function cleanAddress() {
|
||||
document.getElementById('web_address').value =
|
||||
document.getElementById('web_address').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
||||
}
|
||||
|
||||
function validateSetup() {
|
||||
if (document.getElementById('web_name').value == "") {
|
||||
alert("You must pick a name for the new web");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('web_address').value == "") {
|
||||
alert("You must pick an address for the new web");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('system_password').value == "") {
|
||||
alert("You must enter the system password");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
</script>
|
||||
<% @title = "New Wiki Web"; @content_width = 500 %>
|
||||
|
||||
<p>
|
||||
Each web serves as an isolated name space for wiki pages,
|
||||
so different subjects or projects can write about different <i>MuppetShows</i>.
|
||||
</p>
|
||||
|
||||
<form action="../create_web" id="setup" method="post" onSubmit="cleanAddress();
|
||||
return validateSetup()">
|
||||
|
||||
<ol class="setup">
|
||||
<li>
|
||||
<h2 style="margin-bottom: 3px">Name and address for your new web</h2>
|
||||
<div class="help">
|
||||
The name of the web is included in the title on all pages.
|
||||
The address is the base path that all pages within the web live beneath.
|
||||
Ex: the address "rails" gives URLs like <i>/rails/show/HomePage</i>.
|
||||
The address can only consist of letters and digits.
|
||||
</div>
|
||||
<div class="inputBox">
|
||||
Name: <input type="text" id="web_name" name="name" onChange="proposeAddress();" />
|
||||
|
||||
Address: <input type="text" id="web_address" name="address" onChange="cleanAddress();" />
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
|
||||
<p align="right">
|
||||
<small>
|
||||
Enter system password
|
||||
<input type="password" id="system_password" name="system_password" />
|
||||
and
|
||||
<input type="submit" value="Create Web" />
|
||||
</small>
|
||||
</p>
|
||||
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function proposeAddress() {
|
||||
document.getElementById('web_address').value =
|
||||
document.getElementById('web_name').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
||||
}
|
||||
|
||||
function cleanAddress() {
|
||||
document.getElementById('web_address').value =
|
||||
document.getElementById('web_address').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
||||
}
|
||||
|
||||
function validateSetup() {
|
||||
if (document.getElementById('web_name').value == "") {
|
||||
alert("You must pick a name for the new web");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('web_address').value == "") {
|
||||
alert("You must pick an address for the new web");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document.getElementById('system_password').value == "") {
|
||||
alert("You must enter the system password");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
</script>
|
||||
|
|
156
app/views/wiki/page.rhtml
Executable file → Normal file
156
app/views/wiki/page.rhtml
Executable file → Normal file
|
@ -1,78 +1,78 @@
|
|||
<% @title = @page.plain_name %>
|
||||
|
||||
<div id="revision">
|
||||
<%= @page.display_content %>
|
||||
</div>
|
||||
|
||||
<div id="changes" style="display: none">
|
||||
<p style="background: #eee; padding: 3px; border: 1px solid silver">
|
||||
<small>
|
||||
Showing changes from revision #<%= @page.number - 1 %> to #<%= @page.number %>:
|
||||
<ins class="diffins">Added</ins> | <del class="diffdel">Removed</del>
|
||||
</small>
|
||||
</p>
|
||||
|
||||
<%= @page.display_diff %>
|
||||
</div>
|
||||
|
||||
<div class="byline">
|
||||
<%= @page.revisions? ? "Revised" : "Created" %> on <%= @page.pretty_created_at %>
|
||||
by <%= @page.author_link %>
|
||||
<%= "(#{@page.author.ip})" if @page.author.respond_to?(:ip) %>
|
||||
<% if @web.count_pages %>
|
||||
<% total_chars = @page.content.length %>
|
||||
(<%= total_chars %> characters / <%= sprintf("%-.1f", (total_chars / 2275 rescue 0)) %> pages)
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="navigation">
|
||||
<% if @page.name == "HomePage" %>
|
||||
<a href="../edit/<%= @page.name %>" class="navlink" accesskey="E">Edit Page</a>
|
||||
| <a href="../edit_web/" class="navlink">Edit Web</a>
|
||||
<% else %>
|
||||
<a href="../edit/<%= @page.name %>" class="navlink" accesskey="E">Edit</a>
|
||||
<% end %>
|
||||
|
||||
<% if @page.revisions.length > 1 %>
|
||||
| <a href="../revision/<%= @page.name %>?rev=<%= @page.revisions.length - 2 %>" class="navlink" accesskey="R">Back in time</a>
|
||||
<small>(<%= @page.revisions.length - 1 %> revisions)</small>
|
||||
<% end %>
|
||||
|
||||
<% if @page.revisions.length > 1 %>
|
||||
<span id="show_changes">
|
||||
| <a href="#" onClick="toggleChanges(); return false;">See changes</a>
|
||||
</span>
|
||||
<span id="hide_changes" style="display: none">
|
||||
| <a href="#" onClick="toggleChanges(); return false;">Hide changes</a>
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<small>
|
||||
| Views: <a href="../print/<%= @page.name %>">Print</a>
|
||||
<% if defined? RedClothForTex and RedClothForTex.available? and @web.markup == :textile %>
|
||||
| <a href="../tex/<%= @page.name %>">TeX</a> | <a href="../pdf/<%= @page.name %>">PDF</a>
|
||||
<% end %>
|
||||
</small>
|
||||
|
||||
<% if @page.references.length > 0 %>
|
||||
<small>
|
||||
| Linked from: <%= @page.references.collect { |ref| ref.link }.join(", ") %>
|
||||
</small>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<script language="Javascript">
|
||||
function toggleChanges() {
|
||||
if (document.getElementById("changes").style.display == "none") {
|
||||
document.getElementById("changes").style.display = "block";
|
||||
document.getElementById("revision").style.display = "none";
|
||||
document.getElementById("show_changes").style.display = "none";
|
||||
document.getElementById("hide_changes").style.display = "inline";
|
||||
} else {
|
||||
document.getElementById("changes").style.display = "none";
|
||||
document.getElementById("revision").style.display = "block";
|
||||
document.getElementById("show_changes").style.display = "inline";
|
||||
document.getElementById("hide_changes").style.display = "none";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<% @title = @page.plain_name %>
|
||||
|
||||
<div id="revision">
|
||||
<%= @page.display_content %>
|
||||
</div>
|
||||
|
||||
<div id="changes" style="display: none">
|
||||
<p style="background: #eee; padding: 3px; border: 1px solid silver">
|
||||
<small>
|
||||
Showing changes from revision #<%= @page.number - 1 %> to #<%= @page.number %>:
|
||||
<ins class="diffins">Added</ins> | <del class="diffdel">Removed</del>
|
||||
</small>
|
||||
</p>
|
||||
|
||||
<%= @page.display_diff %>
|
||||
</div>
|
||||
|
||||
<div class="byline">
|
||||
<%= @page.revisions? ? "Revised" : "Created" %> on <%= @page.pretty_created_at %>
|
||||
by <%= @page.author_link %>
|
||||
<%= "(#{@page.author.ip})" if @page.author.respond_to?(:ip) %>
|
||||
<% if @web.count_pages %>
|
||||
<% total_chars = @page.content.length %>
|
||||
(<%= total_chars %> characters / <%= sprintf("%-.1f", (total_chars / 2275 rescue 0)) %> pages)
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="navigation">
|
||||
<% if @page.name == "HomePage" %>
|
||||
<a href="../edit/<%= @page.name %>" class="navlink" accesskey="E">Edit Page</a>
|
||||
| <a href="../edit_web/" class="navlink">Edit Web</a>
|
||||
<% else %>
|
||||
<a href="../edit/<%= @page.name %>" class="navlink" accesskey="E">Edit</a>
|
||||
<% end %>
|
||||
|
||||
<% if @page.revisions.length > 1 %>
|
||||
| <a href="../revision/<%= @page.name %>?rev=<%= @page.revisions.length - 2 %>" class="navlink" accesskey="R">Back in time</a>
|
||||
<small>(<%= @page.revisions.length - 1 %> revisions)</small>
|
||||
<% end %>
|
||||
|
||||
<% if @page.revisions.length > 1 %>
|
||||
<span id="show_changes">
|
||||
| <a href="#" onClick="toggleChanges(); return false;">See changes</a>
|
||||
</span>
|
||||
<span id="hide_changes" style="display: none">
|
||||
| <a href="#" onClick="toggleChanges(); return false;">Hide changes</a>
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<small>
|
||||
| Views: <a href="../print/<%= @page.name %>">Print</a>
|
||||
<% if defined? RedClothForTex and RedClothForTex.available? and @web.markup == :textile %>
|
||||
| <a href="../tex/<%= @page.name %>">TeX</a> | <a href="../pdf/<%= @page.name %>">PDF</a>
|
||||
<% end %>
|
||||
</small>
|
||||
|
||||
<% if @page.references.length > 0 %>
|
||||
<small>
|
||||
| Linked from: <%= @page.references.collect { |ref| ref.link }.join(", ") %>
|
||||
</small>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<script language="Javascript">
|
||||
function toggleChanges() {
|
||||
if (document.getElementById("changes").style.display == "none") {
|
||||
document.getElementById("changes").style.display = "block";
|
||||
document.getElementById("revision").style.display = "none";
|
||||
document.getElementById("show_changes").style.display = "none";
|
||||
document.getElementById("hide_changes").style.display = "inline";
|
||||
} else {
|
||||
document.getElementById("changes").style.display = "none";
|
||||
document.getElementById("revision").style.display = "block";
|
||||
document.getElementById("show_changes").style.display = "inline";
|
||||
document.getElementById("hide_changes").style.display = "none";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
28
app/views/wiki/print.rhtml
Executable file → Normal file
28
app/views/wiki/print.rhtml
Executable file → Normal file
|
@ -1,14 +1,14 @@
|
|||
<%
|
||||
@title = @page.plain_name
|
||||
@hide_navigation = true
|
||||
@style_additions = ".newWikiWord { background-color: white; font-style: italic; }"
|
||||
@inline_style = true
|
||||
%>
|
||||
|
||||
<%= @page.display_content_for_export %>
|
||||
|
||||
<div class="byline">
|
||||
<%= @page.revisions? ? "Revised" : "Created" %> on <%= @page.pretty_created_at %>
|
||||
by
|
||||
<%= @page.author_link({ :mode => :export }) %>
|
||||
</div>
|
||||
<%
|
||||
@title = @page.plain_name
|
||||
@hide_navigation = true
|
||||
@style_additions = ".newWikiWord { background-color: white; font-style: italic; }"
|
||||
@inline_style = true
|
||||
%>
|
||||
|
||||
<%= @page.display_content_for_export %>
|
||||
|
||||
<div class="byline">
|
||||
<%= @page.revisions? ? "Revised" : "Created" %> on <%= @page.pretty_created_at %>
|
||||
by
|
||||
<%= @page.author_link({ :mode => :export }) %>
|
||||
</div>
|
||||
|
|
16
app/views/wiki/published.rhtml
Executable file → Normal file
16
app/views/wiki/published.rhtml
Executable file → Normal file
|
@ -1,8 +1,8 @@
|
|||
<%
|
||||
@title = @page.plain_name
|
||||
@hide_navigation = false
|
||||
@style_additions = ".newWikiWord { background-color: white; font-style: italic; }"
|
||||
@inline_style = true
|
||||
%>
|
||||
|
||||
<%= @page.display_published %>
|
||||
<%
|
||||
@title = @page.plain_name
|
||||
@hide_navigation = false
|
||||
@style_additions = ".newWikiWord { background-color: white; font-style: italic; }"
|
||||
@inline_style = true
|
||||
%>
|
||||
|
||||
<%= @page.display_published %>
|
||||
|
|
64
app/views/wiki/recently_revised.rhtml
Executable file → Normal file
64
app/views/wiki/recently_revised.rhtml
Executable file → Normal file
|
@ -1,32 +1,32 @@
|
|||
<% @title = "Recently Revised" %>
|
||||
|
||||
<% unless @categories.empty? %>
|
||||
<div id="categories">
|
||||
<strong>Categories</strong>:
|
||||
[<a href=".">Any</a>]
|
||||
<%= @category_links.join(', ') %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% unless @pages_by_revision.empty? %>
|
||||
<% revision_date = @pages_by_revision.first.revised_on %>
|
||||
<h3><%= revision_date.strftime('%B %e, %Y') %></h3>
|
||||
<ul>
|
||||
<% for page in @pages_by_revision %>
|
||||
<% if page.revised_on < revision_date %>
|
||||
<% revision_date = page.revised_on %>
|
||||
</ul>
|
||||
<h3><%= revision_date.strftime('%B %e, %Y') %></h3>
|
||||
<ul>
|
||||
<% end %>
|
||||
<li>
|
||||
<a href="../show/<%= page.name %>"><%= page.plain_name %></a>
|
||||
<div class="byline" style="margin-bottom: 0px">
|
||||
by <%= page.author_link %>
|
||||
at <%= page.created_at.strftime "%H:%M" %>
|
||||
<%= "from #{page.author.ip}" if page.author.respond_to?(:ip) %>
|
||||
</div>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% end %>
|
||||
<% @title = "Recently Revised" %>
|
||||
|
||||
<% unless @categories.empty? %>
|
||||
<div id="categories">
|
||||
<strong>Categories</strong>:
|
||||
[<a href=".">Any</a>]
|
||||
<%= @category_links.join(', ') %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% unless @pages_by_revision.empty? %>
|
||||
<% revision_date = @pages_by_revision.first.revised_on %>
|
||||
<h3><%= revision_date.strftime('%B %e, %Y') %></h3>
|
||||
<ul>
|
||||
<% for page in @pages_by_revision %>
|
||||
<% if page.revised_on < revision_date %>
|
||||
<% revision_date = page.revised_on %>
|
||||
</ul>
|
||||
<h3><%= revision_date.strftime('%B %e, %Y') %></h3>
|
||||
<ul>
|
||||
<% end %>
|
||||
<li>
|
||||
<a href="../show/<%= page.name %>"><%= page.plain_name %></a>
|
||||
<div class="byline" style="margin-bottom: 0px">
|
||||
by <%= page.author_link %>
|
||||
at <%= page.created_at.strftime "%H:%M" %>
|
||||
<%= "from #{page.author.ip}" if page.author.respond_to?(:ip) %>
|
||||
</div>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% end %>
|
||||
|
|
158
app/views/wiki/revision.rhtml
Executable file → Normal file
158
app/views/wiki/revision.rhtml
Executable file → Normal file
|
@ -1,79 +1,79 @@
|
|||
<% @title = "#{@page.plain_name} (Rev ##{@revision.number})" %>
|
||||
|
||||
<div id="revision">
|
||||
<%= @revision.display_content %>
|
||||
</div>
|
||||
|
||||
<div id="changes" style="display: none">
|
||||
<p style="background: #eee; padding: 3px; border: 1px solid silver">
|
||||
<small>
|
||||
Showing changes from revision #<%= @revision.number - 1 %> to #<%= @revision.number %>:
|
||||
<ins class="diffins">Added</ins> | <del class="diffdel">Removed</del>
|
||||
</small>
|
||||
</p>
|
||||
|
||||
<%= @revision.display_diff %>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="byline">
|
||||
<%= "Revision from #{@revision.pretty_created_at} by" %>
|
||||
<%= @page.web.make_link(@revision.author) %>
|
||||
</div>
|
||||
|
||||
<div class="navigation">
|
||||
|
||||
<% if @revision.next_revision %>
|
||||
<% if @revision.next_revision.number < (@page.revisions.length - 1) %>
|
||||
<a href="../revision/<%= @page.name %>?rev=<%= @revision.next_revision.number %>" class="navlink">
|
||||
<% else %>
|
||||
<a href="../show/<%= @page.name %>" class="navlink">
|
||||
<% end %>
|
||||
Forward in time</a>
|
||||
(<%= @revision.page.revisions.length - @revision.next_revision.number %> more)
|
||||
<% end %>
|
||||
|
||||
<% if @revision.next_revision && @revision.previous_revision %>
|
||||
|
|
||||
<% end %>
|
||||
|
||||
<% if @revision.previous_revision %>
|
||||
<a href="../revision/<%= @page.name %>?rev=<%= @revision.previous_revision.number %>" class="navlink">Back in time</a>
|
||||
(<%= @revision.previous_revision.number + 1 %> more)
|
||||
<% end %>
|
||||
|
||||
| <a href="../show/<%= @page.name %>" class="navlink">See current</a>
|
||||
|
||||
<% if @revision.previous_revision %>
|
||||
<span id="show_changes">
|
||||
| <a href="#" onClick="toggleChanges(); return false;">See changes</a>
|
||||
</span>
|
||||
<span id="hide_changes" style="display: none">
|
||||
| <a href="#" onClick="toggleChanges(); return false;">Hide changes</a>
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
| <a href="../rollback/<%= @page.name %>?rev=<%= @revision.number %>" class="navlink">Rollback</a>
|
||||
|
||||
<% if @page.references.length > 0 %>
|
||||
<small>
|
||||
| Linked from: <%= @page.references.collect { |ref| "<a href='#{ref.name}'>#{ref.name}</a>" }.join(", ") %>
|
||||
</small>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<script language="Javascript">
|
||||
function toggleChanges() {
|
||||
if (document.getElementById("changes").style.display == "none") {
|
||||
document.getElementById("changes").style.display = "block";
|
||||
document.getElementById("revision").style.display = "none";
|
||||
document.getElementById("show_changes").style.display = "none";
|
||||
document.getElementById("hide_changes").style.display = "inline";
|
||||
} else {
|
||||
document.getElementById("changes").style.display = "none";
|
||||
document.getElementById("revision").style.display = "block";
|
||||
document.getElementById("show_changes").style.display = "inline";
|
||||
document.getElementById("hide_changes").style.display = "none";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<% @title = "#{@page.plain_name} (Rev ##{@revision.number})" %>
|
||||
|
||||
<div id="revision">
|
||||
<%= @revision.display_content %>
|
||||
</div>
|
||||
|
||||
<div id="changes" style="display: none">
|
||||
<p style="background: #eee; padding: 3px; border: 1px solid silver">
|
||||
<small>
|
||||
Showing changes from revision #<%= @revision.number - 1 %> to #<%= @revision.number %>:
|
||||
<ins class="diffins">Added</ins> | <del class="diffdel">Removed</del>
|
||||
</small>
|
||||
</p>
|
||||
|
||||
<%= @revision.display_diff %>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="byline">
|
||||
<%= "Revision from #{@revision.pretty_created_at} by" %>
|
||||
<%= @page.web.make_link(@revision.author) %>
|
||||
</div>
|
||||
|
||||
<div class="navigation">
|
||||
|
||||
<% if @revision.next_revision %>
|
||||
<% if @revision.next_revision.number < (@page.revisions.length - 1) %>
|
||||
<a href="../revision/<%= @page.name %>?rev=<%= @revision.next_revision.number %>" class="navlink">
|
||||
<% else %>
|
||||
<a href="../show/<%= @page.name %>" class="navlink">
|
||||
<% end %>
|
||||
Forward in time</a>
|
||||
(<%= @revision.page.revisions.length - @revision.next_revision.number %> more)
|
||||
<% end %>
|
||||
|
||||
<% if @revision.next_revision && @revision.previous_revision %>
|
||||
|
|
||||
<% end %>
|
||||
|
||||
<% if @revision.previous_revision %>
|
||||
<a href="../revision/<%= @page.name %>?rev=<%= @revision.previous_revision.number %>" class="navlink">Back in time</a>
|
||||
(<%= @revision.previous_revision.number + 1 %> more)
|
||||
<% end %>
|
||||
|
||||
| <a href="../show/<%= @page.name %>" class="navlink">See current</a>
|
||||
|
||||
<% if @revision.previous_revision %>
|
||||
<span id="show_changes">
|
||||
| <a href="#" onClick="toggleChanges(); return false;">See changes</a>
|
||||
</span>
|
||||
<span id="hide_changes" style="display: none">
|
||||
| <a href="#" onClick="toggleChanges(); return false;">Hide changes</a>
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
| <a href="../rollback/<%= @page.name %>?rev=<%= @revision.number %>" class="navlink">Rollback</a>
|
||||
|
||||
<% if @page.references.length > 0 %>
|
||||
<small>
|
||||
| Linked from: <%= @page.references.collect { |ref| "<a href='#{ref.name}'>#{ref.name}</a>" }.join(", ") %>
|
||||
</small>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<script language="Javascript">
|
||||
function toggleChanges() {
|
||||
if (document.getElementById("changes").style.display == "none") {
|
||||
document.getElementById("changes").style.display = "block";
|
||||
document.getElementById("revision").style.display = "none";
|
||||
document.getElementById("show_changes").style.display = "none";
|
||||
document.getElementById("hide_changes").style.display = "inline";
|
||||
} else {
|
||||
document.getElementById("changes").style.display = "none";
|
||||
document.getElementById("revision").style.display = "block";
|
||||
document.getElementById("show_changes").style.display = "inline";
|
||||
document.getElementById("hide_changes").style.display = "none";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
0
app/views/wiki/rollback.rhtml
Executable file → Normal file
0
app/views/wiki/rollback.rhtml
Executable file → Normal file
44
app/views/wiki/rss_feed.rhtml
Executable file → Normal file
44
app/views/wiki/rss_feed.rhtml
Executable file → Normal file
|
@ -1,22 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<channel>
|
||||
<title><%= @web.name %></title>
|
||||
<link><%= url_for :only_path => false, :web => @web_name, :action => 'show', :id => 'HomePage' %></link>
|
||||
<description>An Instiki wiki</description>
|
||||
<language>en-us</language>
|
||||
<ttl>40</ttl>
|
||||
<% for page in @pages_by_revision %>
|
||||
<item>
|
||||
<title><%= page.plain_name %></title>
|
||||
<% unless @hide_description %>
|
||||
<description><%= CGI.escapeHTML(page.display_content) %></description>
|
||||
<% end %>
|
||||
<pubDate><%= page.created_at.strftime "%a, %e %b %Y %H:%M:%S %Z" %></pubDate>
|
||||
<guid><%= url_for :only_path => false, :web => @web_name, :action => 'show', :id => page.name %></guid>
|
||||
<link><%= url_for :only_path => false, :web => @web_name, :action => 'show', :id => page.name %></link>
|
||||
<dc:creator><%= WikiWords.separate(page.author) %></dc:creator>
|
||||
</item>
|
||||
<% end %>
|
||||
</channel>
|
||||
</rss>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<channel>
|
||||
<title><%= @web.name %></title>
|
||||
<link><%= url_for :only_path => false, :web => @web_name, :action => 'show', :id => 'HomePage' %></link>
|
||||
<description>An Instiki wiki</description>
|
||||
<language>en-us</language>
|
||||
<ttl>40</ttl>
|
||||
<% for page in @pages_by_revision %>
|
||||
<item>
|
||||
<title><%= page.plain_name %></title>
|
||||
<% unless @hide_description %>
|
||||
<description><%= CGI.escapeHTML(page.display_content) %></description>
|
||||
<% end %>
|
||||
<pubDate><%= page.created_at.strftime "%a, %e %b %Y %H:%M:%S %Z" %></pubDate>
|
||||
<guid><%= url_for :only_path => false, :web => @web_name, :action => 'show', :id => page.name %></guid>
|
||||
<link><%= url_for :only_path => false, :web => @web_name, :action => 'show', :id => page.name %></link>
|
||||
<dc:creator><%= WikiWords.separate(page.author) %></dc:creator>
|
||||
</item>
|
||||
<% end %>
|
||||
</channel>
|
||||
</rss>
|
||||
|
|
26
app/views/wiki/search.rhtml
Executable file → Normal file
26
app/views/wiki/search.rhtml
Executable file → Normal file
|
@ -1,13 +1,13 @@
|
|||
<% @title = @results.length > 0 ? "#{@results.length} pages contains \"#{@params["query"]}\"" : "No pages contains \"#{@query}\"" %>
|
||||
|
||||
<% if @results.length > 0 %>
|
||||
<ul>
|
||||
<% for page in @results %>
|
||||
<li><a href="../show/<%= page.name %>"><%= page.plain_name %></a></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% else %>
|
||||
<p>Perhaps you should try expanding your query. Remember that Instiki searches for entire phrases, so if you search for "all that jazz" it will not match pages that contain these words in separation—only as a sentence fragment.</p>
|
||||
|
||||
<p>If you're a high-tech computer wizard, you might even want try constructing a regular expression. That's actually what Instiki uses, so go right ahead and flex your "[a-z]*Leet?RegExpSkill(s|z)"</p>
|
||||
<% end %>
|
||||
<% @title = @results.length > 0 ? "#{@results.length} pages contains \"#{@params["query"]}\"" : "No pages contains \"#{@query}\"" %>
|
||||
|
||||
<% if @results.length > 0 %>
|
||||
<ul>
|
||||
<% for page in @results %>
|
||||
<li><a href="../show/<%= page.name %>"><%= page.plain_name %></a></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% else %>
|
||||
<p>Perhaps you should try expanding your query. Remember that Instiki searches for entire phrases, so if you search for "all that jazz" it will not match pages that contain these words in separation—only as a sentence fragment.</p>
|
||||
|
||||
<p>If you're a high-tech computer wizard, you might even want try constructing a regular expression. That's actually what Instiki uses, so go right ahead and flex your "[a-z]*Leet?RegExpSkill(s|z)"</p>
|
||||
<% end %>
|
||||
|
|
44
app/views/wiki/tex.rhtml
Executable file → Normal file
44
app/views/wiki/tex.rhtml
Executable file → Normal file
|
@ -1,23 +1,23 @@
|
|||
\documentclass[12pt,titlepage]{article}
|
||||
|
||||
\usepackage[danish]{babel} %danske tekster
|
||||
\usepackage[OT1]{fontenc} %rigtige danske bogstaver...
|
||||
\usepackage{a4}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{ucs}
|
||||
\usepackage[utf8x]{inputenc}
|
||||
\input epsf
|
||||
|
||||
%-------------------------------------------------------------------
|
||||
|
||||
\begin{document}
|
||||
|
||||
\sloppy
|
||||
|
||||
%-------------------------------------------------------------------
|
||||
|
||||
\section*{<%= @page.name %>}
|
||||
|
||||
<%= @tex_content %>
|
||||
|
||||
\documentclass[12pt,titlepage]{article}
|
||||
|
||||
\usepackage[danish]{babel} %danske tekster
|
||||
\usepackage[OT1]{fontenc} %rigtige danske bogstaver...
|
||||
\usepackage{a4}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{ucs}
|
||||
\usepackage[utf8x]{inputenc}
|
||||
\input epsf
|
||||
|
||||
%-------------------------------------------------------------------
|
||||
|
||||
\begin{document}
|
||||
|
||||
\sloppy
|
||||
|
||||
%-------------------------------------------------------------------
|
||||
|
||||
\section*{<%= @page.name %>}
|
||||
|
||||
<%= @tex_content %>
|
||||
|
||||
\end{document}
|
68
app/views/wiki/tex_web.rhtml
Executable file → Normal file
68
app/views/wiki/tex_web.rhtml
Executable file → Normal file
|
@ -1,35 +1,35 @@
|
|||
\documentclass[12pt,titlepage]{article}
|
||||
|
||||
\usepackage{fancyhdr}
|
||||
\pagestyle{fancy}
|
||||
|
||||
\fancyhead[LE,RO]{}
|
||||
\fancyhead[LO,RE]{\nouppercase{\bfseries \leftmark}}
|
||||
\fancyfoot[C]{\thepage}
|
||||
|
||||
\usepackage[danish]{babel} %danske tekster
|
||||
\usepackage{a4}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{ucs}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\input epsf
|
||||
|
||||
|
||||
%-------------------------------------------------------------------
|
||||
|
||||
\title{<%= @web_name %>}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\maketitle
|
||||
|
||||
\tableofcontents
|
||||
\pagebreak
|
||||
|
||||
\sloppy
|
||||
|
||||
%-------------------------------------------------------------------
|
||||
|
||||
<%= @tex_content %>
|
||||
|
||||
\documentclass[12pt,titlepage]{article}
|
||||
|
||||
\usepackage{fancyhdr}
|
||||
\pagestyle{fancy}
|
||||
|
||||
\fancyhead[LE,RO]{}
|
||||
\fancyhead[LO,RE]{\nouppercase{\bfseries \leftmark}}
|
||||
\fancyfoot[C]{\thepage}
|
||||
|
||||
\usepackage[danish]{babel} %danske tekster
|
||||
\usepackage{a4}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{ucs}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\input epsf
|
||||
|
||||
|
||||
%-------------------------------------------------------------------
|
||||
|
||||
\title{<%= @web_name %>}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\maketitle
|
||||
|
||||
\tableofcontents
|
||||
\pagebreak
|
||||
|
||||
\sloppy
|
||||
|
||||
%-------------------------------------------------------------------
|
||||
|
||||
<%= @tex_content %>
|
||||
|
||||
\end{document}
|
36
app/views/wiki/web_list.rhtml
Executable file → Normal file
36
app/views/wiki/web_list.rhtml
Executable file → Normal file
|
@ -1,18 +1,18 @@
|
|||
<% @title = "Wiki webs" %>
|
||||
|
||||
<ul>
|
||||
<% for web in @webs %>
|
||||
<li>
|
||||
<% if web.published %>
|
||||
<%= web.make_link 'HomePage', web.name, :mode => :publish %> (read-only) /
|
||||
<%= web.make_link 'HomePage', 'editable version', :mode => :edit %> (requires login)
|
||||
<% else %>
|
||||
<%= web.make_link 'HomePage', nil, :mode => :edit %>
|
||||
<% end %>
|
||||
<div class="byline" style="margin-bottom: 0px">
|
||||
<%= web.pages.length %> pages by <%= web.authors.length %> authors
|
||||
</div>
|
||||
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% @title = "Wiki webs" %>
|
||||
|
||||
<ul>
|
||||
<% for web in @webs %>
|
||||
<li>
|
||||
<% if web.published %>
|
||||
<%= web.make_link 'HomePage', web.name, :mode => :publish %> (read-only) /
|
||||
<%= web.make_link 'HomePage', 'editable version', :mode => :edit %> (requires login)
|
||||
<% else %>
|
||||
<%= web.make_link 'HomePage', nil, :mode => :edit %>
|
||||
<% end %>
|
||||
<div class="byline" style="margin-bottom: 0px">
|
||||
<%= web.pages.length %> pages by <%= web.authors.length %> authors
|
||||
</div>
|
||||
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
|
18
app/views/wiki_words_help.rhtml
Executable file → Normal file
18
app/views/wiki_words_help.rhtml
Executable file → Normal file
|
@ -1,9 +1,9 @@
|
|||
<h3>Wiki words</h3>
|
||||
<p style="border-top: 1px dotted #ccc; margin-top: 0px">
|
||||
Two or more uppercase words stuck together (camel case) or any phrase surrounded by double
|
||||
brackets is a wiki word. A camel-case wiki word can be escaped by putting \ in front of it.
|
||||
</p>
|
||||
<p>
|
||||
Wiki words: <i>HomePage, ThreeWordsTogether, [[C++]], [[Let's play again!]]</i><br/>
|
||||
Not wiki words: <i>IBM, School</i>
|
||||
</p>
|
||||
<h3>Wiki words</h3>
|
||||
<p style="border-top: 1px dotted #ccc; margin-top: 0px">
|
||||
Two or more uppercase words stuck together (camel case) or any phrase surrounded by double
|
||||
brackets is a wiki word. A camel-case wiki word can be escaped by putting \ in front of it.
|
||||
</p>
|
||||
<p>
|
||||
Wiki words: <i>HomePage, ThreeWordsTogether, [[C++]], [[Let's play again!]]</i><br/>
|
||||
Not wiki words: <i>IBM, School</i>
|
||||
</p>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue