require "cgi" require "redcloth_for_tex" class WikiController < ActionControllerServlet EXPORT_DIRECTORY = WikiService.storage_path unless const_defined?("EXPORT_DIRECTORY") def index if web_address redirect_show "HomePage" elsif !wiki.setup? redirect_path "/new_system/" elsif wiki.webs.length == 1 redirect_show "HomePage", wiki.webs.values.first.address else redirect_path "/web_list/" end end # Administrating the Instiki setup -------------------------------------------- def new_system wiki.setup? ? redirect_path("/") : render end def new_web redirect_path("/") if wiki.system["password"].nil? end def create_system wiki.setup(@params["password"], @params["web_name"], @params["web_address"]) unless wiki.setup? redirect_path "/" end def create_web redirect_path("/") unless wiki.authenticate(@params["system_password"]) wiki.create_web(@params["name"], @params["address"]) redirect_show("HomePage", @params["address"]) end # Outside a single web -------------------------------------------------------- def web_list @system, @webs = wiki.system, wiki.webs.values render "wiki/web_list" end def login render "wiki/login" end def authenticate password_check(@params["password"]) ? redirect_show("HomePage") : redirect_action("login") end def static_style_sheet() render "static_style_sheet" end # Within a single web --------------------------------------------------------- def parse_category @categories = web.categories @category = @params["category"] @pages_in_category = web.select { |page| page.in_category?(@category) } @pages_without_category = web.select { |page| page.categories.length == 0 } if @category == 'none' @pages_in_category = @pages_without_category end @set_name = ( @categories.include?(@category) ? "category '#{@category}'" : "the web" ) @category_links = @categories.map do |c| (@category == c ? "[#{c}]" : "#{c}") end end def search set_menu_pages @query = @params["query"] rex = /#{@query}/i @results = web.select { |page| rex.match(page.name) or rex.match(page.content) } @bliki_results = web.bliki.values.select do |entry| rex.match(entry.name) or rex.match(entry.content) end @results.length == 1 ? redirect_show(@results.first.name) : render end def authors @authors = web.select.authors end def recently_revised parse_category set_menu_pages @pages_by_revision = @pages_in_category.by_revision end def rss_with_content @pages_by_revision = web.select.by_revision.first(15) @uri = @req.request_uri host = @req.meta_vars["HTTP_X_FORWARDED_HOST"] || "#{@uri.host}:#{@uri.port.to_s}" @web_url = "#{@uri.scheme}://#{host}/#{@web.address}" @res["Content-Type"] = "text/xml" render "wiki/rss_feed" end def rss_with_headlines @hide_description = true rss_with_content end def list parse_category set_menu_pages @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 case @req.query['Action'] when 'Delete' # Handle page deletion wiki.delete_page(web_address, @req.query['sel_page_name']) redirect_action "list/" when 'Create' # Handle page creation redirect_show @req.query['newpage'] when 'Rename' # Handle page rename wiki.rename_page(web_address, @req.query['sel_page_name'], @req.query['newpage']) redirect_action "list/" end end def export_html file_name = "#{web.address}-html-#{web.revised_on.strftime("%Y-%m-%d-%H-%M")}.zip" file_path = EXPORT_DIRECTORY + file_name export_pages_to_zip_file(file_path) unless FileTest.exists?(file_path) send_export(file_name, file_path) end def export_markup file_name = "#{web.address}-markup-#{web.revised_on.strftime("%Y-%m-%d-%H-%M")}.zip" file_path = EXPORT_DIRECTORY + file_name export_markup_to_zip_file(file_path) unless FileTest.exists?(file_path) send_export(file_name, file_path) end def export_pdf file_name = "#{web.address}-tex-#{web.revised_on.strftime("%Y-%m-%d-%H-%M")}" file_path = EXPORT_DIRECTORY + file_name export_web_to_tex(file_path + ".tex") unless FileTest.exists?(file_path + ".tex") convert_tex_to_pdf(file_path + ".tex") send_export(file_name + ".pdf", file_path + ".pdf") end def export_tex file_name = "#{web.address}-tex-#{web.revised_on.strftime("%Y-%m-%d-%H-%M")}.tex" file_path = EXPORT_DIRECTORY + file_name export_web_to_tex(file_path) unless FileTest.exists?(file_path) send_export(file_name, file_path) end def update_web redirect_show("HomePage") unless 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['mind_map_size'], @params['symbols_map'], @params['links_map'] ) redirect_show("HomePage", @params["address"]) end def remove_orphaned_pages if wiki.authenticate(@params["system_password"]) wiki.remove_orphaned_pages(web_address) redirect_action "list/" else redirect_show "HomePage" end end def todo #{{{ parse_category set_menu_pages @pages_by_name = @pages_in_category.by_name @todo_items = Hash.new { Array.new } @bliki_todo_items = Hash.new { Array.new } @pages_by_name.each do |page| if page.content =~ Todo.pattern # Page has todo items. Get the rendered version (marked-up and with links): content = page.revisions.last.display_content @todo_items[page] = content.scan /TODO:<\/strong> (.*?)<\/span>/ end end @todo_items = @todo_items.sort_by { |page, items| page.name } web.bliki.each do |pname, entry| if entry.content =~ Todo.pattern # Entry has todo items. Get the rendered version (marked-up and with links): content = entry.revisions.last.display_content @bliki_todo_items[entry] = content.scan /TODO:<\/strong> (.*?)<\/span>/ end end @bliki_todo_items = @bliki_todo_items.sort_by { |entry, items| entry.name } end #}}} def set_menu_pages #{{{ parse_category @all_pages = web.select { true } @menu_pages = case web.menu_type when 'all' then @all_pages.by_name when 'recent' then @all_pages.by_last_visited when 'viewed' then @all_pages.by_most_viewed when 'revised' then @all_pages.by_revision when 'user' then @menu_content = web.rendered_menu; nil when 'category' then web.select { |page| page.in_category?(web.menu_category) } when 'linkers' web.select { |page| page.wiki_words.size > 0 }.sort_by { |page| page.name } end if web.menu_limit && @menu_pages @menu_pages = @menu_pages[0..web.menu_limit] end end #}}} def bliki #{{{ set_menu_pages @entries = web.bliki_entries_by_date unless @req.query['authorname'].nil? || @req.query['authorname'] == 'noselect' @entries = @entries.select { |page| page.authors.include?(@req.query['authorname']) } end unless @req.query['regexp'].nil? @entries = @entries.select { |page| page.content =~ /#{@req.query['regexp']}/i } end @color = web.color @authors = web.authors end #}}} require 'pp' def mind #{{{ parse_category set_menu_pages @prog = @req.query['draw_type'] || 'neato' @graph_type = @req.query['graph_type'] || 'normal' missing = @pages_in_category.wanted_pages if @req.query['missing'] show_authors = !@req.query.empty? && @req.query['show_authors'] == 'on' show_leaves = @req.query.empty? || @req.query['show_leaves'] == 'on' # TODO: fix handling of multiple-select for whole application @selected_categories = @req.body.split('&').map { |pair| pair.split('=') }.select { |k,v| k == 'selected_categs' }.map { |k,v| v } if @req.body @selected_categories ||= [] @selected_categories = [] if @selected_categories.include? 'all' @pngFile = @mapFile = nil case @graph_type when 'normal' then @pngFile, @mapFile = web.create_mind_map(@prog, missing, show_authors, show_leaves, @selected_categories) when 'author' then @pngFile, @mapFile = web.create_author_graph(@prog, @selected_categories) when 'category' then @pngFile, @mapFile = web.create_category_graph(@prog, show_authors, @selected_categories) end end #}}} def edit_menu #{{{ @menu_type = web.menu_type @menu_content = web.menu_content @list_limit = web.menu_limit @list_limit += 1 if @list_limit >= -1 end #}}} def save_menu #{{{ redirect_show("HomePage") unless wiki.authenticate(@params["system_password"]) unless @req.query['action'] == 'Cancel Update' type = @req.query['type'] content = @req.query['content'] category = @req.query['category'] limit = @req.query['limit'].to_i rescue nil limit = 20 unless limit limit -= 1 if limit >= 0 # need to go through the WikiService to persist the command: wiki.save_menu_pref web_address, type, limit, content, category end if web_address # redirect to the most recently viewed page, or the home page. pname = begin web.select{ true }.by_last_visited.first.name rescue "HomePage" end redirect_show pname elsif wiki.webs.length == 1 # only one web, so go there. redirect_show "HomePage", wiki.webs.values.first.address else redirect_path "/web_list/" end end #}}} def get_map_img file_name = "map.png" file_path = File.join WikiService.storage_path, file_name send_export(file_name, file_path, "image/png") end # Within a single page -------------------------------------------------------- def show set_menu_pages if @page = wiki.read_page(web_address, page_name) unless page_name == 'HomePage' # HomePage should not be in the menu as there's a link at the top. @page.last_visited = Time.now @page.viewed += 1 end begin render_action "page" rescue => e $stderr << e.backtrace.join("\n") redirect_action "edit/#{CGI.escape(page_name)}?msg=#{CGI.escape(e.message)}" end else if page_name redirect_action "new/#{CGI.escape(page_name)}" else redirect_show "HomePage" end end end def published if web.published then @page = wiki.read_page(web_address, page_name || "HomePage") else redirect_show("HomePage") end end def print @page = wiki.read_page(web_address, page_name) end def tex @page = wiki.read_page(web_address, page_name) @tex_content = RedClothForTex.new(@page.content).to_tex end def pdf page = wiki.read_page(web_address, 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")}" file_path = EXPORT_DIRECTORY + file_name export_page_to_tex(file_path + ".tex") unless FileTest.exists?(file_path + ".tex") convert_tex_to_pdf(file_path + ".tex") send_export(file_name + ".pdf", file_path + ".pdf") end def new @page_name, @author = page_name, default_author end def edit @page = wiki.read_page(web_address, page_name) if !@page.locked?(Time.now) || @params["break_lock"] @page.lock(Time.now, default_author) @author = default_author render else render "wiki/locked" end end def cancel_edit @page = wiki.read_page(web_address, page_name) @page.unlock redirect_show end def save if web.pages[page_name] page = wiki.revise_page( web_address, page_name, @params["content"], Time.now, Author.new(@params["author"], remote_ip) ) page.unlock else page = wiki.write_page( web_address, page_name, @params["content"], Time.now, Author.new(@params["author"], remote_ip) ) end write_cookie("author", @params["author"], true) redirect_show(page_name) end def revision @page = wiki.read_page(web_address, page_name) @revision = @page.revisions[@params["rev"].to_i] end def rollback @page = wiki.read_page(web_address, page_name) wiki.rollback_page(web_address, page_name, @params["rev"].to_i, Time.now, remote_ip) redirect_show end # Bliki ---------------------------------------------------------------------- def bliki_delete wiki.delete_bliki_entry(web_address, page_name) redirect_bliki end def bliki_edit @page = wiki.read_bliki_entry(web_address, page_name) if !@page.locked?(Time.now) || @params["break_lock"] @page.lock(Time.now, default_author) @author = default_author render else redirect_path "#{web_address}/locked" end end def cancel_bliki_edit @page = wiki.read_bliki_entry(web_address, page_name) @page.unlock if @page redirect_bliki end def bliki_save pname = page_name || @params["pagename"] if web.bliki[pname] page = wiki.revise_bliki_entry(web_address, pname, @params["content"], Time.now, @params["author"]) page.unlock else page = wiki.write_bliki_entry(web_address, pname, @params["content"], Time.now, @params["author"]) end write_cookie("author", @params["author"]) redirect_bliki end def bliki_revision @page = wiki.read_bliki_entry(web_address, page_name || @params['pagename']) @revision = @page.revisions[@params["rev"].to_i] || @page.revisions.last end def rollback_bliki @page = wiki.read_bliki_entry(web_address, page_name) wiki.rollback_bliki_entry(web_address, page_name, @params["rev"].to_i, Time.now) redirect_bliki end # ---------------------------------------------------------------------------- protected def before_action if in_a_web? && !authorized?(web_address) && !%w( login authenticate published ).include?(action_name) redirect_action("login") return false elsif in_a_web? @web, @page_name, @action_name = web, page_name, action_name end end def action_name if in_a_web? request_path[1] elsif action_methods.include?(request_path[0]) request_path[0] else "index" end end def redirect_show(page = @page.name, web = web_address) redirect_path "/#{web}/show/#{CGI.escape(page)}" end def redirect_bliki redirect_path "/#{web_address}/bliki/" end def redirect_action(action, web = web_address) redirect_path "/#{web}/#{action}" end def link_to_bliki(entry, web = web_address) #{{{ "#{entry.name}" end #}}} private def wiki WikiService.instance end def web wiki.webs[web_address] end def in_a_web? request_path.length > 1 end def web_address request_path[0] end def page_name CGI.unescape(request_path[2]) if request_path[2] end def authorized?(web_address) (web && web.password.nil?) || (read_cookie(web_address) && read_cookie(web_address) == web.password) || password_check(@params["password"]) end def default_author read_cookie("author") || "AnonymousCoward" end def password_check(password) web && @params["password"] == web.password && write_cookie(web_address, @params["password"]) end def export_pages_to_zip_file(zip_file_path) Zip::ZipOutputStream.open(zip_file_path) do |zos| web.select.by_name.each do |@page| zos.put_next_entry(@page.name + ".html") zos.puts(template_engine("print").result(binding)) end zos.put_next_entry("index.html") zos.puts('') end end def export_markup_to_zip_file(zip_file_path) Zip::ZipOutputStream.open(zip_file_path) do |zos| web.select.by_name.each do |page| zos.put_next_entry(page.name + ".#{web.markup}") zos.puts(page.content) end end end def export_web_to_tex(file_path) @web_name = web.name @tex_content = table_of_contents(web.pages["HomePage"].content.dup, render_tex_web) File.open(file_path, "w") { |f| f.write(template_engine("tex_web").result(binding)) } 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 export_page_to_tex(file_path) tex File.open(file_path, "w") { |f| f.write(template_engine("tex").result(binding)) } end def convert_tex_to_pdf(tex_path) `cd #{File.dirname(tex_path)}; pdflatex --interaction=scrollmode '#{File.basename(tex_path)}'` end def truncate(text, length = 30, truncate_string = "...") if text.length > length then text[0..(length - 3)] + truncate_string else text end end def render_markup_help sub_template "#{web.markup}_help" end def send_export(file_name, file_path, content_type = "application/zip") @res["Content-Type"] = content_type @res["Content-Disposition"] = "attachment; filename=#{file_name}" @res["Content-Length"] = File.size(file_path) File.open(file_path, "rb") { |f| @res.body = f.read } end def template_engine(template_name) ERB.new(IO.readlines(action_template_path(template_name)).join) end def remote_ip $stderr << "#{@req.meta_vars['HTTP_X_FORWARDED_FOR']} || #{@req.meta_vars['REMOTE_ADDR']}" @req.meta_vars["HTTP_X_FORWARDED_FOR"] || @req.meta_vars["REMOTE_ADDR"] end end # jEdit :folding=indent:collapseFolds=2: