Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/sugar_network
diff options
context:
space:
mode:
authorAleksey Lim <alsroot@sugarlabs.org>2014-02-03 10:08:50 (GMT)
committer Aleksey Lim <alsroot@sugarlabs.org>2014-02-03 10:08:50 (GMT)
commitb4b008c1f302059221a1a43ed237e6d562ec7f97 (patch)
tree1c8b4dd71d6c165c461bbefe0847154ea333b91b /sugar_network
parent726ac1d66321ee7ac6b4cc7eff17001b5aeb6c5e (diff)
Keep Post comments in aggregated property
Diffstat (limited to 'sugar_network')
-rw-r--r--sugar_network/db/__init__.py2
-rw-r--r--sugar_network/db/directory.py29
-rw-r--r--sugar_network/db/index.py15
-rw-r--r--sugar_network/db/metadata.py8
-rw-r--r--sugar_network/db/routes.py50
-rw-r--r--sugar_network/model/post.py9
-rw-r--r--sugar_network/node/routes.py25
-rw-r--r--sugar_network/toolkit/router.py16
8 files changed, 125 insertions, 29 deletions
diff --git a/sugar_network/db/__init__.py b/sugar_network/db/__init__.py
index b6bde81..2f22a36 100644
--- a/sugar_network/db/__init__.py
+++ b/sugar_network/db/__init__.py
@@ -351,7 +351,7 @@ Volume
from sugar_network.db.metadata import \
indexed_property, stored_property, blob_property, \
- Property, StoredProperty, BlobProperty, IndexedProperty
+ Property, StoredProperty, BlobProperty, IndexedProperty, AggregatedType
from sugar_network.db.index import index_flush_timeout, \
index_flush_threshold, index_write_queue
from sugar_network.db.resource import Resource
diff --git a/sugar_network/db/directory.py b/sugar_network/db/directory.py
index 96a2923..944f73a 100644
--- a/sugar_network/db/directory.py
+++ b/sugar_network/db/directory.py
@@ -24,6 +24,7 @@ from sugar_network.toolkit.router import ACL
from sugar_network.db.storage import Storage
from sugar_network.db.metadata import BlobProperty, Metadata, GUID_PREFIX
from sugar_network.db.metadata import IndexedProperty, StoredProperty
+from sugar_network.db.metadata import AggregatedType
from sugar_network.toolkit import http, exception, enforce
@@ -278,9 +279,16 @@ class Directory(object):
if isinstance(prop, BlobProperty):
del meta['seqno']
else:
- meta = {'mtime': meta['mtime'],
- 'value': meta.get('value'),
- }
+ value = meta.get('value')
+ if prop.typecast is AggregatedType:
+ value_ = {}
+ for key, agg in value.items():
+ aggseqno = agg.pop('seqno')
+ if aggseqno >= start and \
+ (not end or aggseqno <= end):
+ value_[key] = agg
+ value = value_
+ meta = {'mtime': meta['mtime'], 'value': value}
yield name, meta, seqno
yield doc.guid, patch()
@@ -303,6 +311,12 @@ class Directory(object):
else:
meta['seqno'] = (orig_meta or {}).get('seqno') or 0
meta.update(kwargs)
+ if self.metadata.get(prop).typecast is AggregatedType:
+ for agg in meta['value'].values():
+ agg['seqno'] = meta['seqno']
+ if orig_meta:
+ orig_meta['value'].update(meta['value'])
+ meta['value'] = orig_meta['value']
merge[prop] = meta
if op is not None:
patch[prop] = meta.get('value')
@@ -365,7 +379,14 @@ class Directory(object):
value = meta['value']
changes[name] = prop.default if value is None else value
else:
- if prop.localized:
+ if prop.typecast is AggregatedType:
+ for aggvalue in value.values():
+ aggvalue['seqno'] = seqno
+ if existed:
+ value_ = record.get(name)['value']
+ value_.update(value)
+ value = value_
+ elif prop.localized:
if not isinstance(value, dict):
value = {toolkit.default_lang(): value}
if existed and \
diff --git a/sugar_network/db/index.py b/sugar_network/db/index.py
index aa75f80..7ff43bb 100644
--- a/sugar_network/db/index.py
+++ b/sugar_network/db/index.py
@@ -349,9 +349,9 @@ class IndexWriter(IndexReader):
_logger.debug('Index %r object: %r', self.metadata.name, properties)
- document = xapian.Document()
+ doc = xapian.Document()
term_generator = xapian.TermGenerator()
- term_generator.set_document(document)
+ term_generator.set_document(doc)
for name, prop in self._props.items():
value = guid \
@@ -366,21 +366,20 @@ class IndexWriter(IndexReader):
slotted_value = toolkit.gettext(value) or ''
else:
slotted_value = next(_fmt_prop_value(prop, value))
- document.add_value(prop.slot, slotted_value)
+ doc.add_value(prop.slot, slotted_value)
if prop.prefix or prop.full_text:
for value_ in _fmt_prop_value(prop, value):
if prop.prefix:
if prop.boolean:
- document.add_boolean_term(
- _term(prop.prefix, value_))
+ doc.add_boolean_term(_term(prop.prefix, value_))
else:
- document.add_term(_term(prop.prefix, value_))
+ doc.add_term(_term(prop.prefix, value_))
if prop.full_text:
term_generator.index_text(value_, 1, prop.prefix or '')
term_generator.increase_termpos()
- self._db.replace_document(_term(GUID_PREFIX, guid), document)
+ self._db.replace_document(_term(GUID_PREFIX, guid), doc)
self._pending_updates += 1
if post_cb is not None:
@@ -475,7 +474,7 @@ def _fmt_prop_value(prop, value):
for i in value:
for j in fmt(i):
yield j
- else:
+ elif value is not None:
yield str(value)
return fmt(value if prop.fmt is None else prop.fmt(value))
diff --git a/sugar_network/db/metadata.py b/sugar_network/db/metadata.py
index 7cba5ce..55942a7 100644
--- a/sugar_network/db/metadata.py
+++ b/sugar_network/db/metadata.py
@@ -58,6 +58,10 @@ stored_property = lambda ** kwargs: indexed_property(StoredProperty, **kwargs)
blob_property = lambda ** kwargs: indexed_property(BlobProperty, **kwargs)
+class AggregatedType(dict):
+ pass
+
+
class Metadata(dict):
"""Structure to describe the document.
@@ -107,8 +111,8 @@ class Metadata(dict):
return self._name
def __getitem__(self, prop_name):
- enforce(prop_name in self, 'There is no %r property in %r',
- prop_name, self.name)
+ enforce(prop_name in self, http.NotFound,
+ 'There is no %r property in %r', prop_name, self.name)
return dict.__getitem__(self, prop_name)
diff --git a/sugar_network/db/routes.py b/sugar_network/db/routes.py
index 123e001..19ad26c 100644
--- a/sugar_network/db/routes.py
+++ b/sugar_network/db/routes.py
@@ -25,6 +25,7 @@ from contextlib import contextmanager
from os.path import exists
from sugar_network import toolkit
+from sugar_network.db.metadata import AggregatedType
from sugar_network.db.metadata import BlobProperty, StoredProperty, LIST_TYPES
from sugar_network.toolkit.router import Blob, ACL, route
from sugar_network.toolkit import http, enforce
@@ -114,6 +115,52 @@ class Routes(object):
def get_prop_meta(self, request, response):
self._prop_meta(request, response)
+ @route('POST', [None, None, None],
+ acl=ACL.AUTH, mime_type='application/json')
+ def insert_to_aggprop(self, request):
+ content = request.content or {}
+ enforce(isinstance(content, dict), http.BadRequest, 'Invalid value')
+
+ directory = self.volume[request.resource]
+ prop = directory.metadata[request.prop]
+
+ enforce(prop.typecast is AggregatedType, http.BadRequest,
+ 'Property is not aggregated')
+ prop.assert_access(ACL.INSERT)
+ self.on_aggprop_update(request, prop, None)
+
+ if request.principal:
+ authors = content['author'] = {}
+ self._useradd(authors, request.principal, ACL.ORIGINAL)
+ guid = content.pop('guid') if 'guid' in content else toolkit.uuid()
+ props = {request.prop: {guid: content}}
+ event = {}
+ self.on_update(request, props, event)
+ directory.update(request.guid, props, event)
+
+ return guid
+
+ @route('DELETE', [None, None, None, None],
+ acl=ACL.AUTH, mime_type='application/json')
+ def remove_from_aggprop(self, request):
+ directory = self.volume[request.resource]
+ doc = directory.get(request.guid)
+ prop = directory.metadata[request.prop]
+
+ enforce(prop.typecast is AggregatedType, http.BadRequest,
+ 'Property is not aggregated')
+ prop.assert_access(ACL.REMOVE)
+
+ guid = request.path[3]
+ enforce(guid in doc[request.prop], http.NotFound,
+ 'No such aggregated item')
+ self.on_aggprop_update(request, prop, doc[request.prop][guid])
+
+ props = {request.prop: {guid: {}}}
+ event = {}
+ self.on_update(request, props, event)
+ directory.update(request.guid, props, event)
+
@route('PUT', [None, None], cmd='useradd',
arguments={'role': 0}, acl=ACL.AUTH | ACL.AUTHOR)
def useradd(self, request, user, role):
@@ -145,6 +192,9 @@ class Routes(object):
def on_update(self, request, props, event):
props['mtime'] = int(time.time())
+ def on_aggprop_update(self, request, prop, value):
+ pass
+
def after_post(self, doc):
pass
diff --git a/sugar_network/model/post.py b/sugar_network/model/post.py
index 602ad02..fda366f 100644
--- a/sugar_network/model/post.py
+++ b/sugar_network/model/post.py
@@ -45,7 +45,7 @@ class Post(db.Resource):
def title(self, value):
return value
- @db.indexed_property(prefix='D', full_text=True, localized=True,
+ @db.indexed_property(prefix='M', full_text=True, localized=True,
acl=ACL.CREATE | ACL.READ)
def message(self, value):
return value
@@ -59,6 +59,13 @@ class Post(db.Resource):
def vote(self, value):
return value
+ @db.indexed_property(prefix='D', typecast=db.AggregatedType,
+ full_text=True, default=db.AggregatedType(),
+ fmt=lambda x: [i.get('message') for i in x.values()],
+ acl=ACL.READ | ACL.INSERT | ACL.REMOVE)
+ def comments(self, value):
+ return value
+
@db.blob_property(mime_type='image/png')
def preview(self, value):
if value:
diff --git a/sugar_network/node/routes.py b/sugar_network/node/routes.py
index 4bc97e4..eb48c70 100644
--- a/sugar_network/node/routes.py
+++ b/sugar_network/node/routes.py
@@ -271,13 +271,7 @@ class NodeRoutes(model.VolumeRoutes, model.FrontRoutes):
request.principal = request.authorization.login
if op.acl & ACL.AUTHOR and request.guid:
- if request.resource == 'user':
- allowed = (request.principal == request.guid)
- else:
- doc = self.volume[request.resource].get(request.guid)
- allowed = (request.principal in doc['author'])
- enforce(allowed or self.authorize(request.principal, 'root'),
- http.Forbidden, 'Operation is permitted only for authors')
+ self._enforce_authority(request)
if op.acl & ACL.SUPERUSER:
enforce(self.authorize(request.principal, 'root'), http.Forbidden,
@@ -300,6 +294,12 @@ class NodeRoutes(model.VolumeRoutes, model.FrontRoutes):
if 'deleted' in props.get('layer', []):
event['event'] = 'delete'
+ def on_aggprop_update(self, request, prop, value):
+ if prop.acl & ACL.AUTHOR:
+ self._enforce_authority(request)
+ elif value is not None:
+ self._enforce_authority(request, value.get('author'))
+
def find(self, request, reply):
limit = request.get('limit')
if limit is None or limit < 0:
@@ -402,6 +402,17 @@ class NodeRoutes(model.VolumeRoutes, model.FrontRoutes):
del data[key]
return result
+ def _enforce_authority(self, request, author=None):
+ if request.resource == 'user':
+ allowed = (request.principal == request.guid)
+ else:
+ if author is None:
+ doc = self.volume[request.resource].get(request.guid)
+ author = doc['author']
+ allowed = request.principal in author
+ enforce(allowed or self.authorize(request.principal, 'root'),
+ http.Forbidden, 'Operation is permitted only for authors')
+
def generate_node_stats(volume, path):
tmp_path = toolkit.mkdtemp()
diff --git a/sugar_network/toolkit/router.py b/sugar_network/toolkit/router.py
index 9a430b7..df57ff3 100644
--- a/sugar_network/toolkit/router.py
+++ b/sugar_network/toolkit/router.py
@@ -82,20 +82,24 @@ class ACL(object):
WRITE = 1 << 3
READ = 1 << 4
DELETE = 1 << 5
- PUBLIC = CREATE | WRITE | READ | DELETE
+ INSERT = 1 << 6
+ REMOVE = 1 << 7
+ PUBLIC = CREATE | WRITE | READ | DELETE | INSERT | REMOVE
- AUTH = 1 << 6
- AUTHOR = 1 << 7
- SUPERUSER = 1 << 8
+ AUTH = 1 << 8
+ AUTHOR = 1 << 9
+ SUPERUSER = 1 << 10
- LOCAL = 1 << 9
- CALC = 1 << 10
+ LOCAL = 1 << 11
+ CALC = 1 << 12
NAMES = {
CREATE: 'Create',
WRITE: 'Write',
READ: 'Read',
DELETE: 'Delete',
+ INSERT: 'Insert',
+ REMOVE: 'Remove',
}