app/controllers/wiki.rb in Pimki-1.7.092 vs app/controllers/wiki.rb in Pimki-1.8.092

- old
+ new

@@ -1,943 +1,951 @@ -require "cgi" -require "redcloth_for_tex" - -RenderedTodo = Struct.new( :text, :context, :due_date ) - -class WikiController < ActionControllerServlet - EXPORT_DIRECTORY = WikiService.storage_path - - def index - if web_address - check_external_req_and_redirect web - elsif !wiki.setup? - redirect_path "/new_system/" - elsif wiki.webs.length == 1 - check_external_req_and_redirect wiki.webs.values.first - else - wiki.default_web ? - check_external_req_and_redirect(wiki.webs[wiki.default_web]) : - redirect_path("/web_list/") - end - end - - def check_external_req_and_redirect web - if web.default_to_published and @req.addr[2] != @req.peeraddr[2] - redirect_action("published/", web.address) - else - redirect_show("HomePage", web.address) - 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 - - def stop - redirect_show("HomePage") unless wiki.authenticate(@params["system_password"]) - - begin - secs = @params['seconds'].to_i - raise if secs.zero? - @logger.warn "Pimki server will stop in #{secs} seconds!" - WikiService.request_stop - render_text "Pimki server will stop in #{secs} seconds!" - Thread.new { sleep secs; Kernel.exit! } - rescue - redirect_show("HomePage") - end - 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 ? "<span class=\"selected\">[#{c}]</span>" : "<a href=\"?category=#{c}\">#{c}</a>") - end - end - - def search - set_menu_pages - @query = @params["query"] - search_content = [nil, 'both', 'contents'].include? @params['fields'] - search_names = [nil, 'both', 'names'].include? @params['fields'] - - case @params['expression'] - when 'regex', nil - rex = Regexp.new @query, (Regexp::IGNORECASE unless @params['case']) - @results = if [nil, 'all', 'pages', nil].include? @params['where'] - web.select do |page| - (search_names and rex.match(page.name)) or (search_content and rex.match(page.content)) - end - else - [] - end - @bliki_results = if [nil, 'all', 'bliki'].include? @params['where'] - web.bliki.values.select do |entry| - (search_names and rex.match(entry.name)) or (search_content and rex.match(entry.content)) - end - else - [] - end - - when 'all' - words = @query.split(/\s/).reject { |w| w.empty? } - @results = if [nil, 'all', 'pages'].include? @params['where'] - web.select do |page| - words.all? do |word| - (search_names and page.name.index(word)) or (search_content and page.content.index(word)) - end - end - else - [] - end - @bliki_results = if [nil, 'all', 'bliki'].include? @params['where'] - web.bliki.values.select do |entry| - words.all? do |word| - (search_names and entry.name.index(word)) or (search_content and entry.content.index(word)) - end - end - else - [] - end - - when 'exact' - @results = if [nil, 'all', 'pages'].include? @params['where'] - web.select do |page| - (search_names and page.name.index(@query)) or (search_content and page.content.index(@query)) - end - else - [] - end - @bliki_results = if [nil, 'all', 'bliki'].include? @params['where'] - web.bliki.values.select do |entry| - (search_names and entry.name.index(@query)) or (search_content and entry.content.index(@query)) - end - else - [] - end - end - - if !@params['category'].nil? and @params['category'] != 'noselect' - @selected_categories = parse_multi_select 'category' - @results.reject! { |page| (page.categories & @selected_categories).empty? } - @bliki_results.reject! { |page| (page.categories & @selected_categories).empty? } - end - - if !@params['author'].nil? and @params['author'] != 'noselect' - @selected_authors = parse_multi_select 'author' - @results.reject! { |page| (page.authors & @selected_authors).empty? } - @bliki_results.reject! { |page| (page.authors & @selected_authors).empty? } - end - - redirect_show(@results.first.name) if @results.length == 1 && @bliki_results.length == 0 - redirect_path("/#{web_address}/bliki_revision/#{@bliki_results.first.name}?rev=#{@bliki_results.first.revisions.size-1}") if @results.length == 0 && @bliki_results.length == 1 - render_action "search" - end - - def glossary - set_menu_pages - - rex = %r{([A-Z\d]+)\(([\w\s]+)\)} - scan_results = web.select.map { |page| [page.link, page.content.scan(rex)] } - scan_results += web.bliki.values.map { |entry| [link_to_bliki(entry), entry.content.scan(rex)] } - results = Hash.new { Array.new } - scan_results.each { |page, acronyms| acronyms.each { |ac| results[ac] += [page] } } - @results = results.map{ |(ac, df), pg|[[ac,df], pg.uniq] }.sort_by{ |(ac, df), pg| ac } - - acronyms = @results.map { |(ac, df), pg| ac } - rex = %r{(#{acronyms.join('|')})[^\(]} - results = web.select.map { |page| [page.link, page.content.scan(rex)] } - results += web.bliki.values.map { |entry| [link_to_bliki(entry), entry.content.scan(rex)] } - @undefined_on = Hash.new { Array.new } - results.each { |page, acronyms| acronyms.each { |ac| @undefined_on[ac[0]] += [page] } } - @undefined_on = @undefined_on.inject({}) { |hsh, (k, v)| hsh[k] = v.uniq; hsh } - end - - def authors - set_menu_pages - @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 = @rss_bliki_only ? [] : web.select.by_revision.first(15) - @bliki_entries = web.bliki_entries_by_date - @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 rss_bliki_only - @rss_bliki_only = true - rss_with_content - end - - def rss_todo_items - todo - @display_todo = true - rss_with_content - end - - def feeds - set_menu_pages - 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 - - if @req.query['Action'] - # redirect_action 'list/' if web.check_pass_on_edit and not password_check(@params['password']) - - case @req.query['Action'] - when 'Delete' # Handle page deletion - wiki.delete_page(web_address, @req.query['del_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['ren_sel_page_name'], @req.query['newpage']) - redirect_action "list/" - end - end - - end - - def export - set_menu_pages - end - - def export_html - file_name = "#{web.address}-html-#{web.revised_on.strftime("%Y-%m-%d-%H-%M")}.zip" - file_path = File.join 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 = File.join 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 = File.join 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 = File.join EXPORT_DIRECTORY, file_name - - export_web_to_tex(file_path) unless FileTest.exists?(file_path) - send_export(file_name, file_path) - end - - def edit_web #{{{ - parse_category - set_mm_options - @snapshot_interval = WikiService.snapshot_interval_hours - end #}}} - - def update_web - redirect_show("HomePage") unless wiki.authenticate(@params["system_password"]) - - set_mm_options - - 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['default_to_published'] ? true : false, - @params["brackets_only"] ? true : false, - @params["count_pages"] ? true : false, - @params['mind_map_size'], - @params['symbols_map'], - @params['links_map'], - @params['snapshots_interval'], - @params['enable_dclick_edit'], - @params['enable_menu'], - @params['check_pass_on_edit'] == 'each_edit', - @prog, @graph_type, @missing, @show_authors, @show_leaves, @selected_categories - ) - - redirect_show("HomePage", @params["address"]) - end - - def administrate - @logger.info "Taking administrative action: #{@params['action']}" - redirect_show 'HomePage' unless wiki.authenticate(@params['system_password']) - case @params['action'] - when 'Delete Orphan Pages' - wiki.remove_orphaned_pages(web_address) - - when 'Clear Render Cache' - clear_render_cache true - - when 'Force Data Snapshot' - WikiService.take_snapshot - - when 'Clean Storage' - WikiService.clean_old_snapshots - - when 'Make This Web Default' - wiki.default_web = web_address - - when 'Remove This Web' - wiki.webs.delete web_address - redirect_path '/' - - end - @message = 'Operation succeeded' - redirect_action 'edit_web/' - end - - FAR_FUTURE = Date.new(4000,1,1).freeze - TODO_RE = %r{<todo-tag context='(.*?)' due_date='(.*?)'><span class="todo"><strong>TODO(?:[^:]*)?:</strong> (.*?)</span></todo-tag>} - - def todo #{{{ - parse_category - set_menu_pages - - @context = @params['context'] - @sort_order = @params['sort_order'] - - # the clear_render_cache hack to make sure we don't have old-style rendered - # todos is no longer needed - can go thru the edit web to explicitely clear the cache. - @todo_items = analyse_rendered_todo_items @pages_in_category.by_name - @bliki_todo_items = analyse_rendered_todo_items web.bliki.values - - @context_links = @todo_items.clone.update(@bliki_todo_items).map { |page, items| - # 'items' contain the full 'todo' info - items.map { |item| item.context } - }.flatten.compact.uniq.reject { |c| c.nil? || c.empty? }.map { |context| - @context == context ? - "[<span class='selected'>#{context}</span>]" : - "<a href='?context=#{context}#{"&sort_order=#{@sort_order}" if @sort_order}'>#{context}</a>" - } - - @todo_items = sort_and_filter_todo_items @todo_items, @sort_order, @context - @bliki_todo_items = sort_and_filter_todo_items @bliki_todo_items, @sort_order, @context - - end #}}} - - def analyse_rendered_todo_items pages - items = Hash.new { Array.new } - pages.each do |page| - if page.has_todos? - # Page has todo items. Get the rendered version (marked-up and with links): - # I specifically don't use the todo chunkss because I want the fully marked-up - # text of the item. - content = page.revisions.last.display_content - items[page] = content.scan(TODO_RE).map { |match| - RenderedTodo.new match[2], (match[0].empty? ? [] : match[0].split(',') ), (Date.parse(match[1]) rescue FAR_FUTURE) - } - end - end - items - end - - def sort_and_filter_todo_items items, sort_order, context - case sort_order - when 'due_date' - result = items.map { |page, todos| - # sort the to items themselves - [page, todos.sort_by { |i| i.due_date } ] - }.sort_by { |page, todos| - # sort the pages by the one with the most urgent todo - todos.map{ |i| i.due_date }.min - } - - else # default = sort by page name - result = items.sort_by { |page, items| page.name } - - end - - if context - # filter the items to those that are in context - result = result.map { |page, items| - [ page, items.select { |item| item.context.include? context } ] - }.select { |page, items| not items.empty? } - end - result - end - - def get_todo_display_style due_date - # default is the muted 'darkred', to prevent to many bright red - # items on one page: (See also chunks/todo.rb) - (due_date <=> Date.today) > -1 ? "todoFuture" : "todo" - end - - def clear_render_cache dont_redirect=false - web.refresh_revisions - redirect_path "/#{web_address}/edit_web/" unless dont_redirect - end - - def set_menu_pages published = false #{{{ - 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 'category' then web.select { |page| page.in_category?(web.menu_category) } - when 'user' - @menu_content = if Page === web.menu_content - web.menu_content.revisions.last.display_content - else - web.menu_content - end - @menu_content = @menu_content.gsub('/show/', '/published/').gsub( %r{<a href="../published/.*?">\?</a>}, '') if published - nil - 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 #}}} - - def mind #{{{ - parse_category - set_menu_pages - set_mm_options - - @pngFile = @mapFile = nil - case @graph_type - when 'normal' - @pngFile, @mapFile = web.create_mind_map(@prog, @missing, - @show_authors, @show_leaves, @selected_categories, @mm_size) - - when 'author' - @pngFile, @mapFile = web.create_author_graph(@prog, @selected_categories, @mm_size) - - when 'category' - @pngFile, @mapFile = web.create_category_graph(@prog, - @show_authors, @selected_categories, @mm_size) - end - end #}}} - - def set_mm_options #{{{ - if @req.query.empty? - @prog = web.mm_prog - @graph_type = web.mm_graph_type - @missing = @pages_in_category.wanted_pages if web.mm_show_missing - @show_authors = web.mm_show_authors - @show_leaves = web.mm_show_leaves - @selected_categories = web.mm_selected_categories - @mm_size = web.mind_map_size - else - @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['show_authors'] == 'on' - @show_leaves = @req.query.empty? || @req.query['show_leaves'] == 'on' - - @selected_categories = parse_multi_select 'selected_categs' - @selected_categories = [] if @selected_categories.include? 'all' - @mm_size = @params['mind_map_size'] || web.mind_map_size - end - end #}}} - - def parse_multi_select field #{{{ - if @req.body - @req.body.split('&').map { |pair| - pair.split('=') }.select { |k,v| - k == field }.map { |k,v| v } - else - [] - 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 - @author = default_author - 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'] - author = @req.query['author'] - - 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, Author.new(author, remote_ip)) - end - - redirect_action 'edit_web/' # go back to web options page - 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 - - def adv_search - parse_category - set_menu_pages - 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 - set_menu_pages true - 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 = File.join 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 - # redirect_show("HomePage") if web.check_pass_on_edit and not password_check(@params['password']) - @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.check_pass_on_edit and not password_check(@params['password']) - # wiki.read_page(web_address, page_name).unlock if web.pages[page_name] - # redirect_show("HomePage") - # end - - if web.pages[page_name] - page = wiki.revise_page( - web_address, page_name, @params["content"], Time.now, - Author.new(@params["author"], remote_ip), @params['edit_type'] - ) - - 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 - redirect_show 'HomePage' if page_name.nil? - set_menu_pages - @page = wiki.read_page(web_address, page_name) - @revision = @page.revisions[@params["rev"].to_i] - end - - def rollback - # redirect_show("HomePage") if web.check_pass_on_edit and not password_check(@params['password']) - - @page = wiki.read_page(web_address, page_name) - @revision = @page.revisions[@params["rev"].to_i] - end - - # Bliki ---------------------------------------------------------------------- - - def bliki_delete - # redirect_bliki if web.check_pass_on_edit and not password_check(@params['password']) - - wiki.delete_bliki_entry(web_address, page_name) - redirect_bliki - end - - def bliki_edit - # redirect_bliki if web.check_pass_on_edit and not password_check(@params['password']) - - @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 - @bliki_entry = true - render "wiki/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_new - @entry_name = @params['entry_name'] - @author = default_author - end - - def bliki_save - # redirect_bliki if web.check_pass_on_edit and not password_check(@params['password']) - - 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 - parse_category - set_menu_pages - @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 - # redirect_bliki if web.check_pass_on_edit and not password_check(@params['password']) - - @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) #{{{ - "<a class='existingWikiWord' href='/#{web}/bliki_revision/#{entry.name}?rev=#{entry.revisions.size-1}'>#{entry.name}</a>" - 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.nil? || - 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 "pages-metadata.txt" - web.select.by_name.each do |page| - zos.puts "#{page.name}: by #{page.author}, created on #{page.pretty_created_at}" - end - - web.select_bliki.by_name.each do |@page| - zos.put_next_entry("bliki/#{@page.name}.html") - zos.puts(template_engine("print").result(binding)) - end - - zos.put_next_entry "bliki/bliki-metadata.txt" - web.select_bliki.by_name.each do |page| - zos.puts "#{page.name}: by #{page.author}, created on #{page.revisions.first.pretty_created_at}" - end - - zos.put_next_entry("index.html") - zos.puts('<html><head><META HTTP-EQUIV="Refresh" CONTENT="0;URL=HomePage.html"></head></html>') - 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 - - zos.put_next_entry "pages-metadata.txt" - web.select.by_name.each do |page| - zos.puts "#{page.name}: by #{page.author}, created on #{page.pretty_created_at}" - end - - web.select_bliki.by_name.each do |page| - zos.put_next_entry("bliki/#{page.name}.#{web.markup}") - zos.puts(page.content) - end - - zos.put_next_entry "bliki/bliki-metadata.txt" - web.select_bliki.by_name.each do |page| - zos.puts "#{page.name}: by #{page.author}, created on #{page.revisions.first.pretty_created_at}" - 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 - pages = web.select.by_name.inject({}) do |tex_web, page| - tex_web[page.name] = RedClothForTex.new(page.content).to_tex - tex_web - end - - bliki_entries = web.select_bliki.by_name.inject({}) do |tex_web, page| - tex_web["bliki/#{page.name}"] = RedClothForTex.new(page.content).to_tex - tex_web - end - - pages.update bliki_entries - 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 - if web - markup = web.markup - markup = 'markdown' if markup.to_s =~ /markdown/ - sub_template("#{markup}_help") - else - '' - end - 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 - +require "cgi" +require "redcloth_for_tex" + +RenderedTodo = Struct.new( :text, :context, :due_date ) + +class WikiController < ActionControllerServlet + EXPORT_DIRECTORY = WikiService.storage_path + + def index + if web_address + check_external_req_and_redirect web + elsif !wiki.setup? + redirect_path "/new_system/" + elsif wiki.webs.length == 1 + check_external_req_and_redirect wiki.webs.values.first + else + wiki.default_web ? + check_external_req_and_redirect(wiki.webs[wiki.default_web]) : + redirect_path("/web_list/") + end + end + + def check_external_req_and_redirect web + if web.default_to_published and @req.addr[2] != @req.peeraddr[2] + redirect_action("published/", web.address) + else + redirect_show("HomePage", web.address) + 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 + + def stop + redirect_show("HomePage") unless wiki.authenticate(@params["system_password"]) + + begin + secs = @params['seconds'].to_i + raise if secs.zero? + @logger.warn "Pimki server will stop in #{secs} seconds!" + WikiService.request_stop + render_text "Pimki server will stop in #{secs} seconds!" + Thread.new { sleep secs; Kernel.exit! } + rescue + redirect_show("HomePage") + end + 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 ? "<span class=\"selected\">[#{c}]</span>" : "<a href=\"?category=#{c}\">#{c}</a>") + end + end + + def search + set_menu_pages + @query = @params["query"] + search_content = [nil, 'both', 'contents'].include? @params['fields'] + search_names = [nil, 'both', 'names'].include? @params['fields'] + + case @params['expression'] + when 'regex', nil + rex = Regexp.new @query, (Regexp::IGNORECASE unless @params['case']) + @results = if [nil, 'all', 'pages', nil].include? @params['where'] + web.select do |page| + (search_names and rex.match(page.name)) or (search_content and rex.match(page.content)) + end + else + [] + end + @bliki_results = if [nil, 'all', 'bliki'].include? @params['where'] + web.bliki.values.select do |entry| + (search_names and rex.match(entry.name)) or (search_content and rex.match(entry.content)) + end + else + [] + end + + when 'all' + words = @query.split(/\s/).reject { |w| w.empty? } + @results = if [nil, 'all', 'pages'].include? @params['where'] + web.select do |page| + words.all? do |word| + (search_names and page.name.index(word)) or (search_content and page.content.index(word)) + end + end + else + [] + end + @bliki_results = if [nil, 'all', 'bliki'].include? @params['where'] + web.bliki.values.select do |entry| + words.all? do |word| + (search_names and entry.name.index(word)) or (search_content and entry.content.index(word)) + end + end + else + [] + end + + when 'exact' + @results = if [nil, 'all', 'pages'].include? @params['where'] + web.select do |page| + (search_names and page.name.index(@query)) or (search_content and page.content.index(@query)) + end + else + [] + end + @bliki_results = if [nil, 'all', 'bliki'].include? @params['where'] + web.bliki.values.select do |entry| + (search_names and entry.name.index(@query)) or (search_content and entry.content.index(@query)) + end + else + [] + end + end + + if !@params['category'].nil? and @params['category'] != 'noselect' + @selected_categories = parse_multi_select 'category' + @results.reject! { |page| (page.categories & @selected_categories).empty? } + @bliki_results.reject! { |page| (page.categories & @selected_categories).empty? } + end + + if !@params['author'].nil? and @params['author'] != 'noselect' + @selected_authors = parse_multi_select 'author' + @results.reject! { |page| (page.authors & @selected_authors).empty? } + @bliki_results.reject! { |page| (page.authors & @selected_authors).empty? } + end + + redirect_show(@results.first.name) if @results.length == 1 && @bliki_results.length == 0 + redirect_path("/#{web_address}/bliki_revision/#{@bliki_results.first.name}?rev=#{@bliki_results.first.revisions.size-1}") if @results.length == 0 && @bliki_results.length == 1 + render_action "search" + end + + def glossary + set_menu_pages + + rex = %r{([A-Z\d]+)\(([\w\s]+)\)} + scan_results = web.select.map { |page| [page.link, page.content.scan(rex)] } + scan_results += web.bliki.values.map { |entry| [link_to_bliki(entry), entry.content.scan(rex)] } + results = Hash.new { Array.new } + scan_results.each { |page, acronyms| acronyms.each { |ac| results[ac] += [page] } } + @results = results.map{ |(ac, df), pg|[[ac,df], pg.uniq] }.sort_by{ |(ac, df), pg| ac } + + acronyms = @results.map { |(ac, df), pg| ac } + rex = %r{(#{acronyms.join('|')})[^\(]} + results = web.select.map { |page| [page.link, page.content.scan(rex)] } + results += web.bliki.values.map { |entry| [link_to_bliki(entry), entry.content.scan(rex)] } + @undefined_on = Hash.new { Array.new } + results.each { |page, acronyms| acronyms.each { |ac| @undefined_on[ac[0]] += [page] } } + @undefined_on = @undefined_on.inject({}) { |hsh, (k, v)| hsh[k] = v.uniq; hsh } + end + + def authors + set_menu_pages + @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 = @rss_bliki_only ? [] : web.select.by_revision.first(15) + @bliki_entries = web.bliki_entries_by_date + @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 rss_bliki_only + @rss_bliki_only = true + rss_with_content + end + + def rss_todo_items + todo + @display_todo = true + rss_with_content + end + + def feeds + set_menu_pages + 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 + + if @req.query['Action'] + # redirect_action 'list/' if web.check_pass_on_edit and not password_check(@params['password']) + + case @req.query['Action'] + when 'Delete' # Handle page deletion + wiki.delete_page(web_address, @req.query['del_sel_page_name']) + clear_render_cache true + 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['ren_sel_page_name'], @req.query['ren_newpage']) + clear_render_cache true + redirect_action "list/" + end + end + + end + + def export + set_menu_pages + end + + def export_html + file_name = "#{web.address}-html-#{web.revised_on.strftime("%Y-%m-%d-%H-%M")}.zip" + file_path = File.join 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 = File.join 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 = File.join 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 = File.join EXPORT_DIRECTORY, file_name + + export_web_to_tex(file_path) unless FileTest.exists?(file_path) + send_export(file_name, file_path) + end + + def edit_web #{{{ + parse_category + set_mm_options + @snapshot_interval = WikiService.snapshot_interval_hours + end #}}} + + def update_web + redirect_show("HomePage") unless wiki.authenticate(@params["system_password"]) + + parse_category + set_mm_options + + 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['default_to_published'] ? true : false, + @params["brackets_only"] ? true : false, + @params["count_pages"] ? true : false, + @params['mind_map_size'], + @params['symbols_map'], + @params['links_map'], + @params['snapshots_interval'], + @params['enable_dclick_edit'] ? true : false, + @params['enable_menu'] ? true : false, + @params['check_pass_on_edit'] == 'each_edit', + @prog, @graph_type, @missing, @show_authors, @show_leaves, @selected_categories + ) + + redirect_show("HomePage", @params["address"]) + end + + def administrate + @logger.info "Taking administrative action: #{@params['action']}" + redirect_show 'HomePage' unless wiki.authenticate(@params['system_password']) + case @params['action'] + when 'Delete Orphan Pages' + wiki.remove_orphaned_pages(web_address) + + when 'Clear Render Cache' + clear_render_cache true + + when 'Force Data Snapshot' + WikiService.take_snapshot + + when 'Clean Storage' + WikiService.clean_old_snapshots + + when 'Make This Web Default' + wiki.default_web = web_address + + when 'Remove This Web' + wiki.webs.delete web_address + redirect_path '/' + + end + @message = 'Operation succeeded' + redirect_action 'edit_web/' + end + + FAR_FUTURE = Date.new(4000,1,1).freeze + TODO_RE = %r{<todo-tag context='(.*?)' due_date='(.*?)'><span class="todo"><strong>TODO(?:[^:]*)?:</strong> (.*?)</span></todo-tag>} + + def todo #{{{ + parse_category + set_menu_pages + + @context = @params['context'] + @sort_order = @params['sort_order'] + + # the clear_render_cache hack to make sure we don't have old-style rendered + # todos is no longer needed - can go thru the edit web to explicitely clear the cache. + @todo_items = analyse_rendered_todo_items @pages_in_category.by_name + @bliki_todo_items = analyse_rendered_todo_items web.bliki.values + + @context_links = @todo_items.clone.update(@bliki_todo_items).map { |page, items| + # 'items' contain the full 'todo' info + items.map { |item| item.context } + }.flatten.compact.uniq.reject { |c| c.nil? || c.empty? }.map { |context| + @context == context ? + "[<span class='selected'>#{context}</span>]" : + "<a href='?context=#{context}#{"&sort_order=#{@sort_order}" if @sort_order}'>#{context}</a>" + } + + @todo_items = sort_and_filter_todo_items @todo_items, @sort_order, @context + @bliki_todo_items = sort_and_filter_todo_items @bliki_todo_items, @sort_order, @context + + end #}}} + + def analyse_rendered_todo_items pages + items = Hash.new { Array.new } + pages.each do |page| + if page.has_todos? + # Page has todo items. Get the rendered version (marked-up and with links): + # I specifically don't use the todo chunkss because I want the fully marked-up + # text of the item. + content = page.revisions.last.display_content + items[page] = content.scan(TODO_RE).map { |match| + RenderedTodo.new match[2], (match[0].empty? ? [] : match[0].split(',') ), (Date.parse(match[1]) rescue FAR_FUTURE) + } + end + end + items + end + + def sort_and_filter_todo_items items, sort_order, context + case sort_order + when 'due_date' + result = items.map { |page, todos| + # sort the to items themselves + [page, todos.sort_by { |i| i.due_date } ] + }.sort_by { |page, todos| + # sort the pages by the one with the most urgent todo + todos.map{ |i| i.due_date }.min + } + + else # default = sort by page name + result = items.sort_by { |page, items| page.name } + + end + + if context + # filter the items to those that are in context + result = result.map { |page, items| + [ page, items.select { |item| item.context.include? context } ] + }.select { |page, items| not items.empty? } + end + result + end + + def get_todo_display_style due_date + # default is the muted 'darkred', to prevent to many bright red + # items on one page: (See also chunks/todo.rb) + (due_date <=> Date.today) > -1 ? "todoFuture" : "todo" + end + + def clear_render_cache dont_redirect=false + web.refresh_revisions + redirect_path "/#{web_address}/edit_web/" unless dont_redirect + end + + def set_menu_pages published = false #{{{ + 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 'category' then web.select { |page| page.in_category?(web.menu_category) } + when 'user' + @menu_content = if Page === web.menu_content + web.menu_content.revisions.last.display_content + else + web.menu_content + end + @menu_content = @menu_content.gsub('/show/', '/published/').gsub( %r{<a href="../published/.*?">\?</a>}, '') if published + nil + 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 #}}} + + def mind #{{{ + parse_category + set_menu_pages + set_mm_options + + @pngFile = @mapFile = nil + case @graph_type + when 'normal' + @pngFile, @mapFile = web.create_mind_map(@prog, @missing, + @show_authors, @show_leaves, @selected_categories, @mm_size) + + when 'author' + @pngFile, @mapFile = web.create_author_graph(@prog, @selected_categories, @mm_size) + + when 'category' + @pngFile, @mapFile = web.create_category_graph(@prog, + @show_authors, @selected_categories, @mm_size) + end + end #}}} + + def set_mm_options #{{{ + if @req.query.empty? + @prog = web.mm_prog + @graph_type = web.mm_graph_type + @missing = @pages_in_category.wanted_pages if web.mm_show_missing + @show_authors = web.mm_show_authors + @show_leaves = web.mm_show_leaves + @selected_categories = web.mm_selected_categories + @mm_size = web.mind_map_size + else + @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['show_authors'] == 'on' + @show_leaves = @req.query.empty? || @req.query['show_leaves'] == 'on' + + @selected_categories = parse_multi_select 'selected_categs' + @selected_categories = [] if @selected_categories.include? 'all' + @mm_size = @params['mind_map_size'] || web.mind_map_size + end + end #}}} + + def parse_multi_select field #{{{ + if @req.body + @req.body.split('&').map { |pair| + pair.split('=') }.select { |k,v| + k == field }.map { |k,v| v } + else + [] + 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 + @author = default_author + 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'] + sel_category = @req.query['sel_category'] + author = @req.query['author'] + + 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, sel_category, Author.new(author, remote_ip)) + end + + redirect_action 'edit_web/' # go back to web options page + 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 + + def adv_search + parse_category + set_menu_pages + 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 + set_menu_pages true + 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 = File.join 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 + # redirect_show("HomePage") if web.check_pass_on_edit and not password_check(@params['password']) + @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.check_pass_on_edit and not password_check(@params['password']) + # wiki.read_page(web_address, page_name).unlock if web.pages[page_name] + # redirect_show("HomePage") + # end + + if web.pages[page_name] + page = wiki.revise_page( + web_address, page_name, @params["content"], Time.now, + Author.new(@params["author"], remote_ip), @params['edit_type'] + ) + + 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 + redirect_show 'HomePage' if page_name.nil? + set_menu_pages + @page = wiki.read_page(web_address, page_name) + @revision = @page.revisions[@params["rev"].to_i] + end + + def rollback + # redirect_show("HomePage") if web.check_pass_on_edit and not password_check(@params['password']) + + @page = wiki.read_page(web_address, page_name) + @revision = @page.revisions[@params["rev"].to_i] + end + + # Bliki ---------------------------------------------------------------------- + + def bliki_delete + # redirect_bliki if web.check_pass_on_edit and not password_check(@params['password']) + + wiki.delete_bliki_entry(web_address, page_name) + clear_render_cache true + redirect_bliki + end + + def bliki_edit + # redirect_bliki if web.check_pass_on_edit and not password_check(@params['password']) + + @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 + @bliki_entry = true + render "wiki/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_new + @entry_name = @params['entry_name'] + @author = default_author + end + + def bliki_save + # redirect_bliki if web.check_pass_on_edit and not password_check(@params['password']) + + 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 + parse_category + set_menu_pages + @page = wiki.read_bliki_entry(web_address, page_name || @params['pagename']) + if @page.nil? + render_text "Unable to find bliki entry #{page_name || @params['pagename']}" + return + end + @revision = @page.revisions[@params["rev"].to_i] || @page.revisions.last + end + + def rollback_bliki + # redirect_bliki if web.check_pass_on_edit and not password_check(@params['password']) + + @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) #{{{ + "<a class='existingWikiWord' href='/#{web}/bliki_revision/#{entry.name}?rev=#{entry.revisions.size-1}'>#{entry.name}</a>" + 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.nil? || + 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 "pages-metadata.txt" + web.select.by_name.each do |page| + zos.puts "#{page.name}: by #{page.author}, created on #{page.pretty_created_at}" + end + + web.select_bliki.by_name.each do |@page| + zos.put_next_entry("bliki/#{@page.name}.html") + zos.puts(template_engine("print").result(binding)) + end + + zos.put_next_entry "bliki/bliki-metadata.txt" + web.select_bliki.by_name.each do |page| + zos.puts "#{page.name}: by #{page.author}, created on #{page.revisions.first.pretty_created_at}" + end + + zos.put_next_entry("index.html") + zos.puts('<html><head><META HTTP-EQUIV="Refresh" CONTENT="0;URL=HomePage.html"></head></html>') + 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 + + zos.put_next_entry "pages-metadata.txt" + web.select.by_name.each do |page| + zos.puts "#{page.name}: by #{page.author}, created on #{page.pretty_created_at}" + end + + web.select_bliki.by_name.each do |page| + zos.put_next_entry("bliki/#{page.name}.#{web.markup}") + zos.puts(page.content) + end + + zos.put_next_entry "bliki/bliki-metadata.txt" + web.select_bliki.by_name.each do |page| + zos.puts "#{page.name}: by #{page.author}, created on #{page.revisions.first.pretty_created_at}" + 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 + pages = web.select.by_name.inject({}) do |tex_web, page| + tex_web[page.name] = RedClothForTex.new(page.content).to_tex + tex_web + end + + bliki_entries = web.select_bliki.by_name.inject({}) do |tex_web, page| + tex_web["bliki/#{page.name}"] = RedClothForTex.new(page.content).to_tex + tex_web + end + + pages.update bliki_entries + 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 + if web + markup = web.markup + markup = 'markdown' if markup.to_s =~ /markdown/ + sub_template("#{markup}_help") + else + '' + end + 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: \ No newline at end of file