func printEdittag(w http.ResponseWriter, r *http.Request) {
	a, err := new(w, r)
	if err != nil {
		log.Println(err)
		return
	}
	datfile := a.Req.FormValue("file")
	strTitle := util.FileDecode(datfile)
	ca := thread.NewCache(datfile)
	datfile = html.EscapeString(datfile)
	if !ca.Exists() {
		a.Print404(nil, "")
		return
	}
	d := struct {
		Message  cgi.Message
		AdminCGI string
		Datfile  string
		Tags     string
		Sugtags  tag.Slice
		Usertags tag.Slice
	}{
		a.M,
		cfg.AdminURL,
		datfile,
		user.String(ca.Datfile),
		suggest.Get(ca.Datfile, nil),
		user.GetByThread(ca.Datfile),
	}
	a.Header(fmt.Sprintf("%s: %s", a.M["edit_tag"], strTitle), "", nil, true)
	cgi.RenderTemplate("edit_tag", d, a.WR)
	a.Footer(nil)
}
printEdittag renders the page for editing tags in thread specified by form "file".
package admin
import (
	"fmt"
	"html"
	"log"
	"math/rand"
	"net/http"
	"regexp"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"time"
	"errors"
	"github.com/shingetsu-gou/shingetsu-gou/cfg"
	"github.com/shingetsu-gou/shingetsu-gou/cgi"
	"github.com/shingetsu-gou/shingetsu-gou/myself"
	"github.com/shingetsu-gou/shingetsu-gou/node"
	"github.com/shingetsu-gou/shingetsu-gou/node/manager"
	"github.com/shingetsu-gou/shingetsu-gou/recentlist"
	"github.com/shingetsu-gou/shingetsu-gou/record"
	"github.com/shingetsu-gou/shingetsu-gou/tag"
	"github.com/shingetsu-gou/shingetsu-gou/tag/suggest"
	"github.com/shingetsu-gou/shingetsu-gou/tag/user"
	"github.com/shingetsu-gou/shingetsu-gou/thread"
	"github.com/shingetsu-gou/shingetsu-gou/util"
)
var adminSID = ""
func Setup(s *cgi.LoggingServeMux) {
	s.RegistCompressHandler(cfg.AdminURL+"/status", printStatus)
	s.RegistCompressHandler(cfg.AdminURL+"/edittag", printEdittag)
	s.RegistCompressHandler(cfg.AdminURL+"/savetag", saveTagCGI)
	s.RegistCompressHandler(cfg.AdminURL+"/search", printSearch)
	s.RegistCompressHandler(cfg.AdminURL+"/", execCmd)
}
func execCmd(w http.ResponseWriter, r *http.Request) {
	a, err := new(w, r)
	if err != nil {
		log.Println(err)
		return
	}
	cmd := a.Req.FormValue("cmd")
	rmFiles := a.Req.Form["file"]
	rmRecords := a.Req.Form["record"]
	switch cmd {
	case "rdel":
		a.printDeleteRecord(rmFiles, rmRecords)
	case "fdel":
		a.printDeleteFile(rmFiles)
	case "xrdel":
		a.doDeleteRecord(rmFiles, rmRecords, a.Req.FormValue("dopost"))
	case "xfdel":
		a.doDeleteFile(rmFiles)
	}
}
func printSearch(w http.ResponseWriter, r *http.Request) {
	a, err := new(w, r)
	if err != nil {
		log.Println(err)
		return
	}
	query := a.Req.FormValue("query")
	if query == "" {
		a.Header(a.M["search"], "", nil, true)
		fmt.Fprintf(a.WR, "<p>%s</p>", a.M["desc_search"])
		a.printSearchForm("")
		a.Footer(nil)
	} else {
		a.printSearchResult(query)
	}
}
func printStatus(w http.ResponseWriter, r *http.Request) {
	a, err := new(w, r)
	if err != nil {
		log.Println()
		return
	}
	records := 0
	var size int64
	for _, ca := range thread.AllCaches() {
		records += ca.Len(record.All)
		size += ca.Size()
	}
	var mem runtime.MemStats
	runtime.ReadMemStats(&mem)
	var port0 string
	switch myself.GetStatus() {
	case cfg.Normal:
		port0 = a.M["opened"]
	case cfg.UPnP:
		port0 = "UPnP"
	case cfg.Port0:
		port0 = a.M["port0"]
	case cfg.Disconnected:
		port0 = a.M["disconnected"]
	}
	s := map[string]string{
		"known_nodes":       strconv.Itoa(manager.NodeLen()),
		"linked_nodes":      strconv.Itoa(manager.ListLen()),
		"files":             strconv.Itoa(thread.Len()),
		"records":           strconv.Itoa(records),
		"cache_size":        fmt.Sprintf("%.1f%s", float64(size)/1024/1024, a.M["mb"]),
		"self_node":         node.Me(false).Nodestr,
		"alloc_mem":         fmt.Sprintf("%.1f%s", float64(mem.Alloc)/1024/1024, a.M["mb"]),
		"connection_status": port0,
	}
	ns := map[string][]string{
		"known_nodes":  manager.GetNodestrSlice(),
		"linked_nodes": manager.GetNodestrSliceInList(),
	}
	d := struct {
		Status     map[string]string
		NodeStatus map[string][]string
		Message    cgi.Message
	}{
		s,
		ns,
		a.M,
	}
	a.Header(a.M["status"], "", nil, true)
	cgi.RenderTemplate("status", d, a.WR)
	a.Footer(nil)
}