From e874730679592331c481bf7137f62250e46e6e93 Mon Sep 17 00:00:00 2001
From: shortcutme <tamas@zeronet.io>
Date: Wed, 2 May 2018 02:31:31 +0200
Subject: [PATCH] Rev3464, Download site as zip

---
 plugins/Sidebar/SidebarPlugin.py | 33 +++++++++++++++++++++++-
 plugins/Sidebar/ZipStream.py     | 43 ++++++++++++++++++++++++++++++++
 src/Config.py                    |  2 +-
 3 files changed, 76 insertions(+), 2 deletions(-)
 create mode 100644 plugins/Sidebar/ZipStream.py

diff --git a/plugins/Sidebar/SidebarPlugin.py b/plugins/Sidebar/SidebarPlugin.py
index 4bf716f2..962edba0 100644
--- a/plugins/Sidebar/SidebarPlugin.py
+++ b/plugins/Sidebar/SidebarPlugin.py
@@ -17,6 +17,7 @@ from Plugin import PluginManager
 from Debug import Debug
 from Translate import Translate
 from util import helper
+from ZipStream import ZipStream
 
 plugin_dir = "plugins/Sidebar"
 media_dir = plugin_dir + "/media"
@@ -62,6 +63,28 @@ class UiRequestPlugin(object):
             for part in super(UiRequestPlugin, self).actionUiMedia(path):
                 yield part
 
+    def actionZip(self):
+        address = self.get["address"]
+        site = self.server.site_manager.get(address)
+        if not site:
+            return self.error404("Site not found")
+
+        title = site.content_manager.contents.get("content.json", {}).get("title", "").encode('ascii', 'ignore')
+        filename = "%s-backup-%s.zip" % (title, time.strftime("%Y-%m-%d_%H_%M"))
+        self.sendHeader(content_type="application/zip", extra_headers={'Content-Disposition': 'attachment; filename="%s"' % filename})
+
+        return self.streamZip(site.storage.getPath("."))
+
+    def streamZip(self, file_path):
+        zs = ZipStream(file_path)
+        while 1:
+            data = zs.read()
+            if not data:
+                break
+            yield data
+
+
+
 
 @PluginManager.registerTo("UiWebsocket")
 class UiWebsocketPlugin(object):
@@ -137,7 +160,15 @@ class UiWebsocketPlugin(object):
         """))
 
     def sidebarRenderFileStats(self, body, site):
-        body.append(_(u"<li><label>{_[Files]}  <small><a href='#Site+directory' id='link-directory' class='link-right'>{_[Open site directory]}</a></small></label><ul class='graph graph-stacked'>"))
+        body.append(_(u"""
+            <li>
+             <label>
+              {_[Files]}
+              <small><a href='#Site+directory' id='link-directory' class='link-right'>{_[Open site directory]}</a>
+              <a href='/ZeroNet-Internal/Zip?address={site.address}' id='link-zip' class='link-right'>{_[Download as .zip]}</a></small>
+             </label>
+             <ul class='graph graph-stacked'>
+        """))
 
         extensions = (
             ("html", "yellow"),
diff --git a/plugins/Sidebar/ZipStream.py b/plugins/Sidebar/ZipStream.py
new file mode 100644
index 00000000..a78eac2b
--- /dev/null
+++ b/plugins/Sidebar/ZipStream.py
@@ -0,0 +1,43 @@
+import cStringIO as StringIO
+import os
+import zipfile
+
+
+class ZipStream(file):
+    def __init__(self, dir_path):
+        self.dir_path = dir_path
+        self.pos = 0
+        self.zf = zipfile.ZipFile(self, 'w', zipfile.ZIP_DEFLATED)
+        self.buff = StringIO.StringIO()
+        self.file_list = self.getFileList()
+
+    def getFileList(self):
+        for root, dirs, files in os.walk(self.dir_path):
+            for file in files:
+                file_path = root + "/" + file
+                relative_path = os.path.join(os.path.relpath(root, self.dir_path), file)
+                yield file_path, relative_path
+        self.zf.close()
+
+    def read(self, size=60 * 1024):
+        for file_path, relative_path in self.file_list:
+            self.zf.write(file_path, relative_path)
+            if self.buff.tell() >= size:
+                break
+        self.buff.seek(0)
+        back = self.buff.read()
+        self.buff.truncate(0)
+        return back
+
+    def write(self, data):
+        self.pos += len(data)
+        self.buff.write(data)
+
+    def tell(self):
+        return self.pos
+
+    def seek(self, pos, type):
+        pass
+
+    def flush(self):
+        pass
diff --git a/src/Config.py b/src/Config.py
index 0de9c3bf..0c00c84d 100644
--- a/src/Config.py
+++ b/src/Config.py
@@ -10,7 +10,7 @@ class Config(object):
 
     def __init__(self, argv):
         self.version = "0.6.2"
-        self.rev = 3465
+        self.rev = 3467
         self.argv = argv
         self.action = None
         self.config_file = "zeronet.conf"