infocalypse

(djk)
2009-11-11: Added fn-putsite --wiki to insert freesites from wikis.

Added fn-putsite --wiki to insert freesites from wikis.

diff --git a/infocalypse/__init__.py b/infocalypse/__init__.py
--- a/infocalypse/__init__.py
+++ b/infocalypse/__init__.py
@@ -581,6 +581,8 @@ def infocalypse_putsite(ui_, repo, **opt
     """
 
     if opts['createconfig']:
+        if opts['wiki']:
+            raise util.Abort("Use fn-wiki --createconfig.")
         params = {'SITE_CREATE_CONFIG':True}
         execute_putsite(ui_, repo, params)
         return
@@ -592,6 +594,8 @@ def infocalypse_putsite(ui_, repo, **opt
                 params['SITE_KEY'] == 'CHK@'):
             raise util.Abort("--key must be a valid SSK "
                              + "insert key or CHK@.")
+
+    params['ISWIKI'] = opts['wiki']
     read_freesite_cfg(ui_, repo, params, stored_cfg)
 
     try:
@@ -728,6 +732,7 @@ cmdtable = {
                      [('', 'dryrun', None, "don't insert site"),
                      ('', 'index', -1, "edition to insert"),
                      ('', 'createconfig', None, "create default freesite.cfg"),
+                      ('', 'wiki', None, "insert a wiki, requires fnwiki.cfg"),
                      ('', 'key', '', "private SSK to insert under"),]
                      + FCP_OPTS,
                      "[options]"),
diff --git a/infocalypse/config.py b/infocalypse/config.py
--- a/infocalypse/config.py
+++ b/infocalypse/config.py
@@ -381,12 +381,20 @@ class Config:
 # the ConfigParser import hack. See top of file.
 def read_freesite_cfg(ui_, repo, params, stored_cfg):
     """ Read param out of the freesite.cfg file. """
-    cfg_file = os.path.join(repo.root, 'freesite.cfg')
+
+    fname = 'freesite.cfg'
+    # Hack to cut code paths to appease pylint. hmmmm....
+    no_cfg_err = "Use fn-putsite --createconfig to create freesite.cfg"
+    if params['ISWIKI']:
+        fname = 'fnwiki.cfg'
+        no_cfg_err = "Use fn-wiki --createconfig to create fnwiki.cfg"
+    cfg_file = os.path.join(repo.root, fname)
 
     ui_.status('Using config file:\n%s\n' % cfg_file)
     if not os.path.exists(cfg_file):
         ui_.warn("Can't read: %s\n" % cfg_file)
-        raise util.Abort("Use --createconfig to create freesite.cfg")
+        # REDFLAG: DCI TEST
+        raise util.Abort(no_cfg_err)
 
     parser = ConfigParser()
     parser.read(cfg_file)
@@ -394,7 +402,14 @@ def read_freesite_cfg(ui_, repo, params,
         raise util.Abort("Can't read default section of config file?")
 
     params['SITE_NAME'] = parser.get('default', 'site_name')
-    params['SITE_DIR'] = parser.get('default', 'site_dir')
+
+    # wiki specific
+    if params['ISWIKI']:
+        # REDFLAG: DCI test error
+        params['WIKI_ROOT'] = parser.get('default', 'wiki_root')
+    else:
+        params['SITE_DIR'] = parser.get('default', 'site_dir')
+
     if parser.has_option('default','default_file'):
         params['SITE_DEFAULT_FILE'] = parser.get('default', 'default_file')
     else:
@@ -425,6 +440,80 @@ def read_freesite_cfg(ui_, repo, params,
     if not params['SITE_KEY'].startswith('SSK@'):
         raise util.Abort("Stored site key not an SSK?")
 
+
+
+
+def write_default_config(ui_, repo, is_wiki=False):
+    """ Write a default freesite.cfg or fnwiki.cfg file into the repository
+        root dir. """
+
+    if not is_wiki:
+        file_name = os.path.join(repo.root, 'freesite.cfg')
+        text = \
+"""# freesite.cfg used by fn-putsite
+[default]
+# Human readable site name.
+site_name = default
+# Directory to insert from relative to the repository root.
+site_dir = site_root
+# Optional external file to load the site key from, relative
+# to the directory your .infocalypse/infocalypse.ini file
+# is stored in. This file should contain ONLY the SSK insert
+# key up to the first slash.
+#
+# If this value is not set the insert SSK for the repo is
+# used.
+#site_key_file = example_freesite_key.txt
+#
+# Optional file to display by default.  If this is not
+# set index.html is used.
+#default_file = index.html
+"""
+    else:
+        file_name = os.path.join(repo.root, 'fnwiki.cfg')
+        text = \
+"""# fnwiki.cfg used by fn-wiki and fn-putsite --wiki
+[default]
+# Wiki specific stuff
+#
+# The directory relative to the repository with the files
+# for the wiki. The directory layout is as follows:
+# <wiki_root>/wikitext/ -- contains wiki text file
+# <wiki_root>/www/piki.css -- contains the css for the wiki/freesite.
+# <wiki_root>/www/pikipiki-logo.png -- png diplayed in wiki headers.
+wiki_root = wiki_root
+#
+# freesite insertion stuff
+#
+# Human readable site name.
+site_name = default
+# site_dir = ignored # NOT USED for wikis
+# Optional external file to load the site key from, relative
+# to the directory your .infocalypse/infocalypse.ini file
+# is stored in. This file should contain ONLY the SSK insert
+# key up to the first slash.
+#
+# If this value is not set the insert SSK for the repo is
+# used.
+#site_key_file = example_freesite_key.txt
+#
+# File to display by default.
+default_file = FrontPage
+"""
+    if os.path.exists(file_name):
+        raise util.Abort("Already exists: %s" % file_name)
+
+
+    out_file = open(file_name, 'w')
+    try:
+        out_file.write(text)
+    finally:
+        out_file.close()
+
+    ui_.status('Created config file:\n%s\n' % file_name)
+    ui_.status('You probably want to edit at least the site_name.\n')
+
+
 def known_hashes(trust_map):
     """ Return all repo hashes in the trust map. """
     ret = set([])
diff --git a/infocalypse/sitecmds.py b/infocalypse/sitecmds.py
--- a/infocalypse/sitecmds.py
+++ b/infocalypse/sitecmds.py
@@ -21,44 +21,28 @@
 
 
 import os
+import shutil
+import sys # REDFLAG: DCI
 
 from mercurial import util
 
 from fcpconnection import FCPError
 from fcpclient import FCPClient, get_file_infos, set_index_file
 
-def write_default_config(ui_, repo):
-    """ Write a default freesite.cfg file into the repository root dir. """
-    file_name = os.path.join(repo.root, 'freesite.cfg')
+#------------------------------------------------------------
+# REDFLAG: DCI path hacks
+import validate
+ADD_DIR = os.path.join(os.path.dirname(
+    os.path.dirname(os.path.dirname(validate.__file__))),
+                       'clean_piki')
+sys.path.append(ADD_DIR)
 
-    if os.path.exists(file_name):
-        raise util.Abort("Already exists: %s" % file_name)
+import servepiki
 
-    out_file = open(file_name, 'w')
-    try:
-        out_file.write("""[default]
-# Human readable site name.
-site_name = default
-# Directory to insert from relative to the repository root.
-site_dir = site_root
-# Optional external file to load the site key from, relative
-# to the directory your .infocalypse/infocalypse.ini file
-# is stored in. This file should contain ONLY the SSK insert
-# key up to the first slash.
-#
-# If this value is not set the insert SSK for the repo is
-# used.
-#site_key_file = example_freesite_key.txt
-#
-# Optional file to display by default.  If this is not
-# set index.html is used.
-#default_file = index.html
-""")
-    finally:
-        out_file.close()
+#------------------------------------------------------------
 
-    ui_.status('Created config file:\n%s\n' % file_name)
-    ui_.status('You probably want to edit at least the site_name.\n')
+# REDFLAG: DCI deal with loading hacks for config
+from config import write_default_config
 
 def get_insert_uri(params):
     """ Helper function builds the insert URI. """
@@ -78,27 +62,43 @@ def show_request_uri(ui_, params, uri):
         request_uri = uri
     ui_.status('RequestURI:\n%s\n' % request_uri)
 
-def execute_putsite(ui_, repo, params):
-    """ Run the putsite command. """
-    def progress(dummy, msg):
-        """ Message callback which writes to the hg ui instance."""
+def dump_wiki_html(wiki_root, staging_dir):
+    """ Dump the wiki as flat directory of html.
 
-        if msg[0] == 'SimpleProgress':
-            ui_.status("Progress: (%s/%s/%s)\n" % (msg[1]['Succeeded'],
-                                                   msg[1]['Required'],
-                                                   msg[1]['Total']))
-        else:
-            ui_.status("Progress: %s\n" % msg[0])
+        wiki_root is the directory containing the wikitext and www dirs.
+        staging_dir MUST contain the substring 'deletable'.
+    """
 
+    # i.e. so you can't delete your home directory by mistake.
+    if not staging_dir.find("deletable"):
+        raise ValueError("staging dir name must contain 'deletable'")
 
-    if params.get('SITE_CREATE_CONFIG', False):
-        write_default_config(ui_, repo)
-        return
+    if os.path.exists(staging_dir):
+        shutil.rmtree(staging_dir)
+    assert not os.path.exists(staging_dir)
 
-    # Remove trailing /
-    params['SITE_KEY'] = params['SITE_KEY'].split('/')[0].strip()
-    insert_uri = get_insert_uri(params)
-    site_root = os.path.join(repo.root, params['SITE_DIR'])
+    os.makedirs(staging_dir)
+
+    # REDFLAG: DCI, should be piki.
+    servepiki.dump(staging_dir, wiki_root)
+
+TMP_DUMP_DIR = '_tmp_wiki_html_deletable'
+# Hmmmm... broken out to appease pylint
+def do_freenet_insert(ui_, repo, params, insert_uri, progress_func):
+    """ INTERNAL: Helper does the actual insert. """
+    default_mime_type = "text/plain" # put_complex_dir() default. Hmmmm..
+    if not params['ISWIKI']:
+        site_root = os.path.join(repo.root, params['SITE_DIR'])
+    else:
+        # REDFLAG: DCI temp file cleanup on exception
+
+        # Because wiki html files have no extension to guess from.
+        default_mime_type = 'text/html'
+
+        ui_.status("Dumping wiki as HTML...\n")
+        site_root = os.path.join(params['TMP_DIR'], TMP_DUMP_DIR)
+        dump_wiki_html(os.path.join(repo.root, params['WIKI_ROOT']),
+                       site_root)
 
     ui_.status('Default file: %s\n' % params['SITE_DEFAULT_FILE'])
     ui_.status('Reading files from:\n%s\n' % site_root)
@@ -124,11 +124,12 @@ def execute_putsite(ui_, repo, params):
     client = FCPClient.connect(params['FCP_HOST'],
                                params['FCP_PORT'])
     client.in_params.default_fcp_params['DontCompress'] = False
-    client.message_callback = progress
+    client.message_callback = progress_func
     try:
         ui_.status('Inserting to:\n%s\n' % insert_uri)
         try:
-            request_uri = client.put_complex_dir(insert_uri, infos)[1]['URI']
+            request_uri = client.put_complex_dir(insert_uri, infos,
+                                                 default_mime_type)[1]['URI']
             show_request_uri(ui_, params, request_uri)
         except FCPError, err:
             if err.is_code(9): # magick number for collision
@@ -141,6 +142,36 @@ def execute_putsite(ui_, repo, params):
     finally:
         client.close()
 
+def execute_putsite(ui_, repo, params):
+    """ Run the putsite command. """
+    def progress(dummy, msg):
+        """ Message callback which writes to the hg ui instance."""
+
+        if msg[0] == 'SimpleProgress':
+            ui_.status("Progress: (%s/%s/%s)\n" % (msg[1]['Succeeded'],
+                                                   msg[1]['Required'],
+                                                   msg[1]['Total']))
+        else:
+            ui_.status("Progress: %s\n" % msg[0])
+
+
+    if params.get('SITE_CREATE_CONFIG', False):
+        write_default_config(ui_, repo)
+        return
+
+    # Remove trailing /
+    params['SITE_KEY'] = params['SITE_KEY'].split('/')[0].strip()
+    try:
+        do_freenet_insert(ui_, repo, params,
+                          get_insert_uri(params),
+                          progress)
+    finally:
+        tmp_dump = os.path.join(params['TMP_DIR'], TMP_DUMP_DIR)
+        if os.path.exists(tmp_dump):
+            # REDFLAG: DCI, failure here is horrible.
+            # i.e. untrusted unencrypted data on your disk
+            shutil.rmtree(tmp_dump)
+
 MSG_FMT = """InsertURI:
 %s
 RequestURI:
diff --git a/infocalypse/wikicmds.py b/infocalypse/wikicmds.py
--- a/infocalypse/wikicmds.py
+++ b/infocalypse/wikicmds.py
@@ -35,6 +35,9 @@ from servepiki import serve_wiki, create
 
 from mercurial import util
 
+from config import write_default_config
+
+# REDFLAG: DCI path hacks
 # piki's required files are in that directory.
 import servepiki
 PIKI_WWW_SRC = os.path.dirname(servepiki.__file__)
@@ -62,14 +65,8 @@ def execute_wiki(ui_, repo, params):
                              "Move it out of the way to continue.")
 
         create_empty_wiki(os.path.join(repo.root, 'wiki_root'),  PIKI_WWW_SRC)
-        out = open(os.path.join(repo.root, 'fnwiki.cfg'), 'w')
-        out.write("""[default]
-# fniki.cfg tells piki where to read the wiki data from.
-[default]
-root_dir = wiki_root
-""")
-        out.close()
-        ui_.status("Created fnwiki.cfg and skeleton wiki_root dir.\n")
+        ui_.status("Created skeleton wiki_root dir.\n")
+        write_default_config(ui_, repo, True)
         return
 
     raise util.Abort("Unsupported subcommand: " + params.get('WIKI', 'unknown'))