Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WCM-16: Implement setitem and changeProperties in FilesystemConnector, to help migrating zeit.web testcontent #1028

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/docs/changelog/WCM-16.change
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
WCM-16: Implement setitem and changeProperties in FilesystemConnector, to help migrating zeit.web testcontent
45 changes: 35 additions & 10 deletions core/src/zeit/connector/filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
import logging
import os
import os.path
import xml.sax.saxutils

import gocept.cache.property
import lxml.etree
import zope.app.file.image
import zope.interface

from zeit.connector.interfaces import ID_NAMESPACE, CannonicalId
from zeit.connector.interfaces import ID_NAMESPACE, CannonicalId, DeleteProperty
import zeit.cms.config
import zeit.cms.content.dav
import zeit.connector.converter
Expand Down Expand Up @@ -127,8 +128,9 @@ def _is_collection(self, id):
davtype = ('getcontenttype', 'DAV:')
return properties.get(davtype, '') == 'httpd/unix-directory'

def __setitem__(self, id, object):
raise NotImplementedError()
def __setitem__(self, id, resource):
resource.id = id
self.add(resource)

def __delitem__(self, id):
raise NotImplementedError()
Expand All @@ -140,8 +142,17 @@ def __contains__(self, id):
return False
return True

def add(self, object, verify_etag=True):
raise NotImplementedError()
def add(self, resource, verify_etag=True):
self._write_metadata_file(resource.id, resource.properties)

if resource.is_collection:
os.makedirs(self._path(resource.id), exist_ok=True)
else:
with self._get_file(resource.id, 'wb') as f:
while chunk := resource.data.read(self.WRITE_CHUNK_SIZE):
f.write(chunk)

WRITE_CHUNK_SIZE = 8 * 1024

def copy(self, old_id, new_id):
raise NotImplementedError()
Expand All @@ -150,7 +161,10 @@ def move(self, old_id, new_id):
raise NotImplementedError()

def changeProperties(self, id, properties):
raise NotImplementedError()
resource = self[id]
current = dict(resource.properties)
current.update(properties)
self._write_metadata_file(id, current)

def lock(self, id, principal, until):
raise NotImplementedError()
Expand Down Expand Up @@ -199,21 +213,21 @@ def _path(self, id):
path = id.replace(ID_NAMESPACE, '', 1).rstrip('/')
return os.path.join(self.repository_path, path).rstrip('/')

def _get_file(self, id):
def _get_file(self, id, mode='rb'):
filename = self._path(id)
__traceback_info__ = (id, filename)
try:
return open(filename, 'rb')
return open(filename, mode)
except IOError:
if os.path.isdir(filename):
raise ValueError('The path %r points to a directory.' % filename)
raise KeyError("The resource '%s' does not exist." % id)

def _get_metadata_file(self, id):
def _get_metadata_file(self, id, mode='rb'):
filename = self._path(id) + '.meta'
__traceback_info__ = (id, filename)
try:
return open(filename, 'rb')
return open(filename, mode)
except IOError:
if not id.endswith('.meta'):
return self._get_file(id)
Expand Down Expand Up @@ -284,6 +298,17 @@ def _get_lastmodified(self, id):
mtime = self.mtime(id)
return email.utils.formatdate(mtime, usegmt=True)

def _write_metadata_file(self, id, properties):
with self._get_metadata_file(id, 'w') as f:
f.write("<?xml version='1.0' encoding='UTF-8'?>\n")
f.write('<head>\n')
for (name, ns), value in properties.items():
if value is DeleteProperty or value is None:
continue
value = xml.sax.saxutils.escape(value)
f.write(f' <attribute ns="{ns}" name="{name}">{value}</attribute>\n')
f.write('</head>\n')


factory = Connector.factory

Expand Down