Skip to content
This repository has been archived by the owner on Nov 25, 2020. It is now read-only.

Commit

Permalink
Fixing no aiohttp and dynamic content
Browse files Browse the repository at this point in the history
  • Loading branch information
bloodbare committed May 10, 2020
1 parent 039f783 commit 749bfa4
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 54 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ share
pip-selfcheck.json
g.db
g.db.blobs
settings.json
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
6.0.0a1 (unreleased)
--------------------

- Fixing Dynamic Content
[bloodbare]

- @copy_mult @move_mult to allow to copy and move multiples and keep G endpoints
[bloodbare]

Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ build: ## Builds the environment
bin/pip install -r requirements.txt
bin/python setup.py develop
docker-compose create
docker-compose -f docker-compose-pg.yaml up postgres
docker-compose -f docker-compose.yaml up postgres
make populate

initdb: ## Create initial content in the DB
Expand All @@ -32,4 +32,4 @@ start-backend: ## Starts Guillotina
guillotina -c config-pg.yaml

start-dependencies: ## Starts dependencies (PG, ES, Redis)
docker-compose -f docker-compose-pg.yaml up postgres
docker-compose -f docker-compose.yaml up postgres
1 change: 1 addition & 0 deletions config-local.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jwt:
algorithm: HS256
jsapps:
+admin: guillotina:static/executioner
+manage: guillotina:static/gmi
cors:
allow_origin:
- "*"
Expand Down
62 changes: 33 additions & 29 deletions guillotina_cms/api/images.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from aiohttp.web import StreamResponse
from guillotina import app_settings
from guillotina import configure
from guillotina.api.files import DownloadFile
Expand All @@ -15,63 +14,68 @@


@configure.service(
context=IHasImage, method='GET', permission='guillotina.ViewContent',
name='@@images/{field_name}',
**_traversed_file_doc('Download the image'))
context=IHasImage,
method="GET",
permission="guillotina.ViewContent",
name="@@images/{field_name}",
**_traversed_file_doc("Download the image"),
)
class DownloadImageFile(DownloadFile):
pass


@configure.service(
context=IHasImage, method='GET', permission='guillotina.ViewContent',
name='@@images/{field_name}/{scale}',
**_traversed_file_doc('Download the image scale'))
context=IHasImage,
method="GET",
permission="guillotina.ViewContent",
name="@@images/{field_name}/{scale}",
**_traversed_file_doc("Download the image scale"),
)
class DownloadImageScale(TraversableFieldService):

async def __call__(self):
registry = await get_registry()
settings = registry.for_interface(IImagingSettings)
scale_name = self.request.matchdict['scale']
allowed_sizes = settings['allowed_sizes']
scale_name = self.request.matchdict["scale"]
allowed_sizes = settings["allowed_sizes"]
if scale_name not in allowed_sizes:
raise HTTPNotFound(content={
'reason': f'{scale_name} is not supported'
})
raise HTTPNotFound(content={"reason": f"{scale_name} is not supported"})
file = self.field.get(self.field.context or self.context)
if file is None:
raise HTTPNotFound(content={
'message': 'File or custom filename required to download'
})
raise HTTPNotFound(
content={"message": "File or custom filename required to download"}
)

adapter = get_multi_adapter(
(self.context, self.request, self.field), IFileManager)
data = b''
(self.context, self.request, self.field), IFileManager
)
data = b""
async for chunk in adapter.iter_data():
data += chunk

width, _, height = allowed_sizes[scale_name].partition(':')
width, _, height = allowed_sizes[scale_name].partition(":")

result, format_, size = scaleImage(
data, int(width), int(height),
quality=settings['quality'],
direction='thumbnail')
data,
int(width),
int(height),
quality=settings["quality"],
direction="thumbnail",
)

cors_renderer = app_settings['cors_renderer'](self.request)
cors_renderer = app_settings["cors_renderer"](self.request)
headers = await cors_renderer.get_headers()
headers.update({
'CONTENT-DISPOSITION': 'attachment; filename="{}"'.format(
file.filename)
})
headers.update(
{"CONTENT-DISPOSITION": 'attachment; filename="{}"'.format(file.filename)}
)

download_resp = Response(
status=200,
headers=headers,
content_type=f"image/{format_}",
content_length=len(result)
content_length=len(result),
)
await download_resp.prepare(self.request)

await download_resp.write(result)
await download_resp.write(eof=True)
return download_resp

50 changes: 29 additions & 21 deletions guillotina_cms/api/ws_edit.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from aiohttp import web
from diff_match_patch import diff_match_patch
from guillotina import configure
from guillotina.api.service import Service
Expand All @@ -10,7 +9,7 @@
from guillotina.interfaces import IPubSubUtility
from guillotina.component import get_utility

import aiohttp
import orjson
import asyncio
import json
import logging
Expand Down Expand Up @@ -85,15 +84,15 @@ class WSEdit(Service):
auto_save_delay = 30
auto_save_handle = None


async def __call__(self):
self.data = {}
self.ws = web.WebSocketResponse()

tm = get_tm(self.request)
await tm.abort(self.request)
tm = get_tm()
await tm.abort()
self.ws = self.request.get_ws()

try:
await self.ws.prepare(self.request)
await self.ws.prepare()
except ConnectionResetError:
return {}

Expand All @@ -109,19 +108,28 @@ async def __call__(self):
self.configure_auto_save()

async for msg in self.ws:
if msg.type == aiohttp.WSMsgType.text:
await self.handle_message(msg)
elif msg.type == aiohttp.WSMsgType.error:
logger.debug(
"resource ws connection closed with exception {0:s}".format(self.ws.exception())
)
try:
message = msg.json
except WebSocketJsonDecodeError:
# We only care about json messages
logger.warning("Invalid websocket payload, ignored: {}".format(msg))
continue

if message["op"].lower() == "close":
break

try:
await self.handle_message(message)
except Exception:
logger.error("Exception on ws", exc_info=True)

except asyncio.CancelledError:
logger.debug("browser closed")
pass
finally:
try:
await self.pubsub.unsubscribe(self.channel_name, self.request.uid)
await self.ws.close() # make sure to close socket
await ws.close() # make sure to close socket
except Exception:
pass

Expand Down Expand Up @@ -152,21 +160,21 @@ async def handle_message(self, msg):
pass
else:
try:
data = json.loads(msg.data)
data = orjson.loads(msg.data)
operation = data["t"]
except Exception:
self.ws.send_str(json.dumps({"t": "e", "v": "Not a valid payload"}))
self.ws.send_bytes(orjson.dumps({"t": "e", "v": "Not a valid payload"}))
return

if operation == "dmp":
try:
await self.apply_edit(data)
except Exception:
await self.ws.send_str(json.dumps({"t": "e", "v": "Error applying dmp"}))
await self.ws.send_bytes(orjson.dumps({"t": "e", "v": "Error applying dmp"}))
logger.warn("Error applying dmp", exc_info=True)
elif operation == "save":
await self.save()
self.ws.send_str(json.dumps({"t": "saved"}))
self.ws.send_bytes(orjson.dumps({"t": "saved"}))
await self.pubsub.publish(self.channel_name, {"t": "saved", "ruid": self.request.uid})
elif operation == "saved":
# reset the counter, only one person needs to save it every 30 seconds
Expand All @@ -181,11 +189,11 @@ async def get_field(self, field_name):
if "." in field_name:
schema_klass, field_name = field_name.rsplit(".", 1)
if schema_klass not in self.context.__behaviors__:
self.ws.send_str(json.dumps({"t": "e", "v": "Not a valid field on a behavior"}))
self.ws.send_bytes(json.dumps({"t": "e", "v": "Not a valid field on a behavior"}))
return
schema = resolve_dotted_name(schema_klass)
if schema is None:
self.ws.send_str(json.dumps({"t": "e", "v": "Could not find specified schema"}))
self.ws.send_bytes(json.dumps({"t": "e", "v": "Could not find specified schema"}))
return
behavior = schema(context)
context = behavior
Expand All @@ -196,7 +204,7 @@ async def get_field(self, field_name):
try:
field = schema[field_name]
except KeyError:
self.ws.send_str(json.dumps({"t": "e", "v": "Not a valid field on a behavior"}))
self.ws.send_bytes(json.dumps({"t": "e", "v": "Not a valid field on a behavior"}))
return
return context, field

Expand Down
Empty file.
6 changes: 4 additions & 2 deletions guillotina_cms/subscribers/dynamic.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from guillotina import app_settings
from guillotina_cms.vocabularies.source import AppSettingSource
from guillotina_cms.directives import fieldset_field
from guillotina_cms import dyncontent
from guillotina import configure
from guillotina import FACTORY_CACHE
from guillotina import BEHAVIOR_CACHE
Expand Down Expand Up @@ -154,7 +155,8 @@ def create_content_factory(proto_name, proto_definition):
(parent_class,),
{})

klass.__module__ = 'guillotina_cms.content'
klass.__module__ = 'guillotina_cms.dyncontent'
setattr(dyncontent, proto_name, klass)

behaviors = []
for bhr in proto_definition.get('behaviors', []):
Expand All @@ -180,7 +182,7 @@ def create_content_factory(proto_name, proto_definition):

root = get_utility(IApplication, name='root')
configure.load_configuration(
root.app.config, 'guillotina_cms.content', 'contenttype')
root.app.config, 'guillotina_cms.dyncontent', 'contenttype')
root.app.config.execute_actions()
configure.clear()
load_cached_schema()
Expand Down

0 comments on commit 749bfa4

Please sign in to comment.