Checkout of Instiki Trunk 1/21/2007.

This commit is contained in:
Jacques Distler 2007-01-22 07:43:50 -06:00
commit 69b62b6f33
1138 changed files with 139586 additions and 0 deletions

View file

@ -0,0 +1,94 @@
require 'application'
class AdminController < ApplicationController
layout 'default'
cache_sweeper :web_sweeper
def create_system
if @wiki.setup?
flash[:error] =
"Wiki has already been created in '#{@wiki.storage_path}'. " +
"Shut down Instiki and delete this directory if you want to recreate it from scratch." +
"\n\n" +
"(WARNING: this will destroy content of your current wiki)."
redirect_home(@wiki.webs.keys.first)
elsif @params['web_name']
# form submitted -> create a wiki
@wiki.setup(@params['password'], @params['web_name'], @params['web_address'])
flash[:info] = "Your new wiki '#{@params['web_name']}' is created!\n" +
"Please edit its home page and press Submit when finished."
redirect_to :web => @params['web_address'], :controller => 'wiki', :action => 'new',
:id => 'HomePage'
else
# no form submitted -> go to template
end
end
def create_web
if @params['address']
# form submitted
if @wiki.authenticate(@params['system_password'])
begin
@wiki.create_web(@params['name'], @params['address'])
flash[:info] = "New web '#{@params['name']}' successfully created."
redirect_to :web => @params['address'], :controller => 'wiki', :action => 'new',
:id => 'HomePage'
rescue Instiki::ValidationError => e
@error = e.message
# and re-render the form again
end
else
redirect_to :controller => 'wiki', :action => 'index'
end
else
# no form submitted -> render template
end
end
def edit_web
system_password = @params['system_password']
if system_password
# form submitted
if wiki.authenticate(system_password)
begin
wiki.edit_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,
@params['max_upload_size']
)
flash[:info] = "Web '#{@params['address']}' was successfully updated"
redirect_home(@params['address'])
rescue Instiki::ValidationError => e
logger.warn e.message
@error = e.message
# and re-render the same template again
end
else
@error = password_error(system_password)
# and re-render the same template again
end
else
# no form submitted - go to template
end
end
def remove_orphaned_pages
if wiki.authenticate(@params['system_password_orphaned'])
wiki.remove_orphaned_pages(@web_name)
flash[:info] = 'Orphaned pages removed'
redirect_to :controller => 'wiki', :web => @web_name, :action => 'list'
else
flash[:error] = password_error(@params['system_password_orphaned'])
redirect_to :controller => 'admin', :web => @web_name, :action => 'edit_web'
end
end
end

View file

@ -0,0 +1,190 @@
# The filters added to this controller will be run for all controllers in the application.
# Likewise will all the methods added be available for all controllers.
class ApplicationController < ActionController::Base
# require 'dnsbl_check'
before_filter :dnsbl_check, :connect_to_model, :check_authorization, :setup_url_generator, :set_content_type_header, :set_robots_metatag
after_filter :remember_location, :teardown_url_generator
# For injecting a different wiki model implementation. Intended for use in tests
def self.wiki=(the_wiki)
# a global variable is used here because Rails reloads controller and model classes in the
# development environment; therefore, storing it as a class variable does not work
# class variable is, anyway, not much different from a global variable
#$instiki_wiki_service = the_wiki
logger.debug("Wiki service: #{the_wiki.to_s}")
end
def self.wiki
Wiki.new
end
protected
def check_authorization
if in_a_web? and authorization_needed? and not authorized?
redirect_to :controller => 'wiki', :action => 'login', :web => @web_name
return false
end
end
def connect_to_model
@action_name = @params['action'] || 'index'
@web_name = @params['web']
@wiki = wiki
@author = cookies['author'] || 'AnonymousCoward'
if @web_name
@web = @wiki.webs[@web_name]
if @web.nil?
render(:status => 404, :text => "Unknown web '#{@web_name}'")
return false
end
end
end
FILE_TYPES = {
'.exe' => 'application/octet-stream',
'.gif' => 'image/gif',
'.jpg' => 'image/jpeg',
'.pdf' => 'application/pdf',
'.png' => 'image/png',
'.txt' => 'text/plain',
'.zip' => 'application/zip'
} unless defined? FILE_TYPES
DISPOSITION = {
'application/octet-stream' => 'attachment',
'image/gif' => 'inline',
'image/jpeg' => 'inline',
'application/pdf' => 'inline',
'image/png' => 'inline',
'text/plain' => 'inline',
'application/zip' => 'attachment'
} unless defined? DISPOSITION
def determine_file_options_for(file_name, original_options = {})
original_options[:type] ||= (FILE_TYPES[File.extname(file_name)] or 'application/octet-stream')
original_options[:disposition] ||= (DISPOSITION[original_options[:type]] or 'attachment')
original_options[:stream] ||= false
original_options
end
def send_file(file, options = {})
determine_file_options_for(file, options)
super(file, options)
end
def password_check(password)
if password == @web.password
cookies['web_address'] = password
true
else
false
end
end
def password_error(password)
if password.nil? or password.empty?
'Please enter the password.'
else
'You entered a wrong password. Please enter the right one.'
end
end
def redirect_home(web = @web_name)
if web
redirect_to_page('HomePage', web)
else
redirect_to_url '/'
end
end
def redirect_to_page(page_name = @page_name, web = @web_name)
redirect_to :web => web, :controller => 'wiki', :action => 'show',
:id => (page_name or 'HomePage')
end
def remember_location
if @request.method == :get and
@response.headers['Status'] == '200 OK' and not
%w(locked save back file pic import).include?(action_name)
@session[:return_to] = @request.request_uri
logger.debug "Session ##{session.object_id}: remembered URL '#{@session[:return_to]}'"
end
end
def rescue_action_in_public(exception)
render :status => 500, :text => <<-EOL
<html><body>
<h2>Internal Error</h2>
<p>An application error occurred while processing your request.</p>
<!-- \n#{exception}\n#{exception.backtrace.join("\n")}\n -->
</body></html>
EOL
end
def return_to_last_remembered
# Forget the redirect location
redirect_target, @session[:return_to] = @session[:return_to], nil
tried_home, @session[:tried_home] = @session[:tried_home], false
# then try to redirect to it
if redirect_target.nil?
if tried_home
raise 'Application could not render the index page'
else
logger.debug("Session ##{session.object_id}: no remembered redirect location, trying home")
redirect_home
end
else
logger.debug("Session ##{session.object_id}: " +
"redirect to the last remembered URL #{redirect_target}")
redirect_to_url(redirect_target)
end
end
def set_content_type_header
if %w(rss_with_content rss_with_headlines).include?(action_name)
@response.headers['Content-Type'] = 'text/xml; charset=UTF-8'
else
@response.headers['Content-Type'] = 'text/html; charset=UTF-8'
end
end
def set_robots_metatag
if controller_name == 'wiki' and %w(show published).include? action_name
@robots_metatag_value = 'index,follow'
else
@robots_metatag_value = 'noindex,nofollow'
end
end
def setup_url_generator
PageRenderer.setup_url_generator(UrlGenerator.new(self))
end
def teardown_url_generator
PageRenderer.teardown_url_generator
end
def wiki
self.class.wiki
end
private
def in_a_web?
not @web_name.nil?
end
def authorization_needed?
not %w( login authenticate published rss_with_content rss_with_headlines ).include?(action_name)
end
def authorized?
@web.nil? or
@web.password.nil? or
cookies['web_address'] == @web.password or
password_check(@params['password'])
end
end

View file

@ -0,0 +1,23 @@
module CacheSweepingHelper
def expire_cached_page(web, page_name)
expire_action :controller => 'wiki', :web => web.address,
:action => %w(show published), :id => page_name
expire_action :controller => 'wiki', :web => web.address,
:action => %w(show published), :id => page_name, :mode => 'diff'
end
def expire_cached_summary_pages(web)
categories = WikiReference.find(:all, :conditions => "link_type = 'C'")
%w(recently_revised list).each do |action|
expire_action :controller => 'wiki', :web => web.address, :action => action
categories.each do |category|
expire_action :controller => 'wiki', :web => web.address, :action => action, :category => category.referenced_name
end
end
expire_action :controller => 'wiki', :web => web.address, :action => 'authors'
expire_fragment :controller => 'wiki', :web => web.address, :action => %w(rss_with_headlines rss_with_content)
end
end

View file

@ -0,0 +1,100 @@
# Controller responsible for serving files and pictures.
require 'zip/zip'
class FileController < ApplicationController
layout 'default'
before_filter :check_allow_uploads
def file
@file_name = params['id']
if @params['file']
# form supplied
new_file = @web.wiki_files.create(@params['file'])
if new_file.valid?
flash[:info] = "File '#{@file_name}' successfully uploaded"
return_to_last_remembered
else
# pass the file with errors back into the form
@file = new_file
render
end
else
# no form supplied, this is a request to download the file
file = WikiFile.find_by_file_name(@file_name)
if file
send_data(file.content, determine_file_options_for(@file_name, :filename => @file_name))
else
@file = WikiFile.new(:file_name => @file_name)
render
end
end
end
def cancel_upload
return_to_last_remembered
end
def import
if @params['file']
@problems = []
import_file_name = "#{@web.address}-import-#{Time.now.strftime('%Y-%m-%d-%H-%M-%S')}.zip"
import_from_archive(@params['file'].path)
if @problems.empty?
flash[:info] = 'Import successfully finished'
else
flash[:error] = 'Import finished, but some pages were not imported:<li>' +
@problems.join('</li><li>') + '</li>'
end
return_to_last_remembered
else
# to template
end
end
protected
def check_allow_uploads
render(:status => 404, :text => "Web #{@params['web'].inspect} not found") and return false unless @web
if @web.allow_uploads?
return true
else
render :status => 403, :text => 'File uploads are blocked by the webmaster'
return false
end
end
private
def import_from_archive(archive)
logger.info "Importing pages from #{archive}"
zip = Zip::ZipInputStream.open(archive)
while (entry = zip.get_next_entry) do
ext_length = File.extname(entry.name).length
page_name = entry.name[0..-(ext_length + 1)]
page_content = entry.get_input_stream.read
logger.info "Processing page '#{page_name}'"
begin
existing_page = @wiki.read_page(@web.address, page_name)
if existing_page
if existing_page.content == page_content
logger.info "Page '#{page_name}' with the same content already exists. Skipping."
next
else
logger.info "Page '#{page_name}' already exists. Adding a new revision to it."
wiki.revise_page(@web.address, page_name, page_content, Time.now, @author, PageRenderer.new)
end
else
wiki.write_page(@web.address, page_name, page_content, Time.now, @author, PageRenderer.new)
end
rescue => e
logger.error(e)
@problems << "#{page_name} : #{e.message}"
end
end
logger.info "Import from #{archive} finished"
end
end

View file

@ -0,0 +1,29 @@
require_dependency 'cache_sweeping_helper'
class RevisionSweeper < ActionController::Caching::Sweeper
include CacheSweepingHelper
observe Revision, Page
def after_save(record)
if record.is_a?(Revision)
expire_caches(record.page)
end
end
def after_delete(record)
if record.is_a?(Page)
expire_caches(record)
end
end
private
def expire_caches(page)
expire_cached_summary_pages(page.web)
pages_to_expire = ([page.name] + WikiReference.pages_that_reference(page.name)).uniq
pages_to_expire.each { |page_name| expire_cached_page(page.web, page_name) }
end
end

View file

@ -0,0 +1,14 @@
require_dependency 'cache_sweeping_helper'
class WebSweeper < ActionController::Caching::Sweeper
include CacheSweepingHelper
observe Web
def after_save(record)
web = record
web.pages.each { |page| expire_cached_page(web, page.name) }
expire_cached_summary_pages(web)
end
end

View file

@ -0,0 +1,429 @@
require 'fileutils'
require 'redcloth_for_tex'
require 'parsedate'
require 'zip/zip'
class WikiController < ApplicationController
before_filter :load_page
caches_action :show, :published, :authors, :recently_revised, :list
cache_sweeper :revision_sweeper
layout 'default', :except => [:rss_feed, :rss_with_content, :rss_with_headlines, :tex, :export_tex, :export_html]
def index
if @web_name
redirect_home
elsif not @wiki.setup?
redirect_to :controller => 'admin', :action => 'create_system'
elsif @wiki.webs.length == 1
redirect_home @wiki.webs.values.first.address
else
redirect_to :action => 'web_list'
end
end
# Outside a single web --------------------------------------------------------
def authenticate
if password_check(@params['password'])
redirect_home
else
flash[:info] = password_error(@params['password'])
redirect_to :action => 'login', :web => @web_name
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
@page_names_by_author = @web.page_names_by_author
@authors = @page_names_by_author.keys.sort
end
def export_html
stylesheet = File.read(File.join(RAILS_ROOT, 'public', 'stylesheets', 'instiki.css'))
export_pages_as_zip('html') do |page|
renderer = PageRenderer.new(page.revisions.last)
rendered_page = <<-EOL
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>#{page.plain_name} in #{@web.name}</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 {
color: ##{@web ? @web.color : "393" };
}
.newWikiWord { background-color: white; font-style: italic; }
#{stylesheet}
</style>
<style type="text/css">
#{@web.additional_style}
</style>
</head>
<body>
#{renderer.display_content_for_export}
<div class="byline">
#{page.revisions? ? "Revised" : "Created" } on #{ page.revised_at.strftime('%B %d, %Y %H:%M:%S') }
by
#{ UrlGenerator.new(self).make_link(page.author.name, @web, nil, { :mode => :export }) }
</div>
</body>
</html>
EOL
rendered_page
end
end
def export_markup
export_pages_as_zip(@web.markup) { |page| page.content }
end
def export_pdf
file_name = "#{@web.address}-tex-#{@web.revised_at.strftime('%Y-%m-%d-%H-%M-%S')}"
file_path = File.join(@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_at.strftime('%Y-%m-%d-%H-%M-%S')}.tex"
file_path = File.join(@wiki.storage_path, file_name)
export_web_to_tex(file_path) unless FileTest.exists?(file_path)
send_file file_path
end
def feeds
@rss_with_content_allowed = rss_with_content_allowed?
# show the template
end
def list
parse_category
@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
@pages_by_day = Hash.new { |h, day| h[day] = [] }
@pages_by_revision.each do |page|
day = Date.new(page.revised_at.year, page.revised_at.month, page.revised_at.day)
@pages_by_day[day] << page
end
end
def rss_with_content
if rss_with_content_allowed?
render_rss(hide_description = false, *parse_rss_params)
else
render_text 'RSS feed with content for this web is blocked for security reasons. ' +
'The web is password-protected and not published', '403 Forbidden'
end
end
def rss_with_headlines
render_rss(hide_description = true, *parse_rss_params)
end
def search
@query = @params['query']
@title_results = @web.select { |page| page.name =~ /#{@query}/i }.sort
@results = @web.select { |page| page.content =~ /#{@query}/i }.sort
all_pages_found = (@results + @title_results).uniq
if all_pages_found.size == 1
redirect_to_page(all_pages_found.first.name)
end
end
# Within a single page --------------------------------------------------------
def cancel_edit
@page.unlock
redirect_to_page(@page_name)
end
def edit
if @page.nil?
redirect_home
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.revised_at.strftime('%Y-%m-%d-%H-%M-%S')}"
file_path = File.join(@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
if @page.nil?
redirect_home
end
@link_mode ||= :show
@renderer = PageRenderer.new(@page.revisions.last)
# to template
end
def published
if not @web.published?
render(:text => "Published version of web '#{@web_name}' is not available", :status => 404)
return
end
@page_name ||= 'HomePage'
@page ||= wiki.read_page(@web_name, @page_name)
render(:text => "Page '#{@page_name}' not found", :status => 404) and return unless @page
@renderer = PageRenderer.new(@page.revisions.last)
end
def revision
get_page_and_revision
@show_diff = (@params[:mode] == 'diff')
@renderer = PageRenderer.new(@revision)
end
def rollback
get_page_and_revision
end
def save
render(:status => 404, :text => 'Undefined page name') and return if @page_name.nil?
author_name = @params['author']
author_name = 'AnonymousCoward' if author_name =~ /^\s*$/
cookies['author'] = { :value => author_name, :expires => Time.utc(2030) }
begin
filter_spam(@params['content'])
if @page
wiki.revise_page(@web_name, @page_name, @params['content'], Time.now,
Author.new(author_name, remote_ip), PageRenderer.new)
@page.unlock
else
wiki.write_page(@web_name, @page_name, @params['content'], Time.now,
Author.new(author_name, remote_ip), PageRenderer.new)
end
redirect_to_page @page_name
rescue => e
flash[:error] = e
logger.error e
flash[:content] = @params['content']
if @page
@page.unlock
redirect_to :action => 'edit', :web => @web_name, :id => @page_name
else
redirect_to :action => 'new', :web => @web_name, :id => @page_name
end
end
end
def show
if @page
begin
@renderer = PageRenderer.new(@page.revisions.last)
@show_diff = (@params[:mode] == 'diff')
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
flash[:error] = e.message
if in_a_web?
redirect_to :action => 'edit', :web => @web_name, :id => @page_name
else
raise e
end
end
else
if not @page_name.nil? and not @page_name.empty?
redirect_to :web => @web_name, :action => 'new', :id => @page_name
else
render_text 'Page name is not specified', '404 Not Found'
end
end
end
def tex
@tex_content = RedClothForTex.new(@page.content).to_tex
end
protected
def load_page
@page_name = @params['id']
@page = @wiki.read_page(@web_name, @page_name) if @page_name
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
begin
wd = Dir.getwd
Dir.chdir(File.dirname(tex_path))
logger.info `pdflatex --interaction=nonstopmode #{File.basename(tex_path)}`
ensure
Dir.chdir(wd)
end
end
def export_page_to_tex(file_path)
tex
File.open(file_path, 'w') { |f| f.write(render_to_string(:template => 'wiki/tex', :layout => false)) }
end
def export_pages_as_zip(file_type, &block)
file_prefix = "#{@web.address}-#{file_type}-"
timestamp = @web.revised_at.strftime('%Y-%m-%d-%H-%M-%S')
file_path = File.join(@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("#{CGI.escape(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 "<html><head>" +
"<META HTTP-EQUIV=\"Refresh\" CONTENT=\"0;URL=HomePage.#{file_type}\"></head></html>"
end
end
FileUtils.rm_rf(Dir[File.join(@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.page('HomePage').content, render_tex_web)
File.open(file_path, 'w') { |f| f.write(render_to_string(:template => 'wiki/tex_web', :layout => nil)) }
end
def get_page_and_revision
if @params['rev']
@revision_number = @params['rev'].to_i
else
@revision_number = @page.revisions.length
end
@revision = @page.revisions[@revision_number - 1]
end
def parse_category
@categories = WikiReference.list_categories.sort
@category = @params['category']
if @category
@set_name = "category '#{@category}'"
pages = WikiReference.pages_in_category(@category).sort.map { |page_name| @web.page(page_name) }
@pages_in_category = PageSet.new(@web, pages)
else
# no category specified, return all pages of the web
@pages_in_category = @web.select_all.by_name
@set_name = 'the web'
end
end
def parse_rss_params
if @params.include? 'limit'
limit = @params['limit'].to_i rescue nil
limit = nil if limit == 0
else
limit = 15
end
start_date = Time.local(*ParseDate::parsedate(@params['start'])) rescue nil
end_date = Time.local(*ParseDate::parsedate(@params['end'])) rescue nil
[ limit, start_date, end_date ]
end
def remote_ip
ip = @request.remote_ip
logger.info(ip)
ip
end
def render_rss(hide_description = false, limit = 15, start_date = nil, end_date = nil)
if limit && !start_date && !end_date
@pages_by_revision = @web.select.by_revision.first(limit)
else
@pages_by_revision = @web.select.by_revision
@pages_by_revision.reject! { |page| page.revised_at < start_date } if start_date
@pages_by_revision.reject! { |page| page.revised_at > end_date } if end_date
end
@hide_description = hide_description
@link_action = @web.password ? 'published' : 'show'
render :action => '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 rss_with_content_allowed?
@web.password.nil? or @web.published?
end
def truncate(text, length = 30, truncate_string = '...')
if text.length > length then text[0..(length - 3)] + truncate_string else text end
end
def filter_spam(content)
@@spam_patterns ||= load_spam_patterns
@@spam_patterns.each do |pattern|
raise "Your edit was blocked by spam filtering" if content =~ pattern
end
end
def load_spam_patterns
spam_patterns_file = "#{RAILS_ROOT}/config/spam_patterns.txt"
if File.exists?(spam_patterns_file)
File.readlines(spam_patterns_file).inject([]) { |patterns, line| patterns << Regexp.new(line.chomp, Regexp::IGNORECASE) }
else
[]
end
end
end

View file

@ -0,0 +1,93 @@
# The methods added to this helper will be available to all templates in the application.
module ApplicationHelper
# Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container
# where the elements respond to first and last (such as a two-element array), the "lasts" serve as option values and
# the "firsts" as option text. Hashes are turned into this form automatically, so the keys become "firsts" and values
# become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag.
#
# Examples (call, result):
# html_options([["Dollar", "$"], ["Kroner", "DKK"]])
# <option value="$">Dollar</option>\n<option value="DKK">Kroner</option>
#
# html_options([ "VISA", "Mastercard" ], "Mastercard")
# <option>VISA</option>\n<option selected>Mastercard</option>
#
# html_options({ "Basic" => "$20", "Plus" => "$40" }, "$40")
# <option value="$20">Basic</option>\n<option value="$40" selected>Plus</option>
def html_options(container, selected = nil)
container = container.to_a if Hash === container
html_options = container.inject([]) do |options, element|
if element.is_a? Array
if element.last != selected
options << "<option value=\"#{element.last}\">#{element.first}</option>"
else
options << "<option value=\"#{element.last}\" selected>#{element.first}</option>"
end
else
options << ((element != selected) ? "<option>#{element}</option>" : "<option selected>#{element}</option>")
end
end
html_options.join("\n")
end
# Creates a hyperlink to a Wiki page, without checking if the page exists or not
def link_to_existing_page(page, text = nil, html_options = {})
link_to(
text || page.plain_name,
{:web => @web.address, :action => 'show', :id => page.name, :only_path => true},
html_options)
end
# Creates a hyperlink to a Wiki page, or to a "new page" form if the page doesn't exist yet
def link_to_page(page_name, web = @web, text = nil, options = {})
raise 'Web not defined' if web.nil?
UrlGenerator.new(@controller).make_link(page_name, web, text,
options.merge(:base_url => "#{base_url}/#{web.address}"))
end
def author_link(page, options = {})
UrlGenerator.new(@controller).make_link(page.author.name, page.web, nil, options)
end
def base_url
home_page_url = url_for :controller => 'admin', :action => 'create_system', :only_path => true
home_page_url.sub(%r-/create_system/?$-, '')
end
# Creates a menu of categories
def categories_menu
if @categories.empty?
''
else
"<div id=\"categories\">\n" +
'<strong>Categories</strong>:' +
'[' + link_to_unless_current('Any', :web => @web.address, :action => @action_name) + "]\n" +
@categories.map { |c|
link_to_unless_current(c, :web => @web.address, :action => @action_name, :category => c)
}.join(', ') + "\n" +
'</div>'
end
end
# Performs HTML escaping on text, but keeps linefeeds intact (by replacing them with <br/>)
def escape_preserving_linefeeds(text)
h(text).gsub(/\n/, '<br/>')
end
def format_date(date, include_time = true)
# Must use DateTime because Time doesn't support %e on at least some platforms
if include_time
DateTime.new(date.year, date.mon, date.day, date.hour, date.min, date.sec).strftime("%B %e, %Y %H:%M:%S")
else
DateTime.new(date.year, date.mon, date.day).strftime("%B %e, %Y")
end
end
def rendered_content(page)
PageRenderer.new(page.revisions.last).display_content
end
end

View file

@ -0,0 +1,89 @@
module WikiHelper
def navigation_menu_for_revision
menu = []
menu << forward
menu << back_for_revision if @revision_number > 1
menu << current_revision
menu << see_or_hide_changes_for_revision if @revision_number > 1
menu << rollback
menu
end
def navigation_menu_for_page
menu = []
menu << edit_page
menu << edit_web if @page.name == "HomePage"
if @page.revisions.length > 1
menu << back_for_page
menu << see_or_hide_changes_for_page
end
menu
end
def edit_page
link_text = (@page.name == "HomePage" ? 'Edit Page' : 'Edit')
link_to(link_text, {:web => @web.address, :action => 'edit', :id => @page.name},
{:class => 'navlink', :accesskey => 'E', :name => 'edit'})
end
def edit_web
link_to('Edit Web', {:web => @web.address, :action => 'edit_web'},
{:class => 'navlink', :accesskey => 'W', :name => 'edit_web'})
end
def forward
if @revision_number < @page.revisions.length - 1
link_to('Forward in time',
{:web => @web.address, :action => 'revision', :id => @page.name, :rev => @revision_number + 1},
{:class => 'navlink', :accesskey => 'F', :name => 'to_next_revision'}) +
" <small>(#{@revision.page.revisions.length - @revision_number} more)</small> "
else
link_to('Forward in time', {:web => @web.address, :action => 'show', :id => @page.name},
{:class => 'navlink', :accesskey => 'F', :name => 'to_next_revision'}) +
" <small> (to current)</small>"
end
end
def back_for_revision
link_to('Back in time',
{:web => @web.address, :action => 'revision', :id => @page.name, :rev => @revision_number - 1},
{:class => 'navlink', :name => 'to_previous_revision'}) +
" <small>(#{@revision_number - 1} more)</small>"
end
def back_for_page
link_to('Back in time',
{:web => @web.address, :action => 'revision', :id => @page.name,
:rev => @page.revisions.length - 1},
{:class => 'navlink', :accesskey => 'B', :name => 'to_previous_revision'}) +
" <small>(#{@page.revisions.length - 1} #{@page.revisions.length - 1 == 1 ? 'revision' : 'revisions'})</small>"
end
def current_revision
link_to('See current', {:web => @web.address, :action => 'show', :id => @page.name},
{:class => 'navlink', :name => 'to_current_revision'})
end
def see_or_hide_changes_for_revision
link_to(@show_diff ? 'Hide changes' : 'See changes',
{:web => @web.address, :action => 'revision', :id => @page.name, :rev => @revision_number,
:mode => (@show_diff ? nil : 'diff') },
{:class => 'navlink', :accesskey => 'C', :name => 'see_changes'})
end
def see_or_hide_changes_for_page
link_to(@show_diff ? 'Hide changes' : 'See changes',
{:web => @web.address, :action => 'show', :id => @page.name, :mode => (@show_diff ? nil : 'diff') },
{:class => 'navlink', :accesskey => 'C', :name => 'see_changes'})
end
def rollback
link_to('Rollback',
{:web => @web.address, :action => 'rollback', :id => @page.name, :rev => @revision_number},
{:class => 'navlink', :name => 'rollback'})
end
end

18
app/models/author.rb Normal file
View file

@ -0,0 +1,18 @@
class Author < String
attr_accessor :ip
attr_reader :name
def initialize(name, ip = nil)
@ip = ip
super(name)
end
def name=(value)
self.gsub!(/.+/, value)
end
alias_method :name, :to_s
def <=>(other)
name <=> other.to_s
end
end

121
app/models/page.rb Normal file
View file

@ -0,0 +1,121 @@
class Page < ActiveRecord::Base
belongs_to :web
has_many :revisions, :order => 'id'
has_many :wiki_references, :order => 'referenced_name'
has_one :current_revision, :class_name => 'Revision', :order => 'id DESC'
def revise(content, time, author, renderer)
revisions_size = new_record? ? 0 : revisions.size
if (revisions_size > 0) and content == current_revision.content
raise Instiki::ValidationError.new(
"You have tried to save page '#{name}' without changing its content")
end
author = Author.new(author.to_s) unless author.is_a?(Author)
# Try to render content to make sure that markup engine can take it,
renderer.revision = Revision.new(
:page => self, :content => content, :author => author, :revised_at => time)
renderer.display_content(update_references = true)
# 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_size > 0) && continous_revision?(time, author)
current_revision.update_attributes(:content => content, :revised_at => time)
else
revisions.create(:content => content, :author => author, :revised_at => time)
end
save
self
end
def rollback(revision_number, time, author_ip, renderer)
roll_back_revision = self.revisions[revision_number]
if roll_back_revision.nil?
raise Instiki::ValidationError.new("Revision #{revision_number} not found")
end
author = Author.new(roll_back_revision.author.name, author_ip)
revise(roll_back_revision.content, time, author, renderer)
end
def revisions?
revisions.size > 1
end
def previous_revision(revision)
revision_index = revisions.each_with_index do |rev, index|
if rev.id == revision.id
break index
else
nil
end
end
if revision_index.nil? or revision_index == 0
nil
else
revisions[revision_index - 1]
end
end
def references
web.select.pages_that_reference(name)
end
def wiki_words
wiki_references.select { |ref| ref.wiki_word? }.map { |ref| ref.referenced_name }
end
def linked_from
web.select.pages_that_link_to(name)
end
def included_from
web.select.pages_that_include(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
LOCKING_PERIOD = 30.minutes
def lock(time, locked_by)
update_attributes(:locked_at => time, :locked_by => locked_by)
end
def lock_duration(time)
((time - locked_at) / 60).to_i unless locked_at.nil?
end
def unlock
update_attribute(:locked_at, nil)
end
def locked?(comparison_time)
locked_at + LOCKING_PERIOD > comparison_time unless locked_at.nil?
end
def to_param
name
end
private
def continous_revision?(time, author)
(current_revision.author == author) && (revised_at + 30.minutes > time)
end
# Forward method calls to the current revision, so the page responds to all revision calls
def method_missing(method_id, *args, &block)
method_name = method_id.to_s
# Perform a hand-off to AR::Base#method_missing
if @attributes.include?(method_name) or md = /(=|\?|_before_type_cast)$/.match(method_name)
super(method_id, *args, &block)
else
current_revision.send(method_id)
end
end
end

View file

@ -0,0 +1,15 @@
# This class maintains the state of wiki references for newly created or newly deleted pages
class PageObserver < ActiveRecord::Observer
def after_create(page)
WikiReference.update_all("link_type = '#{WikiReference::LINKED_PAGE}'",
['referenced_name = ?', page.name])
end
def before_destroy(page)
WikiReference.delete_all ['page_id = ?', page.id]
WikiReference.update_all("link_type = '#{WikiReference::WANTED_PAGE}'",
['referenced_name = ?', page.name])
end
end

92
app/models/page_set.rb Normal file
View file

@ -0,0 +1,92 @@
# 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)
# 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.revised_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.revised_at }).reverse
end
def pages_that_reference(page_name)
all_referring_pages = WikiReference.pages_that_reference(page_name)
self.select { |page| all_referring_pages.include?(page.name) }
end
def pages_that_link_to(page_name)
all_linking_pages = WikiReference.pages_that_link_to(page_name)
self.select { |page| all_linking_pages.include?(page.name) }
end
def pages_that_include(page_name)
all_including_pages = WikiReference.pages_that_include(page_name)
self.select { |page| all_including_pages.include?(page.name) }
end
def pages_authored_by(author)
all_pages_authored_by_the_author =
Page.connection.select_all(sanitize_sql([
"SELECT page_id FROM revision WHERE author = '?'", 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
# Pages that refer to themselves and have no links from outside are oprphans.
def orphaned_pages
never_orphans = web.authors + ['HomePage']
self.select { |page|
if never_orphans.include? page.name
false
else
references = pages_that_reference(page.name)
references.empty? or references == [page]
end
}
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.sort
end
end

4
app/models/revision.rb Normal file
View file

@ -0,0 +1,4 @@
class Revision < ActiveRecord::Base
belongs_to :page
composed_of :author, :mapping => [ %w(author name), %w(ip ip) ]
end

4
app/models/system.rb Normal file
View file

@ -0,0 +1,4 @@
class System < ActiveRecord::Base
set_table_name 'system'
validates_presence_of :password
end

147
app/models/web.rb Normal file
View file

@ -0,0 +1,147 @@
class Web < ActiveRecord::Base
has_many :pages
has_many :wiki_files
def wiki
Wiki.new
end
def settings_changed?(markup, safe_mode, brackets_only)
self.markup != markup ||
self.safe_mode != safe_mode ||
self.brackets_only != brackets_only
end
def add_page(name, content, time, author, renderer)
page = page(name) || Page.new(:web => self, :name => name)
page.revise(content, time, author, renderer)
end
def authors
connection.select_all(
'SELECT DISTINCT r.author AS author ' +
'FROM revisions r ' +
'JOIN pages p ON p.id = r.page_id ' +
'WHERE p.web_id = ' + self.id.to_s +
'ORDER by 1 '
).collect { |row| row['author'] }
end
def categories
select.map { |page| page.categories }.flatten.uniq.sort
end
def page(name)
pages.find(:first, :conditions => ['name = ?', name])
end
def last_page
return Page.find(:first, :order => 'id desc', :conditions => ['web_id = ?', self.id])
end
def has_page?(name)
Page.count(['web_id = ? AND name = ?', id, name]) > 0
end
def has_file?(file_name)
WikiFile.find_by_file_name(file_name) != nil
end
def markup
read_attribute('markup').to_sym
end
def page_names_by_author
connection.select_all(
'SELECT DISTINCT r.author AS author, p.name AS page_name ' +
'FROM revisions r ' +
'JOIN pages p ON r.page_id = p.id ' +
"WHERE p.web_id = #{self.id} " +
'ORDER by p.name'
).inject({}) { |result, row|
author, page_name = row['author'], row['page_name']
result[author] = [] unless result.has_key?(author)
result[author] << page_name
result
}
end
def remove_pages(pages_to_be_removed)
pages_to_be_removed.each { |p| p.destroy }
end
def revised_at
select.most_recent_revision
end
def select(&condition)
PageSet.new(self, pages, condition)
end
def select_all
PageSet.new(self, pages, nil)
end
def to_param
address
end
def create_files_directory
return unless allow_uploads == 1
dummy_file = self.wiki_files.build(:file_name => '0', :description => '0', :content => '0')
dir = File.dirname(dummy_file.content_path)
begin
require 'fileutils'
FileUtils.mkdir_p dir
dummy_file.save
dummy_file.destroy
rescue => e
logger.error("Failed create files directory for #{self.address}: #{e}")
raise "Instiki could not create directory to store uploaded files. " +
"Please make sure that Instiki is allowed to create directory " +
"#{File.expand_path(dir)} and add files to it."
end
end
private
# Returns an array of all the wiki words in any current revision
def wiki_words
pages.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.map { |p| p.name }
end
protected
before_save :sanitize_markup
after_save :create_files_directory
before_validation :validate_address
validates_uniqueness_of :address
validates_length_of :color, :in => 3..6
def sanitize_markup
self.markup = markup.to_s
end
def validate_address
unless address == CGI.escape(address)
self.errors.add(:address, 'should contain only valid URI characters')
raise Instiki::ValidationError.new("#{self.class.human_attribute_name('address')} #{errors.on(:address)}")
end
end
def default_web?
defined? DEFAULT_WEB and self.address == DEFAULT_WEB
end
def files_path
if default_web?
"#{RAILS_ROOT}/public/files"
else
"#{RAILS_ROOT}/public/#{self.address}/files"
end
end
end

92
app/models/wiki.rb Normal file
View file

@ -0,0 +1,92 @@
class Wiki
cattr_accessor :storage_path, :logger
self.storage_path = "#{RAILS_ROOT}/storage/"
self.logger = RAILS_DEFAULT_LOGGER
def authenticate(password)
password == (system.password || 'instiki')
end
def create_web(name, address, password = nil)
@webs = nil
Web.create(:name => name, :address => address, :password => password)
end
def delete_web(address)
web = Web.find_by_address(address)
unless web.nil?
web.destroy
@webs = nil
end
end
def edit_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, max_upload_size = nil)
if not (web = Web.find_by_address(old_address))
raise Instiki::ValidationError.new("Web with address '#{old_address}' does not exist")
end
web.update_attributes(:address => new_address, :name => name, :markup => markup, :color => color,
:additional_style => additional_style, :safe_mode => safe_mode, :password => password, :published => published,
:brackets_only => brackets_only, :count_pages => count_pages, :allow_uploads => allow_uploads, :max_upload_size => max_upload_size)
@webs = nil
raise Instiki::ValidationError.new("There is already a web with address '#{new_address}'") unless web.errors.on(:address).nil?
web
end
def read_page(web_address, page_name)
self.class.logger.debug "Reading page '#{page_name}' from web '#{web_address}'"
web = Web.find_by_address(web_address)
if web.nil?
self.class.logger.debug "Web '#{web_address}' not found"
return nil
else
page = web.pages.find(:first, :conditions => ['name = ?', page_name])
self.class.logger.debug "Page '#{page_name}' #{page.nil? ? 'not' : ''} found"
return page
end
end
def remove_orphaned_pages(web_address)
web = Web.find_by_address(web_address)
web.remove_pages(web.select.orphaned_pages)
end
def revise_page(web_address, page_name, content, revised_at, author, renderer)
page = read_page(web_address, page_name)
page.revise(content, revised_at, author, renderer)
end
def rollback_page(web_address, page_name, revision_number, time, author_id = nil)
page = read_page(web_address, page_name)
page.rollback(revision_number, time, author_id)
end
def setup(password, web_name, web_address)
system.update_attribute(:password, password)
create_web(web_name, web_address)
end
def system
@system ||= (System.find(:first) || System.create)
end
def setup?
Web.count > 0
end
def webs
@webs ||= Web.find(:all).inject({}) { |webs, web| webs.merge(web.address => web) }
end
def storage_path
self.class.storage_path
end
def write_page(web_address, page_name, content, written_on, author, renderer)
Web.find_by_address(web_address).add_page(page_name, content, written_on, author, renderer)
end
end

64
app/models/wiki_file.rb Normal file
View file

@ -0,0 +1,64 @@
class WikiFile < ActiveRecord::Base
belongs_to :web
before_save :write_content_to_file
before_destroy :delete_content_file
validates_presence_of %w( web file_name )
validates_length_of :file_name, :within=>1..50
validates_length_of :description, :maximum=>255
def self.find_by_file_name(file_name)
find(:first, :conditions => ['file_name = ?', file_name])
end
SANE_FILE_NAME = /^[a-zA-Z0-9\-_\. ]*$/
def validate
if file_name
if file_name !~ SANE_FILE_NAME
errors.add("file_name", "is invalid. Only latin characters, digits, dots, underscores, " +
"dashes and spaces are accepted")
elsif file_name == '.' or file_name == '..'
errors.add("file_name", "cannot be '.' or '..'")
end
end
if @web and @content
if (@content.size > @web.max_upload_size.kilobytes)
errors.add("content", "size (#{(@content.size / 1024.0).round} kilobytes) exceeds " +
"the maximum (#{web.max_upload_size} kilobytes) set for this wiki")
end
end
errors.add("content", "is empty") if @content.nil? or @content.empty?
end
def content=(content)
if content.respond_to? :read
@content = content.read
else
@content = content
end
end
def content
@content ||= ( File.open(content_path, 'rb') { |f| f.read } )
end
def content_path
web.files_path + '/' + file_name
end
def write_content_to_file
web.create_files_directory unless File.exists?(web.files_path)
File.open(self.content_path, 'wb') { |f| f.write(@content) }
end
def delete_content_file
require 'fileutils'
FileUtils.rm_f(content_path) if File.exists?(content_path)
end
end

View file

@ -0,0 +1,82 @@
class WikiReference < ActiveRecord::Base
LINKED_PAGE = 'L'
WANTED_PAGE = 'W'
INCLUDED_PAGE = 'I'
CATEGORY = 'C'
AUTHOR = 'A'
FILE = 'F'
WANTED_FILE = 'E'
belongs_to :page
validates_inclusion_of :link_type, :in => [LINKED_PAGE, WANTED_PAGE, INCLUDED_PAGE, CATEGORY, AUTHOR, FILE, WANTED_FILE]
# FIXME all finders below MUST restrict their results to pages belonging to a particular web
def self.link_type(web, page_name)
web.has_page?(page_name) ? LINKED_PAGE : WANTED_PAGE
end
def self.pages_that_reference(page_name)
query = 'SELECT name FROM pages JOIN wiki_references ON pages.id = wiki_references.page_id ' +
'WHERE wiki_references.referenced_name = ?' +
"AND wiki_references.link_type in ('#{LINKED_PAGE}', '#{WANTED_PAGE}', '#{INCLUDED_PAGE}')"
names = connection.select_all(sanitize_sql([query, page_name])).map { |row| row['name'] }
end
def self.pages_that_link_to(page_name)
query = 'SELECT name FROM pages JOIN wiki_references ON pages.id = wiki_references.page_id ' +
'WHERE wiki_references.referenced_name = ? ' +
"AND wiki_references.link_type in ('#{LINKED_PAGE}', '#{WANTED_PAGE}')"
names = connection.select_all(sanitize_sql([query, page_name])).map { |row| row['name'] }
end
def self.pages_that_include(page_name)
query = 'SELECT name FROM pages JOIN wiki_references ON pages.id = wiki_references.page_id ' +
'WHERE wiki_references.referenced_name = ? ' +
"AND wiki_references.link_type = '#{INCLUDED_PAGE}'"
names = connection.select_all(sanitize_sql([query, page_name])).map { |row| row['name'] }
end
def self.pages_in_category(category)
query =
'SELECT name FROM pages JOIN wiki_references ON pages.id = wiki_references.page_id ' +
'WHERE wiki_references.referenced_name = ? ' +
"AND wiki_references.link_type = '#{CATEGORY}'"
names = connection.select_all(sanitize_sql([query, category])).map { |row| row['name'] }
end
def self.list_categories
query = "SELECT DISTINCT referenced_name FROM wiki_references WHERE link_type = '#{CATEGORY}'"
connection.select_all(query).map { |row| row['referenced_name'] }
end
def wiki_word?
linked_page? or wanted_page?
end
def wiki_link?
linked_page? or wanted_page? or file? or wanted_file?
end
def linked_page?
link_type == LINKED_PAGE
end
def wanted_page?
link_type == WANTED_PAGE
end
def included_page?
link_type == INCLUDED_PAGE
end
def file?
link_type == FILE
end
def wanted_file?
link_type == WANTED_FILE
end
end

View file

@ -0,0 +1,86 @@
<% @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_tag({ :controller => 'admin', :action => 'create_system' },
{ 'id' => 'setup', 'method' => 'post', 'onSubmit' => 'return validateSetup()',
'accept-charset' => 'utf-8' })
%>
<ol class="setup">
<li>
<h2 style="margin-bottom: 3px">Name and address for your first web</h2>
<div class="help">
The name of the web is included in the title on all pages.
The address is the base path that all pages within the web live beneath.
Ex: the address "rails" gives URLs like <i>/rails/show/HomePage</i>.
The address can only consist of letters and digits.
</div>
<div class="inputBox">
Name: <input type="text" id="web_name" name="web_name" value="Wiki"
onChange="proposeAddress();" onClick="this.value == 'Wiki' ? this.value = '' : true" />
&nbsp;&nbsp;
Address: <input type="text" id="web_address" name="web_address" onChange="cleanAddress();"
value="wiki" />
</div>
</li>
<li>
<h2 style="margin-bottom: 3px">Password for creating and changing webs</h2>
<div class="help">
Administrative access allows you to make new webs and change existing ones.
</div>
<div class="help"><em>Everyone with this password will be able to do this, so pick it carefully!</em></div>
<div class="inputBox">
Password: <input type="password" id="password" name="password" />
&nbsp;&nbsp;
Verify: <input type="password" id="password_check" name="password_check" />
</div>
</li>
</ol>
<p align="right">
<input type="submit" value="Setup" style="margin-left: 40px" />
</p>
<%= end_form_tag %>
<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>

View file

@ -0,0 +1,72 @@
<% @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_tag({ :controller => 'admin', :action => 'create_web' },
{ 'id' => 'setup', 'method' => 'post',
'onSubmit' => 'cleanAddress(); return validateSetup()',
'accept-charset' => 'utf-8' })
%>
<ol class="setup">
<li>
<h2 style="margin-bottom: 3px">Name and address for your new web</h2>
<div class="help">
The name of the web is included in the title on all pages.
The address is the base path that all pages within the web live beneath.
Ex: the address "rails" gives URLs like <i>/rails/show/HomePage</i>.
The address can only consist of letters and digits.
</div>
<div class="inputBox">
Name: <input type="text" id="web_name" name="name" onChange="proposeAddress();" />
&nbsp;&nbsp;
Address: <input type="text" id="web_address" name="address" onChange="cleanAddress();" />
</div>
</li>
</ol>
<p align="right">
<small>
Enter system password
<input type="password" id="system_password" name="system_password" />
and
<input type="submit" value="Create Web" />
</small>
</p>
<%= end_form_tag %>
<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>

View file

@ -0,0 +1,136 @@
<% @title = "Edit Web" %>
<%= form_tag({ :controller => 'admin', :action => 'edit_web', :web => @web.address },
{ 'id' => 'setup', 'method' => 'post',
'onSubmit' => 'cleanAddress(); return validateSetup()',
'accept-charset' => 'utf-8' })
%>
<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">
Name: <input type="text" id="name" name="name" class="disableAutoComplete" value="<%= @web.name %>"
onChange="proposeAddress();" /> &nbsp;&nbsp;
Address: <input type="text" class="disableAutoComplete" id="address" name="address" value="<%= @web.address %>"
onChange="cleanAddress();" />
<small><em>(Letters and digits only)</em></small>
</div>
<h2 style="margin-bottom: 3px">Specialize</h2>
<div class="inputBox">
Markup:
<select name="markup">
<%= html_options({'Textile' => :textile, 'Markdown' => :markdown, 'Mixed' => :mixed,
'RDoc' => :rdoc }, @web.markup) %>
</select>
&nbsp;&nbsp;
Color:
<select name="color">
<%= html_options({ 'Green' => '008B26', 'Purple' => '504685', 'Red' => 'DA0006',
'Orange' => 'FA6F00', 'Grey' => '8BA2B0' }, @web.color) %>
</select>
<br/>
<p>
<small>
<input type="checkbox" class="disableAutoComplete" name="safe_mode" <%= 'checked="on"' if @web.safe_mode? %> />
Safe mode
<em>- strip HTML tags and stylesheet options from the content of all pages</em>
<br/>
<input type="checkbox" class="disableAutoComplete" name="brackets_only" <%= 'checked="on"' if @web.brackets_only? %> />
Brackets only
<em>- require all wiki words to be as [[wiki word]], WikiWord links won't be created</em>
<br/>
<input type="checkbox" class="disableAutoComplete" name="count_pages" <%= 'checked="on"' if @web.count_pages? %> />
Count pages
<br/>
<input type="checkbox" class="disableAutoComplete" name="allow_uploads" <%= 'checked="on"' if @web.allow_uploads? %> />
Allow uploads of no more than
<input type="text" class="disableAutoComplete" name="max_upload_size" value="<%= @web.max_upload_size %>"
width="20" />
kbytes
<em>-
allow users to upload pictures and other files and include them on wiki pages
</em>
<br/>
</small>
</p>
<a href="#"
onClick="document.getElementById('additionalStyle').style.display='block';return false;">
Stylesheet tweaks &gt;&gt;</a>
<small><em>
- add or change styles used by this web; styles defined here take precedence over
instiki.css. Hint: View HTML source of a page you want to style to find ID names on individual
tags.</em></small>
<br/>
<textarea id="additionalStyle" class="disableAutoComplete"
style="display: none; margin-top: 10px; margin-bottom: 5px; width: 560px; height: 200px"
name="additional_style"><%= @web.additional_style %>
</textarea>
</div>
<h2 style="margin-bottom: 3px">Password protection for this web (<%= @web.name %>)</h2>
<div class="help">
This is the password that visitors need to view and edit this web.
Setting the password to nothing will remove the password protection.
</div>
<div class="inputBox">
Password: <input class="disableAutoComplete" type="password" id="password"
name="password" value="<%= @web.password %>" />
&nbsp;&nbsp;
Verify: <input class="disableAutoComplete" type="password" id="password_check"
value="<%= @web.password %>" name="password_check" />
</div>
<h2 style="margin-bottom: 3px">Publish read-only version of this web (<%= @web.name %>)</h2>
<div class="help">
You can turn on a read-only version of this web that's accessible even when the regular web
is password protected.
The published version is accessible through URLs like /wiki/published/HomePage.
</div>
<div class="inputBox">
<input type="checkbox" name="published" class="disableAutoComplete" <%= '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 <%= link_to 'create a new web', :action => 'create_web' %>
</small>
</p>
<%= end_form_tag %>
<br/>
<h1>Other administrative tasks</h1>
<%= form_tag({:controller => 'admin', :web => @web.address, :action => 'remove_orphaned_pages'},
{ :id => 'remove_orphaned_pages',
:onSubmit => "return checkSystemPassword(document.getElementById('system_password_orphaned').value)",
'accept-charset' => 'utf-8' })
%>
<p align="right">
<small>
Clean up by entering system password
<input type="password" id="system_password_orphaned" class="disableAutoComplete" name="system_password_orphaned" />
and
<input type="submit" value="Delete Orphan Pages" />
</small>
</p>
<%= end_form_tag %>
<%= javascript_include_tag 'edit_web' %>

33
app/views/file/file.rhtml Normal file
View file

@ -0,0 +1,33 @@
<%
@title = "Upload #{h @file_name}"
@hide_navigation = false
%>
<%= error_messages_for 'file' %>
<%= form_tag({ :controller => 'file', :web => @web_name, :action => 'file' },
{ 'multipart' => true , 'accept-charset' => 'utf-8' }) %>
<%= hidden_field 'file', 'file_name' %>
<div class="inputFieldWithPrompt">
<b>Content of <%= h @file_name %> to upload <small>(required)</small>:</b>
<br/>
<input type="file" name="file[content]" size="40" />
<br/>
<small>
Please note that the file you are uploadng will be named <%= h @file_name %> on the wiki -
regardless of how it is named on your computer. To change the wiki name of the file, please go
<%= link_to :back %> and edit the wiki page that refers to the file.
</small>
</div>
<div class="inputFieldWithPrompt">
<b>Description <small>(optional)</small>:</b>
<br/>
<%= text_field "file", "description", "size" => 40 %>
</div>
<div>
<input type="submit" value="Upload" /> as
<%= text_field_tag :author, @author,
:onfocus => "this.value == 'AnonymousCoward' ? this.value = '' : true;",
:onblur => "this.value == '' ? this.value = 'AnonymousCoward' : true" %>
</div>
<%= end_form_tag %>

View file

@ -0,0 +1,23 @@
<p>
<%= form_tag({}, { 'multipart' => true, 'accept-charset' => 'utf-8' }) %>
<p>
File to upload:
<br/>
<input type="file" name="file" size="40" />
</p>
<p>
System password:
<br/>
<input type="password" id="system_password" name="system_password" />
</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 %>
| <%= link_to 'Cancel', :web => @web.address, :action => 'file'%> <small>(unlocks page)</small>
<% end %>
</p>
<%= end_form_tag %>
</p>

View file

@ -0,0 +1,79 @@
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>
<% if @page and (@page.name == 'HomePage') and (%w( show published print ).include?(@action_name)) %>
<%= h @web.name %>
<% elsif @web %>
<%= @title %> in <%= h @web.name %>
<% else %>
<%= @title %>
<% end %>
<%= @show_diff ? ' (changes)' : '' %>
</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="robots" content="<%= @robots_metatag_value %>" />
<style type="text/css">
h1#pageName, .newWikiWord a, a.existingWikiWord, .newWikiWord a:hover, #TextileHelp h3 {
color: #<%= @web ? @web.color : "393" %>;
}
<%= File.read(RAILS_ROOT + '/public/stylesheets/instiki.css') if @inline_style %>
</style>
<%= stylesheet_link_tag 'instiki' unless @inline_style %>
<style type="text/css">
<%= @style_additions %>
<%= @web ? @web.additional_style : '' %>
</style>
<% if @web %>
<%= auto_discovery_link_tag(:rss, :controller => 'wiki', :web => @web.address, :action => 'rss_with_headlines') %>
<%= auto_discovery_link_tag(:rss, :controller => 'wiki', :web => @web.address, :action => 'rss_with_content') %>
<% end %>
</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) %>
<%= h(@web.name) + (@show_diff ? ' (changes)' : '') %>
<% elsif @web %>
<small><%= @web.name %></small><br />
<%= @title %>
<% else %>
<%= @title %>
<% end %>
</h1>
<%= render 'navigation' unless @web.nil? || @hide_navigation %>
<% if @flash[:info] %>
<div class="info"><%= escape_preserving_linefeeds @flash[:info] %></div>
<% end %>
<% if @error or @flash[:error] %>
<div class="errorExplanation"><%= escape_preserving_linefeeds(@error || @flash[:error]) %></div>
<% end %>
<%= @content_for_layout %>
<% if @show_footer %>
<div id="footer">
<div>This site is running on <a href="http://instiki.org/">Instiki</a></div>
<div>Powered by <a href="http://rubyonrails.com/">Ruby on Rails</a></div>
</div>
<% end %>
</div> <!-- Content -->
</div> <!-- Container -->
</body>
</html>

View file

@ -0,0 +1,12 @@
<h3>Markdown formatting tips (<a target="_new" href="http://daringfireball.net/projects/markdown/syntax">advanced</a>)</h3>
<table cellspacing="0" cellpadding="0">
<tr><td>_your text_</td><td class="arrow">&rarr;</td><td><em>your text</em></td></tr>
<tr><td>**your text**</td><td class="arrow">&rarr;</td><td><strong>your text</strong></td></tr>
<tr><td>`my code`</td><td class="arrow">&rarr;</td><td><code>my code</code></td></tr>
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">&rarr;</td><td>&#8226; Bulleted list<br />&#8226; Second item</td></tr>
<tr><td>1. Numbered list<br />1. Second item</td><td class="arrow">&rarr;</td><td>1. Numbered list<br />2. Second item</td></tr>
<tr><td>[link name](URL)</td><td class="arrow">&rarr;</td><td><a href="URL">link name</a></td></tr>
<tr><td>***</td><td class="arrow">&rarr;</td><td>Horizontal ruler</td></tr>
<tr><td>&lt;http://url><br />&lt;email@add.com></td><td class="arrow">&rarr;</td><td>Auto-linked</td></tr>
<tr><td>![Alt text](URL)</td><td class="arrow">&rarr;</td><td>Image</td></tr>
</table>

View file

@ -0,0 +1,7 @@
<%= render 'textile_help' %>
<h3>Markdown</h3>
<p>
In addition to Textile, this wiki also understands
<a target="_new" href="http://daringfireball.net/projects/markdown/syntax">Markdown</a>.
</p>

View file

@ -0,0 +1,28 @@
<%
def list_item(text, link_options, description, accesskey = nil)
link_options[:controller] = 'wiki'
link_options[:web] = @web.address
link_to_unless_current(text, link_options, :title => description, :accesskey => accesskey) {
content_tag('b', text, 'title' => description, 'class' => 'navOn')
}
end
%>
<div class="navigation">
<% if @action_name != 'published' then %>
<%= list_item 'Home Page', {:action => 'show', :id => 'HomePage'}, 'Home, Sweet Home', 'H' %> |
<%= list_item 'All Pages', {:action => 'list'}, 'Alphabetically sorted list of pages', 'A' %> |
<%= list_item 'Recently Revised', {:action =>'recently_revised'}, 'Pages sorted by when they were last changed', 'U' %> |
<%= list_item 'Authors', {:action => 'authors'}, 'Who wrote what' %> |
<%= list_item 'Feeds', {:action => 'feeds'}, 'Subscribe to changes by RSS' %> |
<%= list_item 'Export', {:action => 'export'}, 'Download a zip with all the pages in this wiki', 'X' %> |
<%= form_tag({ :controller => 'wiki', :action => 'search', :web => @web.address},
{'id' => 'navigationSearchForm', 'method' => 'get', 'accept-charset' => 'utf-8' }) %>
<input type="text" id="searchField" name="query" value="Search"
onfocus="this.value == 'Search' ? this.value = '' : true"
onblur="this.value == '' ? this.value = 'Search' : true" />
<%= end_form_tag %>
<% else %>
<%= list_item 'Home Page', {:action => 'published', :id => 'HomePage'}, 'Home, Sweet Home', 'H' %> |
<% end%>
</div>

12
app/views/rdoc_help.rhtml Normal file
View file

@ -0,0 +1,12 @@
<h3>RDoc formatting tips (<a target="_new" href="http://rdoc.sourceforge.net/doc/files/markup/simple_markup_rb.html">advanced</a>)</h3>
<table cellspacing="0" cellpadding="0">
<tr><td>_your text_</td><td class="arrow">&rarr;</td><td><em>your text</em></td></tr>
<tr><td>*your text*</td><td class="arrow">&rarr;</td><td><strong>your text</strong></td></tr>
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">&rarr;</td><td>&#8226; Bulleted list<br />&#8226; Second item</td></tr>
<tr><td>1. Numbered list<br />2. Second item</td><td class="arrow">&rarr;</td><td>1. Numbered list<br />2. Second item</td></tr>
<tr><td>+my_code+</td><td class="arrow">&rarr;</td><td><code>my_code</code></td></tr>
<tr><td>---</td><td class="arrow">&rarr;</td><td>Horizontal ruler</td></tr>
<tr><td>[[URL linkname]]</td><td class="arrow">&rarr;</td><td><a href="URL">linkname</a></td></tr>
<tr><td>http://url<br />mailto:e@add.com</td><td class="arrow">&rarr;</td><td>Auto-linked</td></tr>
<tr><td>imageURL</td><td class="arrow">&rarr;</td><td>Image</td></tr>
</table>

View file

@ -0,0 +1,24 @@
<h3>Textile formatting tips (<a href="http://hobix.com/textile/quick.html" onClick="quickRedReference(); return false;">advanced</a>)</h3>
<table cellspacing="0" cellpadding="0">
<tr><td>_your text_</td><td class="arrow">&rarr;</td><td><em>your text</em></td></tr>
<tr><td>*your text*</td><td class="arrow">&rarr;</td><td><strong>your text</strong></td></tr>
<tr><td>%{color:red}hello%</td><td class="arrow">&rarr;</td><td><span style="color: red;">hello</span></td></tr>
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">&rarr;</td><td>&#8226; Bulleted list<br />&#8226; Second item</td></tr>
<tr><td># Numbered list<br /># Second item</td><td class="arrow">&rarr;</td><td>1. Numbered list<br />2. Second item</td></tr>
<tr><td>"linkname":URL</td><td class="arrow">&rarr;</td><td><a href="URL">linkname</a></td></tr>
<tr><td>|a|table|row|<br />|b|table|row|</td><td class="arrow">&rarr;</td><td>Table</td></tr>
<tr><td>http://url<br />email@address.com</td><td class="arrow">&rarr;</td><td>Auto-linked</td></tr>
<tr><td>!imageURL!</td><td class="arrow">&rarr;</td><td>Image</td></tr>
</table>
<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>

View file

@ -0,0 +1,13 @@
<% unless @page.linked_from.empty? %>
<small>
| Linked from:
<%= @page.linked_from.collect { |referring_page| link_to_existing_page referring_page }.join(", ") %>
</small>
<% end %>
<% unless @page.included_from.empty? %>
<small>
| Included from:
<%= @page.included_from.collect { |referring_page| link_to_existing_page referring_page }.join(", ") %>
</small>
<% end %>

View file

@ -0,0 +1,11 @@
<% @title = 'Authors' %>
<ul id="authorList">
<% for author in @authors %>
<li>
<%= link_to_page author %>
co- or authored:
<%= @page_names_by_author[author].collect { |page_name| link_to_page(page_name) }.sort.join ', ' %>
</li>
<% end %>
</ul>

40
app/views/wiki/edit.rhtml Normal file
View file

@ -0,0 +1,40 @@
<%
@title = "Editing #{@page.name}"
@content_width = 720
@hide_navigation = true
%>
<div id="MarkupHelp">
<%= render("#{@web.markup}_help") %>
<%= render 'wiki_words_help' %>
</div>
<div id="editForm">
<%= form_tag({ :action => 'save', :web => @web.address, :id => @page.name },
{ 'id' => 'editForm', 'method' => 'post', 'onSubmit' => 'cleanAuthorName()',
'accept-charset' => 'utf-8' }) %>
<textarea name="content" id="content"><%= h(@flash[:content] || @page.content) %></textarea>
<div id="editFormButtons">
<input type="submit" value="Submit" accesskey="s"/> as
<%= text_field_tag :author, @author,
:onfocus => "this.value == 'AnonymousCoward' ? this.value = '' : true;",
:onblur => "this.value == '' ? this.value = 'AnonymousCoward' : true" %>
|
<span>
<%= link_to('Cancel', {:web => @web.address, :action => 'cancel_edit', :id => @page.name},
{:accesskey => 'c'}) %>
<small>(unlocks page)</small>
</span>
</div>
<%= end_form_tag %>
</div>
<script type="text/javascript">
function cleanAuthorName() {
if (document.getElementById('authorName').value == "") {
document.getElementById('authorName').value = 'AnonymousCoward';
}
}
document.forms["editForm"].elements["content"].focus();
</script>

View file

@ -0,0 +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><%= link_to 'HTML', :web => @web.address, :action => 'export_html' %></li>
<li><%= link_to "Markup (#{@web.markup.to_s.capitalize})", :web => @web.address, :action => 'export_markup' %></li>
<% if OPTIONS[:pdflatex] && @web.markup == :textile %>
<li><%= link_to 'TeX', :web => @web.address, :action => 'export_tex' %></li>
<li><%= link_to 'PDF', :web => @web.address, :action => 'export_pdf' %></li>
<% end %>
</ul>

View file

@ -0,0 +1,14 @@
<% @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>
<% if @rss_with_content_allowed %>
<%= link_to 'Full content (RSS 2.0)', :web => @web.address, :action => :rss_with_content %>
<% end %>
</li>
<li>
<%= link_to 'Headlines (RSS 2.0)', :web => @web.address, :action => :rss_with_headlines %>
</li>
</ul>

64
app/views/wiki/list.rhtml Normal file
View file

@ -0,0 +1,64 @@
<% @title = "All Pages" %>
<%= categories_menu unless @categories.empty? %>
<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>
<% @pages_in_category.each do |page| %>
<li>
<%= link_to_existing_page page, truncate(page.plain_name, 35) %>
</li>
<% end %></ul>
<% if @web.count_pages? %>
<% total_chars = @pages_in_category.characters %>
<p><small>All content: <%= total_chars %> chars / approx. <%= sprintf("%-.1f", (total_chars / 2275 )) %> printed 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">
<% @page_names_that_are_wanted.each do |wanted_page_name| %>
<li>
<%= link_to_page(wanted_page_name, @web, truncate(WikiWords.separate(wanted_page_name), 35)) %>
wanted by
<%= @web.select.pages_that_reference(wanted_page_name).collect { |referring_page|
link_to_existing_page referring_page
}.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">
<% @pages_that_are_orphaned.each do |orphan_page| %>
<li>
<%= link_to_existing_page orphan_page, truncate(orphan_page.plain_name, 35) %>
</li>
<% end %>
</ul>
<% end %>
</div>

View file

@ -0,0 +1,23 @@
<% @title = "#{@page.plain_name} is locked" %>
<p>
<%= link_to_page(@page.locked_by) %>
<% if @page.lock_duration(Time.now) == 0 %>
just started editing this page.
<% else %>
has been editing this page for <%= @page.lock_duration(Time.now) %> minutes.
<% end %>
</p>
<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>

View file

@ -0,0 +1,22 @@
<% @title = "#{@web_name} Login" %><% @hide_navigation = true %>
<p>
<%= form_tag({ :controller => 'wiki', :action => 'authenticate', :web => @web.address},
{ 'name' => 'loginForm', 'id' => 'loginForm', 'method' => 'post', 'accept-charset' => 'utf-8' }) %>
<p>
This web is password-protected. Please enter the password.
<% if @web.published? %>
If you don't have the password, you can view this wiki as a <%= link_to 'read-only version', :action => 'published', :id => 'HomePage' %>.
<% end %>
</p>
<p>
<b>Password: </b>
<input type="password" name="password" id="password" />
<input type="submit" value="Login" default="yes" />
</p>
<%= end_form_tag %>
</p>
<script type="text/javascript">
document.forms["loginForm"].elements["password"].focus();
</script>

33
app/views/wiki/new.rhtml Normal file
View file

@ -0,0 +1,33 @@
<%
@title = "Creating #{WikiWords.separate(@page_name)}"
@content_width = 720
@hide_navigation = true
%>
<div id="MarkupHelp">
<%= render("#{@web.markup}_help") %>
<%= render 'wiki_words_help' %>
</div>
<div id="editForm">
<%= form_tag({ :action => 'save', :web => @web.address, :id => @page_name },
{ 'id' => 'editForm', 'method' => 'post', 'onSubmit' => 'cleanAuthorName();', 'accept-charset' => 'utf-8' }) %>
<textarea name="content" id="content"><%= h(@flash[:content] || '') %></textarea>
<div id="editFormButtons">
<input type="submit" value="Submit" accesskey="s"/> as
<%= text_field_tag :author, @author,
:onfocus => "this.value == 'AnonymousCoward' ? this.value = '' : true;",
:onblur => "this.value == '' ? this.value = 'AnonymousCoward' : true" %>
</div>
<%= end_form_tag %>
</div>
<script type="text/javascript">
function cleanAuthorName() {
if (document.getElementById('authorName').value == "") {
document.getElementById('authorName').value = 'AnonymousCoward';
}
}
document.forms["editForm"].elements["content"].focus();
</script>

51
app/views/wiki/page.rhtml Normal file
View file

@ -0,0 +1,51 @@
<%
@title = @page.plain_name
@title += ' (changes)' if @show_diff
@show_footer = true
%>
<div id="revision">
<% if @show_diff %>
<p style="background: #eee; padding: 3px; border: 1px solid silver">
<small>
Showing changes from revision #<%= @page.revisions.size - 1 %> to #<%= @page.revisions.size %>:
<ins class="diffins">Added</ins> | <del class="diffdel">Removed</del>
</small>
</p>
<%= @renderer.display_diff %>
<% else %>
<%= @renderer.display_content %>
<% end %>
</div>
<div class="byline">
<%= @page.revisions? ? "Revised" : "Created" %> on <%= format_date(@page.revised_at) %>
by <%= author_link(@page) %>
<%= "(#{@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">
<%= navigation_menu_for_page.join(' | ') %>
<small>
| Views:
<%= link_to('Print',
{ :web => @web.address, :action => 'print', :id => @page.name },
{ :accesskey => 'p', :name => 'view_print' }) %>
<% if defined? RedClothForTex and RedClothForTex.available? and @web.markup == :textile %>
|
<%= link_to 'TeX', {:web => @web.address, :action => 'tex', :id => @page.name},
{:name => 'view_tex'} %>
|
<%= link_to 'PDF', {:web => @web.address, :action => 'pdf', :id => @page.name},
{:name => 'view_pdf'} %>
<% end %>
</small>
<%= render :partial => 'inbound_links' %>
</div>

View file

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

View file

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

View file

@ -0,0 +1,19 @@
<% @title = "Recently Revised" %>
<%= categories_menu %>
<% @pages_by_day.keys.sort.reverse.each do |day| %>
<h3><%= format_date(day, include_time = false) %></h3>
<ul>
<% for page in @pages_by_day[day] %>
<li>
<%= link_to_existing_page page %>
<div class="byline" style="margin-bottom: 0px">
by <%= link_to_page(page.author) %>
at <%= format_date(page.revised_at) %>
<%= "from #{page.author.ip}" if page.author.respond_to?(:ip) %>
</div>
</li>
<% end %>
</ul>
<% end %>

View file

@ -0,0 +1,28 @@
<%
@title = "#{@page.plain_name} (Rev ##{@revision_number}#{@show_diff ? ', changes' : ''})"
%>
<div id="revision">
<% if @show_diff %>
<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>
<%= @renderer.display_diff %>
<% else %>
<%= @renderer.display_content %>
<% end %>
</div>
<div class="byline">
<%= "Revision from #{format_date(@revision.revised_at)} by" %>
<%= link_to_page @revision.author %>
</div>
<div class="navigation">
<%= navigation_menu_for_revision.join(' | ') %>
<%= render :partial => 'inbound_links' %>
</div>

View file

@ -0,0 +1,39 @@
<%
@title = "Rollback to #{@page.plain_name} Rev ##{@revision_number}"
@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"] %>
<div id="MarkupHelp">
<%= render("#{@web.markup}_help") %>
<%= render 'wiki_words_help' %>
</div>
<div id="editForm">
<%= form_tag({:web => @web.address, :action => 'save', :id => @page.name},
{ :id => 'editForm', :method => 'post', :onSubmit => 'cleanAuthorName();',
'accept-charset' => 'utf-8' }) %>
<textarea name="content" id="content"><%= @revision.content %></textarea>
<div id="editFormButtons">
<input type="submit" value="Update" accesskey="u" /> as
<input type="text" name="author" id="authorName" value="<%= @author %>"
onClick="this.value == 'AnonymousCoward' ? this.value = '' : true" />
|
<span>
<%= link_to('Cancel', {:web => @web.address, :action => 'cancel_edit', :id => @page.name},
{:accesskey => 'c'}) %>
<small>(unlocks page)</small>
</span>
</div>
<%= end_form_tag %>
</div>
<script type="text/javascript">
function cleanAuthorName() {
if (document.getElementById('authorName').value == "") {
document.getElementById('authorName').value = 'AnonymousCoward';
}
}
</script>

View file

@ -0,0 +1,21 @@
xml.rss('version' => '2.0') do
xml.channel do
xml.title(@web.name)
xml.link(url_for(:only_path => false, :web => @web_name, :action => @link_action, :id => 'HomePage'))
xml.description('An Instiki wiki')
xml.language('en-us')
xml.ttl('40')
for page in @pages_by_revision
xml.item do
xml.title(page.plain_name)
unless @hide_description
xml.description(rendered_content(page))
end
xml.pubDate(page.revised_at.getgm.strftime('%a, %d %b %Y %H:%M:%S Z'))
xml.guid(url_for(:only_path => false, :web => @web_name, :action => @link_action, :id => page.name))
xml.link(url_for(:only_path => false, :web => @web_name, :action => @link_action, :id => page.name))
end
end
end
end

View file

@ -0,0 +1,38 @@
<% @title = "Search results for \"#{h @params["query"]}\"" %>
<% unless @title_results.empty? %>
<h2><%= @title_results.length %> page(s) containing search string in the page name:</h2>
<ul>
<% for page in @title_results %>
<li>
<%= link_to page.plain_name, :web => @web.address, :action => 'show', :id => page.name %>
</li>
<% end %>
</ul>
<% end %>
<% unless @results.empty? %>
<h2> <%= @results.length %> page(s) containing search string in the page text:</h2>
<ul>
<% for page in @results %>
<li>
<%= link_to page.plain_name, :web => @web.address, :action => 'show', :id => page.name %>
</li>
<% end %>
</ul>
<% end %>
<% if (@results + @title_results).empty? %>
<h2>No pages contain "<%= h @params["query"] %>" </h2>
<p>
Perhaps you should try expanding your query. Remember that Instiki searches for entire
phrases, so if you search for "all that jazz" it will not match pages that contain these
words in separation&mdash;only as a sentence fragment.
</p>
<p>
If you're a high-tech computer wizard, you might even want try constructing a Ruby regular
expression. That's actually what Instiki uses, so go right ahead and flex your
"[a-z]*Leet?RegExpSkill(s|z)"
</p>
<% end %>

23
app/views/wiki/tex.rhtml Normal file
View file

@ -0,0 +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 %>
\end{document}

View file

@ -0,0 +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 %>
\end{document}

View file

@ -0,0 +1,25 @@
<% @title = "Wiki webs" %>
<br/>
<% @webs.each do |web| %>
<% if web.password %> <div class="web_protected">
<% else %> <div class="web_normal"> <% end %>
<span>
<%= link_to_page 'HomePage', web, web.name, :mode => 'show' %>
<% if web.published? %>
(<%= link_to_page 'HomePage', web, 'published version', :mode => 'publish' %>)
<% end %>
<div class="byline" style="margin-bottom: 0px">
<%= web.pages.length %> page<% if web.pages.length != 1 %>s<% end %> by <%= web.authors.length %> author<% if web.authors.length != 1 %>s<% end %>
- Last Update: <%= web.last_page.nil? ? format_date(web.created_at) : format_date(web.last_page.revised_at) %><br/>
<% if ! web.last_page.nil? %>
Last Document: <%= link_to_page(web.last_page.name,web) %>
<%= web.last_page.revisions? ? "Revised" : "Created" %> by <%= author_link(web.last_page) %> (<%= web.last_page.current_revision.ip %>)
<% end %>
</div>
</span>
</div><br>
<% end %>
</ul>

View file

@ -0,0 +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>