diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..9dca441
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,24 @@
+Hyde is primarily developed and maintained by [Lakshmi Vyasarajan][1]. The new
+version of Hyde is sponsored by [Flowplayer][2] and [Tero Piirainen][3].
+
+This version would not exist without the contributions for the
+[original hyde project][4].
+
+Contributors
+=============
+
+
+- @rfk
+
+ * Bug fixes
+ * Pyfs publisher with mimetype and etags support
+
+- @tinnet
+
+ * Bug fixes (Default template, Syntax template tag)
+
+
+[1]: http://twitter.com/lakshmivyas
+[2]: http://flowplayer.org
+[3]: http://cloudpanic.com
+[4]: http://github.com/lakshmivyas/hyde
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index e045f20..a6148c8 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,3 +1,12 @@
+Version 0.8.1
+=============
+
+Thanks to @rfk.
+
+* Updated to use nose 1.0
+* Bug fixes to Less CSS
+* PyFS publisher with mimetypes and etags support.
+
Version 0.8
==============
diff --git a/README.rst b/README.rst
index e31c48c..3bb8952 100644
--- a/README.rst
+++ b/README.rst
@@ -1,4 +1,4 @@
-Version 0.8
+Version 0.8.1
A brand new **hyde**
====================
@@ -112,8 +112,17 @@ Next Steps
- Text Compressor (CSS, JS, HTML) ✓
- Image optimizer ✓
+Links
+-----
+
+1. `Changelog`_
+2. `Authors`_
+
+
.. _hyde: https://github.com/lakshmivyas/hyde
.. _here: http://hyde.github.com
.. _Hyde Documentation: https://github.com/hyde/docs
.. _Cloudpanic: https://github.com/tipiirai/cloudpanic
-.. _Ringce: https://github.com/lakshmivyas/ringce/tree/v3.0
\ No newline at end of file
+.. _Ringce: https://github.com/lakshmivyas/ringce/tree/v3.0
+.. _Authors: https://github.com/hyde/hyde/blob/master/AUTHORS
+.. _Changelog: https://github.com/hyde/hyde/blob/master/CHANGELOG.rst
diff --git a/dev-req.txt b/dev-req.txt
index 210c297..4356410 100644
--- a/dev-req.txt
+++ b/dev-req.txt
@@ -9,7 +9,7 @@ Jinja2==2.5.5
pyquery==0.6.1
unittest2==0.5.1
mock==0.7.0b4
-nose==0.11.4
+nose==1.0.0
pep8==0.6.1
pylint==0.22.0
pysmell==0.7.3
diff --git a/hyde/ext/plugins/less.py b/hyde/ext/plugins/less.py
index 575072a..4acf6bb 100644
--- a/hyde/ext/plugins/less.py
+++ b/hyde/ext/plugins/less.py
@@ -34,7 +34,7 @@ class LessCSSPlugin(CLTransformer):
"""
if not resource.source_file.kind == 'less':
- return
+ return text
import_finder = re.compile(
'^\\s*@import\s+(?:\'|\")([^\'\"]*)(?:\'|\")\s*\;\s*$',
re.MULTILINE)
diff --git a/hyde/ext/publishers/pyfs.py b/hyde/ext/publishers/pyfs.py
new file mode 100644
index 0000000..9431afb
--- /dev/null
+++ b/hyde/ext/publishers/pyfs.py
@@ -0,0 +1,99 @@
+"""
+Contains classes and utilities that help publishing a hyde website to
+a filesystem using PyFilesystem FS objects.
+
+This publisher provides an easy way to publish to FTP, SFTP, WebDAV or other
+servers by specifying a PyFS filesystem URL. For example, the following
+are valid URLs that can be used with this publisher:
+
+ ftp://my.server.com/~username/my_blog/
+ dav:https://username:password@my.server.com/path/to/my/site
+
+"""
+
+import getpass
+import hashlib
+
+from hyde.fs import File, Folder
+from hyde.publisher import Publisher
+
+from hyde.util import getLoggerWithNullHandler
+logger = getLoggerWithNullHandler('hyde.ext.publishers.pyfs')
+
+
+from fs.osfs import OSFS
+from fs.path import pathjoin
+from fs.opener import fsopendir
+
+
+
+class PyFS(Publisher):
+
+ def initialize(self, settings):
+ self.settings = settings
+ self.url = settings.url
+ self.check_mtime = getattr(settings,"check_mtime",False)
+ self.check_etag = getattr(settings,"check_etag",False)
+ if self.check_etag and not isinstance(self.check_etag,basestring):
+ raise ValueError("check_etag must name the etag algorithm")
+ self.prompt_for_credentials()
+ self.fs = fsopendir(self.url)
+
+ def prompt_for_credentials(self):
+ credentials = {}
+ if "%(username)s" in self.url:
+ print "Username: ",
+ credentials["username"] = raw_input().strip()
+ if "%(password)s" in self.url:
+ credentials["password"] = getpass.getpass("Password: ")
+ if credentials:
+ self.url = self.url % credentials
+
+ def publish(self):
+ super(PyFS, self).publish()
+ deploy_fs = OSFS(self.site.config.deploy_root_path.path)
+ for (dirnm,local_filenms) in deploy_fs.walk():
+ logger.info("Making directory: %s",dirnm)
+ self.fs.makedir(dirnm,allow_recreate=True)
+ remote_fileinfos = self.fs.listdirinfo(dirnm,files_only=True)
+ # Process each local file, to see if it needs updating.
+ for filenm in local_filenms:
+ filepath = pathjoin(dirnm,filenm)
+ # Try to find an existing remote file, to compare metadata.
+ for (nm,info) in remote_fileinfos:
+ if nm == filenm:
+ break
+ else:
+ info = {}
+ # Skip it if the etags match
+ if self.check_etag and "etag" in info:
+ with deploy_fs.open(filepath,"rb") as f:
+ local_etag = self._calculate_etag(f)
+ if info["etag"] == local_etag:
+ logger.info("Skipping file [etag]: %s",filepath)
+ continue
+ # Skip it if the mtime is more recent remotely.
+ if self.check_mtime and "modified_time" in info:
+ local_mtime = deploy_fs.getinfo(filepath)["modified_time"]
+ if info["modified_time"] > local_mtime:
+ logger.info("Skipping file [mtime]: %s",filepath)
+ continue
+ # Upload it to the remote filesystem.
+ logger.info("Uploading file: %s",filepath)
+ with deploy_fs.open(filepath,"rb") as f:
+ self.fs.setcontents(filepath,f)
+ # Process each remote file, to see if it needs deleting.
+ for (filenm,info) in remote_fileinfos:
+ filepath = pathjoin(dirnm,filenm)
+ if filenm not in local_filenms:
+ logger.info("Removing file: %s",filepath)
+ self.fs.remove(filepath)
+
+ def _calculate_etag(self,f):
+ hasher = getattr(hashlib,self.check_etag.lower())()
+ data = f.read(1024*64)
+ while data:
+ hasher.update(data)
+ data = f.read(1024*64)
+ return hasher.hexdigest()
+
diff --git a/hyde/ext/templates/jinja.py b/hyde/ext/templates/jinja.py
index 5fa3f51..6e86e1a 100644
--- a/hyde/ext/templates/jinja.py
+++ b/hyde/ext/templates/jinja.py
@@ -131,6 +131,9 @@ def syntax(env, value, lexer=None, filename=None):
code = pygments.highlight(value, pyg, formatter)
code = code.replace('\n\n', '\n \n').replace('\n', '
')
caption = filename if filename else pyg.name
+ if hasattr(env.config, 'syntax'):
+ if not getattr(env.config.syntax, 'use_figure', True):
+ return Markup(code)
return Markup(
'\n\n'
% (code, caption))
diff --git a/hyde/model.py b/hyde/model.py
index f9e166d..1d52203 100644
--- a/hyde/model.py
+++ b/hyde/model.py
@@ -67,6 +67,10 @@ class Expando(object):
for k, v in d.iteritems():
if isinstance(v, Expando):
d[k] = v.to_dict()
+ elif isinstance(v, (tuple, list, set, frozenset)):
+ seq = type(v)
+ d[k] = seq(item.to_dict() if isinstance(item, Expando)
+ else item for item in v)
return d
diff --git a/hyde/tests/test_jinja2template.py b/hyde/tests/test_jinja2template.py
index f41ed93..734fd70 100644
--- a/hyde/tests/test_jinja2template.py
+++ b/hyde/tests/test_jinja2template.py
@@ -85,6 +85,7 @@ def test_typogrify():
"""
t = Jinja2Template(JINJA2.path)
t.configure(None)
+ t.env.filters['dateformat'] = dateformat
html = t.render(source, {}).strip()
assert html == u'One & two'
@@ -110,6 +111,7 @@ def test_spaceless():
"""
t = Jinja2Template(JINJA2.path)
t.configure(None)
+ t.env.filters['dateformat'] = dateformat
html = t.render(source, {}).strip()
expected = u"""