hg site extension
 
(Arne Babenhauserheide)
2012-06-24: rename site.py to staticsite.py to avoid import conflicts with the

rename site.py to staticsite.py to avoid import conflicts with the python site module. Also add FTP push support.

diff --git a/site.py b/staticsite.py
rename from site.py
rename to staticsite.py
--- a/site.py
+++ b/staticsite.py
@@ -28,7 +28,7 @@ import datetime
 from mercurial import cmdutil, util, scmutil
 from mercurial import commands, dispatch
 from mercurial.i18n import _
-from mercurial import hg, discovery
+from mercurial import hg, discovery, util, extensions
 
 _staticidentifier = ".statichgrepo"
 
@@ -563,8 +563,13 @@ def addrepo(ui, repo, target, bookmarks)
 
 def upload(ui, repo, target, ftpstring, force):
     """upload the repo to the FTP server identified by the ftp string."""
-    user, password = ftpstring.split("@")[0].split(":")
-    serverandpath = "@".join(ftpstring.split("@")[1:])
+    try:
+        user, password = ftpstring.split("@")[0].split(":")
+        serverandpath = "@".join(ftpstring.split("@")[1:])
+    except ValueError:
+        ui.warn(_("FTP-upload: No @ in FTP-Url. We try anonymous access.\n"))
+        user, password = "anonymous", ""
+        serverandpath = ftpstring # no @, so we just take the whole string
     server = serverandpath.split("/")[0]
     ftppath = "/".join(serverandpath.split("/")[1:])
     timeout = 10
@@ -671,8 +676,8 @@ def staticsite(ui, repo, target=None, **
     # add the hg repo to the static site
     # currently we need to either include all bookmarks or not, because we don’t have the remote repo when parsing the site.
     # TODO: I don’t know if that is the correct way to go. Maybe always push all.
-    bookmarks = opts["bookmarks"]
-    addrepo(ui, repo, target, bookmarks)
+    bookmark = opts["bookmark"]
+    addrepo(ui, repo, target, bookmark)
     # first: just create the site.
     parsesite(ui, repo, target, **opts)
     if opts["upload"]:
@@ -691,6 +696,119 @@ cmdtable = {
                       ('f', 'force', False, 'force recreating all commit files. Slow.'),
                       ('s', 'screenstyle', "", 'use a custom stylesheet for display on screen'),
                       ('p', 'printstyle', "", 'use a custom stylesheet for printing'),
-                      ('B', 'bookmarks', False, 'include the bookmarks')],
+                      ('B', 'bookmark', False, 'include the bookmarks')],
                      "[options] [folder]")
 }
+
+## add ftp as scheme to be handled by this plugin.
+
+wrapcmds = { # cmd: generic, target, fixdoc, ppopts, opts
+    'push': (False, None, False, False, [
+        ('', 'staticsite', None, 'show parent svn revision instead'),
+    ])
+}
+
+## Explicitely wrap functions to change local commands in case the remote repo is an FTP repo. See mercurial.extensions for more information.
+# Get the module which holds the functions to wrap
+#from mercurial import discovery
+# the new function: gets the original function as first argument and the originals args and kwds.
+#def findcommonoutgoing(orig, *args, **opts):
+#    capable = getattr(args[1], 'capable', lambda x: False)
+#    if capable('ftp'):
+#        return []#orig(*args, **opts)
+#    else:
+#        return orig(*args, **opts)
+# really wrap the function
+#extensions.wrapfunction(discovery, 'findcommonoutgoing', findcommonoutgoing)
+
+# explicitely wrap commands in case the remote repo is an FTP repo.
+def ftppush(orig, *args, **opts):
+    print args, opts
+    ui, repo, path = args
+    # only act differently, if the target is an FTP repo.
+    if not path.startswith("ftp"):
+        return orig(*args, **opts)
+    # first create the site at ._site
+    target = "._site"
+    ftpstring = path.replace("ftp://", "")
+    if not "name" in opts:
+        opts["name"] = None
+        opts["screenstyle"] = ""
+        opts["printstyle"] = ""
+        opts["upload"] = ftpstring
+        opts["force"] = False
+    staticsite(ui, repo, target, **opts)
+        
+# really wrap the command
+entry = extensions.wrapcommand(commands.table, "push", ftppush)
+
+# Starting an FTP repo. Not yet used, except for throwing errors for missing commands and faking the lock.
+
+from mercurial import repo, util
+try:
+    from mercurial.error import RepoError
+except ImportError:
+    from mercurial.repo import RepoError
+
+class FTPRepository(repo.repository):
+    def __init__(self, ui, path, create):
+        self.create = create
+        self.ui = ui
+        self.path = path
+        self.capabilities = set(["ftp"])
+
+    def lock(self):
+        """We cannot really lock FTP repos, yet.
+
+        TODO: Implement as locking the repo in the static site folder."""
+        class DummyLock:
+            def release(self):
+                pass
+        l = DummyLock()
+        return l
+
+    def url(self):
+        return self.path
+
+    def lookup(self, key):
+        return key
+
+    def cancopy(self):
+        return False
+
+    def heads(self, *args, **opts):
+        """
+        Whenever this function is hit, we abort. The traceback is useful for
+        figuring out where to intercept the functionality.
+        """
+        raise util.Abort('command heads unavailable for FTP repositories')
+
+    def pushkey(self, namespace, key, old, new):
+        return False
+
+    def listkeys(self, namespace):
+        return {}
+
+    def push(self, remote, force=False, revs=None, newbranch=None):
+        raise util.Abort('command push unavailable for FTP repositories')
+    
+    def pull(self, remote, heads=[], force=False):
+        raise util.Abort('command pull unavailable for FTP repositories')
+    
+    def findoutgoing(self, remote, base=None, heads=None, force=False):
+        raise util.Abort('command findoutgoing unavailable for FTP repositories')
+
+
+class RepoContainer(object):
+    def __init__(self):
+        pass
+
+    def __repr__(self):
+        return '<FTPRepository>'
+
+    def instance(self, ui, url, create):
+        # Should this use urlmod.url(), or is manual parsing better?
+        #context = {}
+        return FTPRepository(ui, url, create)
+
+hg.schemes["ftp"] = RepoContainer()