# 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
    # ============================== attach
    def act_attach
      if @req.query["c"] == "del"
	c_require_member
	filename = @req.query["f"]
	return attach_delete(filename) if @req.is_post? # delete it
	return attach_delete_confirm(filename) # confirm before delete
      end

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

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

      # show attach form and the list of files
      return attach_form_and_list
    end
    alias ext_attach act_attach

    def attach_send_file(filename)
      ext = File.extname(filename).sub(/\A\./, "")
      type = @res.mimetypes[ext]
      file = @site.attach.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 attach_put_file(filename, content)
      c_require_member
      c_require_post

      filename = content.filename
      basename = filename.sub(/\A.*[\/\\]([^\/\\]+)\z/) { $1 }
      begin
	@site.attach.put(basename, content)
      rescue AlreadyExist
	return c_nerror(_("There is a file with same file name."))
      rescue CanNotUseJapaneseCharacter
	return c_nerror(_("Can not use Japanese character for file name"))
      end

      attach_rewrite_page(basename)

      backpage = ".attach"

      return c_notice(_("Attach file done")){[
	  [:p, [:strong, basename], " : ", _("The file is saved.")],
	  [:p, _("Go back"), " : ", [:a, {:href=>backpage}, backpage]]
	]}
    end

    def attach_rewrite_page(basename)
      page = @site[@req.base]
      v = page.load
      if /\{\{ref\}\}/ =~ v
	v.sub!(/\{\{ref\}\}/){ "{{ref("+basename+")}}" } # only once
	page.store(v)
      end
    end

    def attach_form_and_list
      c_require_member

      div = []
      div << [:div, {:class=>"msg"},
	[:p, _("Attach a file")],
	attach_form]

      list = attach_file_list

      if list
	div << [:br]
	div << [:div, {:class=>"msg"},
	  [:p, _("File list")],
	  [:ul, list]]
      end

      #return c_notice(_("Attach file"), "FrontPage.html"){div} # why?
      return c_notice(_("Attach file")){div}
    end

    def attach_form(a=nil)
      [:form, {:action=>a, :method=>"post", :enctype=>"multipart/form-data"},
	[:input, {:type=>"file", :name=>"content", :style=>"width:30em;"}],
	[:br],
	[:input, {:type=>"submit", :value=>"attach"}]]
    end

    def attach_file_list
      base = c_relative_to_absolute(".attach")
      list = []
      @site.attach.each {|file|
	list << [:li,
	  [:a, {:href=>base+"/"+file}, file],
	  " (", [:a, {:href=>base+"?c=del&f="+file}, "delete"], ")"]
      }

      return nil if list.length == 0

      return list
    end

    def attach_delete_confirm(filename)
      return c_notice(_("Confirm to delete a file")){
	[:form, {:method=>"POST", :action=>".attach"},
	  [: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 attach_delete(filename)
      c_require_post
      url = ".attach"
      begin
	@site.attach.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.")){
	[:p, _("Go back"), " : ", [:a, {:href=>url}, url]]
      }
    end
  end
end

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

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

    def test_attach
      # Only member can access attach form.
      session("/test/.attach")
      assert_text("Member Only", "title")

      t_add_user

      # Check directory.
      assert_equal("./test", @site.dir)
      attach_path = @site.dir.path+".attach"
      assert_equal("./test/.attach".path, attach_path)

      filename = "t.txt"
      file = attach_path+filename
      file.unlink if file.exist?

      # Check mimetypes.  should be moved to test-response?
      assert_equal(true, 50 < @res.mimetypes.length)
      assert_equal("text/html", @res.mimetypes["html"])
      assert_equal("text/plain", @res.mimetypes["txt"])
      assert_equal("text/css", @res.mimetypes["css"])
      assert_equal("image/gif", @res.mimetypes["gif"])
      assert_equal("image/png", @res.mimetypes["png"])
      assert_equal("image/jpeg", @res.mimetypes["jpg"])
      assert_equal("application/smil", @res.mimetypes["smil"])
      assert_equal("application/zip", @res.mimetypes["zip"])

      # See form.
      session("/test/.attach")
      assert_text("Attach file", "title")
      assert_xpath([:form,
		     {:method=>"post", :action=>nil,
		       :enctype=>"multipart/form-data"},
		     [:input, {:style=>"width:30em;",
			 :type=>"file", :name=>"content"}],
		     [:br],
		     [:input, {:value=>"attach", :type=>"submit"}]],
		   "//div[@class='msg']/form")
#      px("//div[@class='msg']", 0)

      # Try to get a file.  But there is no file yet.
      session("/test/.attach/t.txt")
      assert_text("No such file", "title")

      d = @dir+".attach"
      d.erase_all_for_test if d.exist?

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

      # Get the file.
      session("/test/.attach/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/.attach") {|req|
	req.query.update({"content"=>content})
      }
      assert_text("There is a file with same file name.", "title")

      # Show the list of attached files.
      session("/test/.attach")
      assert_text("Attach file", "title")
      #px("//ul", 0)
      assert_xpath([:form, {:method=>"post", :action=>nil,
		       :enctype=>"multipart/form-data"},
		     [:input, {:style=>"width:30em;",
			 :type=>"file", :name=>"content"}],
		     [:br],
		     [:input, {:value=>"attach", :type=>"submit"}]],
		   "//div[@class='msg']/form")
      assert_xpath([:a, {:href=>"/test/.attach/t.txt"}, "t.txt"], "//ul/li/a")
      assert_xpath([:a, {:href=>"/test/.attach?c=del&f=t.txt"}, "delete"],
		   "//ul/li/a[2]")
#      px("//div[@class='msg']", 0)
#      assert_xpath([], "//div[@class='msg']")

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

      # Delete it.
      session("POST /test/.attach?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/.attach?c=del&f=t.txt")
      assert_text("Already deleted.", "title")

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

    def test_attach_with_rewrite
      t_add_user

      file = @site.attach.path("t.txt")
      file.unlink if file.exist?

      # See the page.
      assert_wiki([:div, {:class=>"ref"},
		    [:form, {:method=>"post", :action=>"1.attach",
			:enctype=>"multipart/form-data"},
		      [:input, {:style=>"width:30em;",
			  :type=>"file", :name=>"content"}], [:br],
		      [:input, {:value=>"attach", :type=>"submit"}]]],
		  "{{ref}}")

      page = @site["1"]
      assert_equal("{{ref}}", page.load)

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

      session("/test/1.html")
      assert_xpath([:div, {:class=>"section"},
		     [[:div, {:class=>"ref"},
			 [:a, {:href=>".attach/t.txt"},
			   [:img, {:src=>".theme/icons/text.gif",
			       :alt=>"t.txt", :class=>"icon"}],
			   [:br], "t.txt"]]]],
		   "//div[@class='section']")
    end

    def test_attach_with_dir
      t_add_user

      d = @dir+".attach"
      d.erase_all_for_test if d.exist?

      # Try to get a file.
      session("/test/.attach/.thumb/t.jpg")
      assert_text("No such file", "title")

      thumb_dir = d+".thumb"
      thumb_dir.check_directory
      thumb = thumb_dir+"t.jpg"
      thumb.open("wb") {|f|
	f.puts "dummy"
      }

      session("/test/.attach/.thumb/t.jpg")
      assert_instance_of(File, @res.body)
      str = @res.body.read
      @res.body.close # important
      assert_equal("dummy\n", str)
      assert_equal("image/jpeg", @res["Content-Type"])
    end

    def test_attach_to_page
      t_add_user

      page = @site.create_new
      page.store("t")

      # See form.
      session("/test/1.attach")
      assert_text("Attach file", "title")
      assert_xpath([:form,
		     {:method=>"post", :action=>nil,
		       :enctype=>"multipart/form-data"},
		     [:input, {:style=>"width:30em;",
			 :type=>"file", :name=>"content"}],
		     [:br],
		     [:input, {:value=>"attach", :type=>"submit"}]],
		   "//div[@class='msg']/form")

    end
  end
end
