# Copyright (C) 2003-2005 Kouichirou Eto, All rights reserved.

$LOAD_PATH << "../../lib" unless $LOAD_PATH.include?("../../lib")
require "qwik/act-simple-send"
require "qwik/act-ref"

module Qwik
  class Action
    def plg_show_files
      div = [:div, {:class=>"files"},
	  [:h5, [:a, {:href=>@req.base+".files"}, _("Files")]]]
      list = files_list
      div << [:ul, *list] if list
      return div
    end

    def files_list
      ar = @site.files(@req.base).to_a
      return if ar.empty?
      base = c_relative_to_absolute(@req.base+".files")
      return ar.map {|file|
	[:li, [:a, {:href=>base+"/"+file}, file],
	  [:span, {:class=>"delete"},
	    " (", [:a, {:href=>base+"?c=del&f="+file}, _("del")], ")"]]
      }
    end

    def ext_files
      if @req.query["c"] == "del"
	c_require_member
	filename = @req.query["f"]
	return files_delete(filename) if @req.is_post? # delete it
	return files_delete_confirm(filename) # confirm before delete
      end

      args = @req.path_args
      if 0 < args.length # has target file
	filename = args.join("/")
	if !@site.files(@req.base).exist?(filename)
	  return c_nerror(_("No such file"), nil, 404)
	end
	return files_get(filename) # send it
      end

      content = @req.query["content"]
      if content
	return files_put(filename, content)
      end

      # show files form and the list
      return files_form_and_list
    end

    def files_get(filename)
      ext = File.extname(filename).sub(/\A\./, "")
      type = @res.mimetypes[ext]
      file = @site.files(@req.base).path(filename)

      ss = SimpleSender.new(@config, @req, @res)
      return ss.simple_send(file.to_s, type)
      # c_simple_send(file.to_s, type)	# why this does not work?
    end

    def files_put(filename, content)
      c_require_member
      c_require_post

      list = []

      success = true
      content.each_data {|data|
	fullfilename = data.filename
	next if fullfilename.empty?

	filename = fullfilename.sub(/\A.*[\/\\]([^\/\\]+)\z/) { $1 }

	begin
	  @site.files(@req.base).put(filename, data)
	rescue AlreadyExist
	  list << files_msg(filename, _("There is a file with same file name."))
	  success = false
	  next
	rescue CanNotUseJapaneseCharacter
	  list << files_msg(filename,
			    _("Can not use Japanese character for file name"))
	  success = false
	  next
	end

	files_update_page(filename)
	list << files_msg(filename, _("The file is saved."))
      }

      url = @req.base+".html"
      if success
	ar = list+[
	  [:hr],
	  [:p,_("Go next")," : ",[:a,{:href=>url},url]],
	]
	return c_notice(_("Attach file done"), url){ar}
      else
	return c_notice(_("Attach file done"), url){list}
      end
    end

    def files_msg(filename, message)
      [:p, [:strong, filename], " : ", message]
    end

    def files_update_page(basename)
      page = @site[@req.base]
      v = page.load
      page.add("\n{{file(#{basename})}}\n")
    end

    def files_form_and_list
      c_require_member
      ar = []
      10.times {
	ar << [:input, {:type=>"file", :name=>"content"}]
	ar << [:br]
      }
      ar << [:input, {:type=>"submit", :value=>"attach"}]
      #return c_notice(_("Attach file"), @req.base+".html"){[ # why?
      return c_notice(_("Attach file")){[
	  [:p, _("Attach a file")],
	  [:form, {:action=>@req.base+".files",
	      :method=>"post", :enctype=>"multipart/form-data"},
	    *ar]
	]}
    end

    def files_delete_confirm(filename)
      return c_notice(_("Confirm to delete a file")){
	[:form, {:method=>"POST", :action=>"#{@req.base}.files"},
	  [:p, _("Push 'Delete' to delete a file")],
	  [:input, {:type=>"hidden", :name=>"c", :value=>"del"}],
	  [:input, {:type=>"hidden", :name=>"f", :value=>filename}],
	  [:p, [:input, {:type=>"submit", :value=>_("Delete")}]]]
      }
    end

    def files_delete(filename)
      c_require_post
      url = @req.base+".html"
      begin
	@site.files(@req.base).delete(filename)
      rescue AlreadyDeleted
	return c_nerror(_("Already deleted."), url)
      rescue FailedToDelete
	return c_nerror(_("Failed to delete."), url)
      end
      return c_notice(_("The file is deleted."), url){
	[:p, _("Go back"), " : ", [:a, {:href=>url}, url]]
      }
    end
  end
end

if $0 == __FILE__
  require "t/test"
  $debug = true
end

if $debug
  class TC_ActFiles < Test::Unit::TestCase
    include TestSession

    def test_files
      t_add_user

      assert_wiki([:div, {:class=>"files"},
		    [:h5, [:a, {:href=>"1.files"}, "Files"]]], "{{show_files}}")

      page = @site["1"]
      page.store("t")

      # See a page.
      session("/test/1.html")
      #pw("//div[@class='comment']")
      #pw("//div[@class='files']")
      assert_xpath([:div, {:class=>"files"},
		     [:h5, [:a, {:href=>"1.files"}, "Files"]]],
		   "//div[@class='files']")

      # See the form.
      session("/test/1.files")
      form = @res.body.get_path("//div[@class='section']/form")
      assert_equal({:method=>"post", :action=>"1.files",
		     :enctype=>"multipart/form-data"}, form[1])
      assert_equal([:input, {:type=>"file", :name=>"content"}], form[2])
      assert_equal([:input, {:value=>"attach", :type=>"submit"}], form.last)

      # Put a file.
      content = WEBrick::HTTPUtils::FormData.new("test\n")
      content.filename = "t.txt"
      session("POST /test/1.files") {|req|
	req.query.update({"content"=>content})
      }
      assert_text("Attach file done", "title")

      # The reference is added.
      assert_equal("t\n\n{{file(t.txt)}}\n", page.load)

      # Get the file.
      session("/test/1.files/t.txt")
      assert_instance_of(File, @res.body)
      str = @res.body.read
      @res.body.close # important
      assert_equal("test\n", str)
      assert_equal("text/plain", @res["Content-Type"])

      # Put a file with same file name again.  It cause an error.
      content = WEBrick::HTTPUtils::FormData.new("test2\n")
      content.filename = "t.txt"
      session("POST /test/1.files") {|req|
	req.query.update({"content"=>content})
      }
      assert_text("Attach file done", "title")
#      pp @res.body.get_path("//div[@class='msg']/p")
      assert_xpath([:p, [:strong, "t.txt"], " : ",
		     "There is a file with same file name."],
		   "//div[@class='section']/p")

      # See the page again.
      session("/test/1.html")
      assert_xpath([:div, {:class=>"files"},
		     [:h5, [:a, {:href=>"1.files"}, "Files"]],
		     [:ul, [:li, [:a, {:href=>"/test/1.files/t.txt"}, "t.txt"],
			   [:span, {:class=>"delete"},
			     " (", [:a, {:href=>"/test/1.files?c=del&f=t.txt"},
			     "del"], ")"]]]],
		   "//div[@class='files']")

      # Put another file.
      png = "\211PNG\r\n\032\n\000\000\000\rIHDR\000\000\000\001\000\000\000\001\010\002\000\000\000\220wS\336\000\000\000\fIDATx\332c\370\377\377?\000\005\376\002\3763\022\225\024\000\000\000\000IEND\256B`\202"
      content = WEBrick::HTTPUtils::FormData.new(png)
      content.filename = "s.png"
      session("POST /test/1.files") {|req|
	req.query.update({"content"=>content})
      }
      assert_text("Attach file done", "title")

      # The reference is added too.
      assert_equal("t\n\n{{file(t.txt)}}\n\n{{file(s.png)}}\n", page.load)

      # See the page again.
      session("/test/1.html")
      assert_xpath([:div,{:class=>"files"},
		     [:h5, [:a, {:href=>"1.files"}, "Files"]],
		     [:ul, [:li, [:a, {:href=>"/test/1.files/s.png"}, "s.png"],
			 [:span, {:class=>"delete"},
			   " (", [:a, {:href=>"/test/1.files?c=del&f=s.png"},
			     "del"], ")"]],
		       [:li, [:a, {:href=>"/test/1.files/t.txt"}, "t.txt"],
			 [:span, {:class=>"delete"},
			   " (", [:a, {:href=>"/test/1.files?c=del&f=t.txt"},
			     "del"], ")"]]]],
		   "//div[@class='files']")

      # Show a form to delete the file.
      session("/test/1.files?c=del&f=t.txt")
      assert_text("Confirm to delete a file", "title")

      # Delete it.
      session("POST /test/1.files?c=del&f=t.txt")
      assert_text("The file is deleted.", "title")

      # Try to delete it again.  But the file is already deleted.
      session("POST /test/1.files?c=del&f=t.txt")
      assert_text("Already deleted.", "title")

      # Try to get file agin.  But the file is already deleted.
      session("/test/1.files/t.txt")
      assert_text("No such file", "title")
    end

    def test_multi_files
      t_add_user
      page = @site.create_new

      # See the form.
      session("/test/1.files")
      form = @res.body.get_path("//div[@class='section']/form")
      assert_equal({:method=>"post", :action=>"1.files",
		     :enctype=>"multipart/form-data"}, form[1])
      assert_equal([:input, {:type=>"file", :name=>"content"}], form[2])
      assert_equal([:input, {:type=>"file", :name=>"content"}], form[4])
      assert_equal([:input, {:value=>"attach", :type=>"submit"}], form.last)

      # Put multiple files.
      content1 = WEBrick::HTTPUtils::FormData.new("t1")
      content1.filename = "t1.txt"
      content2 = WEBrick::HTTPUtils::FormData.new("t2")
      content2.filename = "t2.txt"
      content1.append_data(content2)
      session("POST /test/1.files") {|req|
	req.query.update({"content"=>content1})
      }
      assert_text("Attach file done", "title")
      assert_in([
		   [:p, [:strong, "t1.txt"], " : ", "The file is saved."],
		   [:p, [:strong, "t2.txt"], " : ", "The file is saved."],
		   [:hr],
		   [:p, "Go next", " : ",
		     [:a, {:href=>"1.html"}, "1.html"]]],
		 "//div[@class='section']")

      # The reference is added.
      assert_equal("\n{{file(t1.txt)}}\n\n{{file(t2.txt)}}\n", page.load)

#      session("POST /test/1.files", {"content"=>content},
#	      {"user"=>"user@e.com", "pass"=>"ABAB4B32"})
#      assert_text("Attach file done", "title")
    end
  end
end

