summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--service/pixelated/config/app_factory.py35
-rw-r--r--service/pixelated/config/routes.py24
-rw-r--r--service/pixelated/controllers/home_controller.py42
-rw-r--r--service/pixelated/resources/__init__.py (renamed from service/pixelated/controllers/__init__.py)8
-rw-r--r--service/pixelated/resources/attachments_resource.py (renamed from service/pixelated/controllers/attachments_controller.py)30
-rw-r--r--service/pixelated/resources/contacts_resource.py (renamed from service/pixelated/controllers/contacts_controller.py)10
-rw-r--r--service/pixelated/resources/features_resource.py (renamed from service/pixelated/controllers/features_controller.py)10
-rw-r--r--service/pixelated/resources/mail_resource.py64
-rw-r--r--service/pixelated/resources/mails_resource.py (renamed from service/pixelated/controllers/mails_controller.py)132
-rw-r--r--service/pixelated/resources/root_resource.py46
-rw-r--r--service/pixelated/resources/sync_info_resource.py (renamed from service/pixelated/controllers/sync_info_controller.py)11
-rw-r--r--service/pixelated/resources/tags_resource.py (renamed from service/pixelated/controllers/tags_controller.py)13
-rw-r--r--service/pixelated/runserver.py13
-rw-r--r--service/setup.py4
-rw-r--r--service/test/functional/features/environment.py2
-rw-r--r--service/test/support/integration/app_test_client.py2
-rw-r--r--service/test/support/integration/soledad_test_base.py2
-rw-r--r--service/test/unit/controllers/mails_controller_test.py2
-rw-r--r--service/test/unit/controllers/sync_info_controller_test.py2
-rw-r--r--web-ui/app/index.html32
-rw-r--r--web-ui/app/js/main.js2
-rw-r--r--web-ui/app/js/views/i18n.js2
-rw-r--r--web-ui/app/scss/news-cycle.scss4
-rw-r--r--web-ui/app/scss/opensans.scss20
24 files changed, 288 insertions, 224 deletions
diff --git a/service/pixelated/config/app_factory.py b/service/pixelated/config/app_factory.py
index 745db937..12830743 100644
--- a/service/pixelated/config/app_factory.py
+++ b/service/pixelated/config/app_factory.py
@@ -19,9 +19,9 @@ from OpenSSL import SSL
from OpenSSL import crypto
from twisted.internet import reactor
from twisted.internet import ssl
+from pixelated.resources.root_resource import RootResource
from twisted.web import resource
from twisted.web.util import redirectTo
-from pixelated.config.routes import setup_routes
from pixelated.adapter.services.mail_service import MailService
from pixelated.adapter.model.mail import InputMail
from pixelated.adapter.services.mail_sender import MailSender
@@ -33,7 +33,6 @@ from pixelated.adapter.listeners.mailbox_indexer_listener import MailboxIndexerL
import pixelated.bitmask_libraries.session as LeapSession
from pixelated.bitmask_libraries.leap_srp import LeapAuthException
from requests.exceptions import ConnectionError
-from pixelated.controllers import *
from pixelated.adapter.services.tag_service import TagService
from leap.common.events import (
register,
@@ -100,20 +99,16 @@ def init_app(app, leap_home):
MailboxIndexerListener.SEARCH_ENGINE = search_engine
InputMail.FROM_EMAIL_ADDRESS = leap_session.account_email()
- home_controller = HomeController()
- features_controller = FeaturesController()
- mails_controller = MailsController(mail_service=mail_service,
- draft_service=draft_service,
- search_engine=search_engine)
- tags_controller = TagsController(search_engine=search_engine)
- contacts_controller = ContactsController(search_engine=search_engine)
- sync_info_controller = SyncInfoController()
- attachments_controller = AttachmentsController(soledad_querier)
-
- register(signal=proto.SOLEDAD_SYNC_RECEIVE_STATUS,
- callback=update_info_sync_and_index_partial(sync_info_controller=sync_info_controller,
- search_engine=search_engine,
- mail_service=mail_service))
+ app.resource.initialize(soledad_querier, search_engine, mail_service, draft_service)
+
+ # add root to reactor
+
+ # register(signal=proto.SOLEDAD_SYNC_RECEIVE_STATUS,
+ # callback=update_info_sync_and_index_partial(sync_info_controller=sync_info_controller,
+ # search_engine=search_engine,
+ # mail_service=mail_service))
+
+
register(signal=proto.SOLEDAD_DONE_DATA_SYNC,
callback=init_index_and_remove_dupes(querier=soledad_querier,
search_engine=search_engine,
@@ -122,11 +117,9 @@ def init_app(app, leap_home):
register(signal=proto.SOLEDAD_DONE_DATA_SYNC, uid=CREATE_KEYS_IF_KEYS_DONT_EXISTS_CALLBACK,
callback=look_for_user_key_and_create_if_cant_find(leap_session))
- setup_routes(app, home_controller, mails_controller, tags_controller, features_controller,
- sync_info_controller, attachments_controller, contacts_controller)
-
def create_app(app, args):
+ app.resource = RootResource()
if args.sslkey and args.sslcert:
listen_with_ssl(app, args)
else:
@@ -136,7 +129,7 @@ def create_app(app, args):
def listen_without_ssl(app, args):
- reactor.listenTCP(args.port, Site(app.resource()), interface=args.host)
+ reactor.listenTCP(args.port, Site(app.resource), interface=args.host)
def _ssl_options(args):
@@ -152,7 +145,7 @@ def _ssl_options(args):
def listen_with_ssl(app, args):
- reactor.listenSSL(args.port, Site(app.resource()), _ssl_options(args), interface=args.host)
+ reactor.listenSSL(args.port, Site(app.resource), _ssl_options(args), interface=args.host)
return reactor
diff --git a/service/pixelated/config/routes.py b/service/pixelated/config/routes.py
deleted file mode 100644
index 5efbbb28..00000000
--- a/service/pixelated/config/routes.py
+++ /dev/null
@@ -1,24 +0,0 @@
-def setup_routes(app, home_controller, mails_controller, tags_controller, features_controller, sync_info_controller,
- attachments_controller, contacts_controller):
- # mails
- app.route('/mails', methods=['GET'])(mails_controller.mails)
- app.route('/mails/unread', methods=['POST'])(mails_controller.mark_many_mail_unread)
- app.route('/mails/read', methods=['POST'])(mails_controller.mark_many_mail_read)
- app.route('/mail/<mail_id>', methods=['GET'])(mails_controller.mail)
- app.route('/mail/<mail_id>', methods=['DELETE'])(mails_controller.delete_mail)
- app.route('/mails/delete', methods=['POST'])(mails_controller.delete_mails)
- app.route('/mails', methods=['POST'])(mails_controller.send_mail)
- app.route('/mail/<mail_id>/tags', methods=['POST'])(mails_controller.mail_tags)
- app.route('/mails', methods=['PUT'])(mails_controller.update_draft)
- # tags
- app.route('/tags', methods=['GET'])(tags_controller.tags)
- # contacts
- app.route('/contacts', methods=['GET'])(contacts_controller.contacts)
- # features
- app.route('/features', methods=['GET'])(features_controller.features)
- # sync info
- app.route('/sync_info', methods=['GET'])(sync_info_controller.sync_info)
- # attachments
- app.route('/attachment/<attachment_id>', methods=['GET'])(attachments_controller.attachment)
- # static
- app.route('/', methods=['GET'], branch=True)(home_controller.home)
diff --git a/service/pixelated/controllers/home_controller.py b/service/pixelated/controllers/home_controller.py
deleted file mode 100644
index ccdad197..00000000
--- a/service/pixelated/controllers/home_controller.py
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# Copyright (c) 2014 ThoughtWorks, Inc.
-#
-# Pixelated is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Pixelated is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
-import os
-
-from twisted.web.static import File
-
-
-class HomeController:
- def __init__(self):
- self.static_folder = self._get_static_folder()
- pass
-
- def _get_static_folder(self):
-
- static_folder = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "..", "web-ui", "app"))
- # this is a workaround for packaging
- if not os.path.exists(static_folder):
- static_folder = os.path.abspath(
- os.path.join(os.path.abspath(__file__), "..", "..", "..", "..", "web-ui", "app"))
- if not os.path.exists(static_folder):
- static_folder = os.path.join('/', 'usr', 'share', 'pixelated-user-agent')
- return static_folder
-
- def home(self, request):
- request_type = request.requestHeaders.getRawHeaders('accept')[0].split(',')[0]
- response_type = request_type if request_type else "text/html"
-
- request.setHeader('Content-Type', response_type)
- return File('%s/' % self.static_folder, defaultType=response_type)
diff --git a/service/pixelated/controllers/__init__.py b/service/pixelated/resources/__init__.py
index 6bc8e7c2..a2e4c9d4 100644
--- a/service/pixelated/controllers/__init__.py
+++ b/service/pixelated/resources/__init__.py
@@ -31,11 +31,3 @@ def respond_json_deferred(entity, request, status_code=200):
import json
-
-from home_controller import HomeController
-from mails_controller import MailsController
-from tags_controller import TagsController
-from features_controller import FeaturesController
-from sync_info_controller import SyncInfoController
-from attachments_controller import AttachmentsController
-from contacts_controller import ContactsController
diff --git a/service/pixelated/controllers/attachments_controller.py b/service/pixelated/resources/attachments_resource.py
index b3fed903..0ab214b9 100644
--- a/service/pixelated/controllers/attachments_controller.py
+++ b/service/pixelated/resources/attachments_resource.py
@@ -19,31 +19,45 @@ import io
import re
from twisted.protocols.basic import FileSender
from twisted.python.log import err
+from twisted.web.resource import Resource
-class AttachmentsController:
-
- def __init__(self, querier):
+class AttachmentResource(Resource):
+ def __init__(self, attachment_id, querier):
+ Resource.__init__(self)
+ self.attachment_id = attachment_id
self.querier = querier
- def attachment(self, request, attachment_id):
+ def render_GET(self, request):
encoding = request.args.get('encoding', [None])[0]
- filename = request.args.get('filename', [attachment_id])[0]
- attachment = self.querier.attachment(attachment_id, encoding)
+ filename = request.args.get('filename', [self.attachment_id])[0]
+ attachment = self.querier.attachment(self.attachment_id, encoding)
request.setHeader(b'Content-Type', b'application/force-download')
request.setHeader(b'Content-Disposition', bytes('attachment; filename=' + filename))
bytes_io = io.BytesIO(attachment['content'])
d = FileSender().beginFileTransfer(bytes_io, request)
- def cbFinished(ignored):
+ def cb_finished(_):
bytes_io.close()
request.finish()
- d.addErrback(err).addCallback(cbFinished)
+ d.addErrback(err).addCallback(cb_finished)
return d
def _extract_mimetype(self, content_type):
match = re.compile('([A-Za-z-]+\/[A-Za-z-]+)').search(content_type)
return match.group(1)
+
+
+class AttachmentsResource(Resource):
+
+ isLeaf = True
+
+ def __init__(self, querier):
+ Resource.__init__(self)
+ self.querier = querier
+
+ def getChild(self, attachment_id, request):
+ return AttachmentResource(attachment_id, self.querier)
diff --git a/service/pixelated/controllers/contacts_controller.py b/service/pixelated/resources/contacts_resource.py
index 5825b563..94468a63 100644
--- a/service/pixelated/controllers/contacts_controller.py
+++ b/service/pixelated/resources/contacts_resource.py
@@ -14,16 +14,20 @@
# You should have received a copy of the GNU Affero General Public License
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
-from pixelated.controllers import respond_json_deferred
+from pixelated.resources import respond_json_deferred
from twisted.internet.threads import deferToThread
+from twisted.web.resource import Resource
-class ContactsController:
+class ContactsResource(Resource):
+
+ isLeaf = True
def __init__(self, search_engine):
+ Resource.__init__(self)
self._search_engine = search_engine
- def contacts(self, request):
+ def render_GET(self, request):
query = request.args.get('q', [''])[0]
d = deferToThread(lambda: self._search_engine.contacts(query))
d.addCallback(lambda tags: respond_json_deferred(tags, request))
diff --git a/service/pixelated/controllers/features_controller.py b/service/pixelated/resources/features_resource.py
index b91aa183..1784e463 100644
--- a/service/pixelated/controllers/features_controller.py
+++ b/service/pixelated/resources/features_resource.py
@@ -14,17 +14,17 @@
# You should have received a copy of the GNU Affero General Public License
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
-from pixelated.controllers import respond_json
+from pixelated.resources import respond_json
import os
+from twisted.web.resource import Resource
-class FeaturesController:
+class FeaturesResource(Resource):
DISABLED_FEATURES = ['draftReply', 'encryptionStatus']
- def __init__(self):
- pass
+ isLeaf = True
- def features(self, request):
+ def render_GET(self, request):
try:
disabled_features = {'logout': os.environ['DISPATCHER_LOGOUT_URL']}
except KeyError:
diff --git a/service/pixelated/resources/mail_resource.py b/service/pixelated/resources/mail_resource.py
new file mode 100644
index 00000000..03873ffb
--- /dev/null
+++ b/service/pixelated/resources/mail_resource.py
@@ -0,0 +1,64 @@
+import json
+from pixelated.resources import respond_json
+from twisted.web.resource import Resource
+
+
+class MailTags(Resource):
+
+ isLeaf = True
+
+ def __init__(self, mail_id, mail_service, search_engine):
+ Resource.__init__(self)
+ self._search_engine = search_engine
+ self._mail_service = mail_service
+ self._mail_id = mail_id
+
+ def render_POST(self, request):
+ content_dict = json.loads(request.content.read())
+ new_tags = map(lambda tag: tag.lower(), content_dict['newtags'])
+ try:
+ self._mail_service.update_tags(self._mail_id, new_tags)
+ mail = self._mail_service.mail(self._mail_id)
+ self._search_engine.index_mail(mail)
+ except ValueError as ve:
+ return respond_json(ve.message, request, 403)
+ return respond_json(mail.as_dict(), request)
+
+
+class Mail(Resource):
+
+ def __init__(self, mail_id, mail_service, search_engine):
+ Resource.__init__(self)
+ self.putChild('tags', MailTags(mail_id, mail_service, search_engine))
+
+ self._search_engine = search_engine
+ self._mail_id = mail_id
+ self._mail_service = mail_service
+
+ def render_GET(self, request):
+ mail = self._mail_service.mail(self._mail_id)
+ return respond_json(mail.as_dict(), request)
+
+ def render_DELETE(self, request):
+ self._delete_mail(self._mail_id)
+ return respond_json(None, request)
+
+ def _delete_mail(self, mail_id):
+ mail = self._mail_service.mail(mail_id)
+ if mail.mailbox_name == 'TRASH':
+ self._mail_service.delete_permanent(mail_id)
+ self._search_engine.remove_from_index(mail_id)
+ else:
+ trashed_mail = self._mail_service.delete_mail(mail_id)
+ self._search_engine.index_mail(trashed_mail)
+
+
+class MailResource(Resource):
+
+ def __init__(self, mail_service, search_engine):
+ Resource.__init__(self)
+ self._mail_service = mail_service
+ self._search_engine = search_engine
+
+ def getChild(self, mail_id, request):
+ return Mail(mail_id, self._mail_service, self._search_engine)
diff --git a/service/pixelated/controllers/mails_controller.py b/service/pixelated/resources/mails_resource.py
index fb9c9adc..75c73349 100644
--- a/service/pixelated/controllers/mails_controller.py
+++ b/service/pixelated/resources/mails_resource.py
@@ -1,50 +1,24 @@
-#
-# Copyright (c) 2014 ThoughtWorks, Inc.
-#
-# Pixelated is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Pixelated is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
-
import json
-
from pixelated.adapter.model.mail import InputMail
-from pixelated.controllers import respond_json
+from pixelated.resources import respond_json
+from twisted.web.resource import Resource
-class MailsController:
-
- def __init__(self, mail_service, draft_service, search_engine):
- self._mail_service = mail_service
- self._draft_service = draft_service
- self._search_engine = search_engine
+def _format_exception(e):
+ exception_info = map(str, list(e.args))
+ return '\n'.join(exception_info)
- def mails(self, request):
- mail_ids, total = self._search_engine.search(request.args.get('q')[0], request.args.get('w')[0], request.args.get('p')[0])
- mails = self._mail_service.mails(mail_ids)
- response = {
- "stats": {
- "total": total,
- },
- "mails": [mail.as_dict() for mail in mails]
- }
+class MailsUnreadResource(Resource):
- return json.dumps(response)
+ isLeaf = True
- def mail(self, request, mail_id):
- mail = self._mail_service.mail(mail_id)
- return respond_json(mail.as_dict(), request)
+ def __init__(self, mail_service, search_engine):
+ Resource.__init__(self)
+ self._search_engine = search_engine
+ self._mail_service = mail_service
- def mark_many_mail_unread(self, request):
+ def render_POST(self, request):
content_dict = json.load(request.content)
idents = content_dict.get('idents')
for ident in idents:
@@ -52,7 +26,17 @@ class MailsController:
self._search_engine.index_mail(mail)
return ""
- def mark_many_mail_read(self, request):
+
+class MailsReadResource(Resource):
+
+ isLeaf = True
+
+ def __init__(self, mail_service, search_engine):
+ Resource.__init__(self)
+ self._search_engine = search_engine
+ self._mail_service = mail_service
+
+ def render_POST(self, request):
content_dict = json.load(request.content)
idents = content_dict.get('idents')
for ident in idents:
@@ -60,6 +44,22 @@ class MailsController:
self._search_engine.index_mail(mail)
return ""
+
+class MailsDeleteResource(Resource):
+
+ isLeaf = True
+
+ def __init__(self, mail_service, search_engine):
+ Resource.__init__(self)
+ self._mail_service = mail_service
+ self._search_engine = search_engine
+
+ def render_POST(self, request):
+ idents = json.loads(request.content.read())['idents']
+ for ident in idents:
+ self._delete_mail(ident)
+ return respond_json(None, request)
+
def _delete_mail(self, mail_id):
mail = self._mail_service.mail(mail_id)
if mail.mailbox_name == 'TRASH':
@@ -69,17 +69,33 @@ class MailsController:
trashed_mail = self._mail_service.delete_mail(mail_id)
self._search_engine.index_mail(trashed_mail)
- def delete_mail(self, request, mail_id):
- self._delete_mail(mail_id)
- return respond_json(None, request)
- def delete_mails(self, request):
- idents = json.loads(request.content.read())['idents']
- for ident in idents:
- self._delete_mail(ident)
- return respond_json(None, request)
+class MailsResource(Resource):
+
+ def __init__(self, search_engine, mail_service, draft_service):
+ Resource.__init__(self)
+ self.putChild('delete', MailsDeleteResource(mail_service, search_engine))
+ self.putChild('read', MailsReadResource(mail_service, search_engine))
+ self.putChild('unread', MailsUnreadResource(mail_service, search_engine))
+
+ self._draft_service = draft_service
+ self._mail_service = mail_service
+ self._search_engine = search_engine
+
+ def render_GET(self, request):
+ mail_ids, total = self._search_engine.search(request.args.get('q')[0], request.args.get('w')[0], request.args.get('p')[0])
+ mails = self._mail_service.mails(mail_ids)
+
+ response = {
+ "stats": {
+ "total": total,
+ },
+ "mails": [mail.as_dict() for mail in mails]
+ }
- def send_mail(self, request):
+ return json.dumps(response)
+
+ def render_POST(self, request):
try:
content_dict = json.loads(request.content.read())
_mail = InputMail.from_dict(content_dict)
@@ -91,20 +107,9 @@ class MailsController:
return respond_json(_mail.as_dict(), request)
except Exception as error:
- return respond_json({'message': self._format_exception(error)}, request, status_code=422)
+ return respond_json({'message': _format_exception(error)}, request, status_code=422)
- def mail_tags(self, request, mail_id):
- content_dict = json.loads(request.content.read())
- new_tags = map(lambda tag: tag.lower(), content_dict['newtags'])
- try:
- self._mail_service.update_tags(mail_id, new_tags)
- mail = self._mail_service.mail(mail_id)
- self._search_engine.index_mail(mail)
- except ValueError as ve:
- return respond_json(ve.message, request, 403)
- return respond_json(mail.as_dict(), request)
-
- def update_draft(self, request):
+ def render_PUT(self, request):
content_dict = json.loads(request.content.read())
_mail = InputMail.from_dict(content_dict)
draft_id = content_dict.get('ident')
@@ -119,6 +124,5 @@ class MailsController:
self._search_engine.index_mail(pixelated_mail)
return respond_json({'ident': pixelated_mail.ident}, request)
- def _format_exception(self, exception):
- exception_info = map(str, list(exception.args))
- return '\n'.join(exception_info)
+
+
diff --git a/service/pixelated/resources/root_resource.py b/service/pixelated/resources/root_resource.py
new file mode 100644
index 00000000..326654eb
--- /dev/null
+++ b/service/pixelated/resources/root_resource.py
@@ -0,0 +1,46 @@
+import os
+from pixelated.resources.attachments_resource import AttachmentsResource
+from pixelated.resources.contacts_resource import ContactsResource
+from pixelated.resources.features_resource import FeaturesResource
+from pixelated.resources.mail_resource import MailResource
+from pixelated.resources.mails_resource import MailsResource
+from pixelated.resources.sync_info_resource import SyncInfoResource
+from pixelated.resources.tags_resource import TagsResource
+from twisted.web.resource import Resource
+from twisted.web.static import File
+
+
+class RootResource(Resource):
+
+ def __init__(self):
+ Resource.__init__(self)
+ self._static_folder = self._get_static_folder()
+
+ def getChild(self, path, request):
+ if path == '':
+ return self
+ return Resource.getChild(self, path, request)
+
+ def initialize(self, querier, search_engine, mail_service, draft_service):
+ self.putChild('assets', File(self._static_folder))
+ self.putChild('attachments', AttachmentsResource(querier))
+ self.putChild('contacts', ContactsResource(search_engine))
+ self.putChild('features', FeaturesResource())
+ self.putChild('sync_info', SyncInfoResource())
+ self.putChild('tags', TagsResource(search_engine))
+ self.putChild('mails', MailsResource(search_engine, mail_service, draft_service))
+ self.putChild('mail', MailResource(mail_service, search_engine))
+
+ def _get_static_folder(self):
+
+ static_folder = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "..", "web-ui", "app"))
+ # this is a workaround for packaging
+ if not os.path.exists(static_folder):
+ static_folder = os.path.abspath(
+ os.path.join(os.path.abspath(__file__), "..", "..", "..", "..", "web-ui", "app"))
+ if not os.path.exists(static_folder):
+ static_folder = os.path.join('/', 'usr', 'share', 'pixelated-user-agent')
+ return static_folder
+
+ def render_GET(self, request):
+ return open(os.path.join(self._static_folder, 'index.html')).read() \ No newline at end of file
diff --git a/service/pixelated/controllers/sync_info_controller.py b/service/pixelated/resources/sync_info_resource.py
index 50e53852..5aa94218 100644
--- a/service/pixelated/controllers/sync_info_controller.py
+++ b/service/pixelated/resources/sync_info_resource.py
@@ -13,11 +13,16 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
-from pixelated.controllers import respond_json
+from pixelated.resources import respond_json
+from twisted.web.resource import Resource
-class SyncInfoController:
+class SyncInfoResource(Resource):
+
+ isLeaf = True
+
def __init__(self):
+ Resource.__init__(self)
self.current = 0
self.total = 0
@@ -29,7 +34,7 @@ class SyncInfoController:
def set_sync_info(self, soledad_sync_status):
self.current, self.total = map(int, soledad_sync_status.content.split('/'))
- def sync_info(self, request):
+ def render_GET(self, request):
_sync_info = {
'is_syncing': self.current != self.total,
'count': {
diff --git a/service/pixelated/controllers/tags_controller.py b/service/pixelated/resources/tags_resource.py
index b6741dcc..8a8ab81f 100644
--- a/service/pixelated/controllers/tags_controller.py
+++ b/service/pixelated/resources/tags_resource.py
@@ -14,20 +14,25 @@
# You should have received a copy of the GNU Affero General Public License
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
-from pixelated.controllers import respond_json_deferred
+from pixelated.resources import respond_json_deferred
from twisted.internet.threads import deferToThread
+from twisted.web.resource import Resource
+from twisted.web.server import NOT_DONE_YET
-class TagsController:
+class TagsResource(Resource):
+
+ isLeaf = True
def __init__(self, search_engine):
+ Resource.__init__(self)
self._search_engine = search_engine
- def tags(self, request):
+ def render_GET(self, request):
query = request.args.get('q', [''])[0]
skip_default_tags = request.args.get('skipDefaultTags', [False])[0]
d = deferToThread(lambda: self._search_engine.tags(query=query, skip_default_tags=skip_default_tags))
d.addCallback(lambda tags: respond_json_deferred(tags, request))
- return d
+ return NOT_DONE_YET
diff --git a/service/pixelated/runserver.py b/service/pixelated/runserver.py
index 37a55582..b6762177 100644
--- a/service/pixelated/runserver.py
+++ b/service/pixelated/runserver.py
@@ -19,10 +19,6 @@ import logging
import json
import os
-from klein import Klein
-
-
-klein_app = Klein()
import ConfigParser
from twisted.python import log
@@ -36,7 +32,14 @@ import pixelated.support.ext_protobuf # monkey patch for protobuf in OSX
import pixelated.support.ext_sqlcipher # monkey patch for sqlcipher in debian
-app = Klein()
+class App:
+
+ def __init__(self):
+ self.resource = None
+ self.config = None
+ pass
+
+app = App()
app.config = {}
diff --git a/service/setup.py b/service/setup.py
index d50d9a7e..8ef66618 100644
--- a/service/setup.py
+++ b/service/setup.py
@@ -82,7 +82,7 @@ setup(name='pixelated-user-agent',
'pixelated.config',
'pixelated.certificates',
'pixelated.support',
- 'pixelated.controllers'
+ 'pixelated.resources'
],
test_suite='nose.collector',
install_requires=[
@@ -97,7 +97,7 @@ setup(name='pixelated-user-agent',
'leap.soledad.common==0.6.3',
'leap.soledad.client==0.6.3',
'leap.mail==0.3.9-1-gc1f9c92',
- 'whoosh==2.3.2'
+ 'whoosh==2.5.7'
],
entry_points={
'console_scripts': [
diff --git a/service/test/functional/features/environment.py b/service/test/functional/features/environment.py
index 72140e40..e4c4fa0c 100644
--- a/service/test/functional/features/environment.py
+++ b/service/test/functional/features/environment.py
@@ -19,7 +19,7 @@ from test.support.dispatcher.proxy import Proxy
from test.support.integration import AppTestClient
from selenium import webdriver
-from pixelated.controllers.features_controller import FeaturesController
+from pixelated.resources.features_resource import FeaturesController
def before_all(context):
diff --git a/service/test/support/integration/app_test_client.py b/service/test/support/integration/app_test_client.py
index eab001c6..b032eefd 100644
--- a/service/test/support/integration/app_test_client.py
+++ b/service/test/support/integration/app_test_client.py
@@ -29,7 +29,7 @@ from pixelated.adapter.services.mail_service import MailService
from pixelated.adapter.services.mailboxes import Mailboxes
from pixelated.adapter.soledad.soledad_querier import SoledadQuerier
from pixelated.adapter.services.tag_service import TagService
-from pixelated.controllers import FeaturesController, HomeController, MailsController, TagsController, \
+from pixelated.resources import FeaturesController, HomeController, MailsController, TagsController, \
SyncInfoController, AttachmentsController, ContactsController
import pixelated.runserver
from pixelated.adapter.model.mail import PixelatedMail
diff --git a/service/test/support/integration/soledad_test_base.py b/service/test/support/integration/soledad_test_base.py
index 4149462c..5892de60 100644
--- a/service/test/support/integration/soledad_test_base.py
+++ b/service/test/support/integration/soledad_test_base.py
@@ -15,7 +15,7 @@
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
import unittest
-from pixelated.controllers import *
+from pixelated.resources import *
from test.support.integration.app_test_client import AppTestClient
from test.support.integration.model import ResponseMail
diff --git a/service/test/unit/controllers/mails_controller_test.py b/service/test/unit/controllers/mails_controller_test.py
index 8108bc19..7e5d0e7d 100644
--- a/service/test/unit/controllers/mails_controller_test.py
+++ b/service/test/unit/controllers/mails_controller_test.py
@@ -20,7 +20,7 @@ from io import BytesIO
from klein.test_resource import requestMock
from mock import MagicMock
from mockito import *
-from pixelated.controllers.mails_controller import MailsController
+from pixelated.resources.mails_controller import MailsController
class TestMailsController(unittest.TestCase):
diff --git a/service/test/unit/controllers/sync_info_controller_test.py b/service/test/unit/controllers/sync_info_controller_test.py
index cd3aeb02..1fb38822 100644
--- a/service/test/unit/controllers/sync_info_controller_test.py
+++ b/service/test/unit/controllers/sync_info_controller_test.py
@@ -17,7 +17,7 @@ import unittest
import json
from mock import MagicMock
-from pixelated.controllers import SyncInfoController
+from pixelated.resources import SyncInfoController
from mockito import *
diff --git a/web-ui/app/index.html b/web-ui/app/index.html
index e4e0f07d..29356e3c 100644
--- a/web-ui/app/index.html
+++ b/web-ui/app/index.html
@@ -3,16 +3,16 @@
<head>
<link rel="icon"
type="image/png"
- href="/images/Favicon.png">
+ href="assets/images/Favicon.png">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Pixelated Mail</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
-<link href="bower_components/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css">
-<link href="css/opensans.css" rel="stylesheet" type="text/css">
-<link href="css/news-cycle.css" rel="stylesheet" type="text/css"/>
-<link rel="stylesheet" href="/css/main.css">
+<link href="assets/bower_components/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css">
+<link href="assets/css/opensans.css" rel="stylesheet" type="text/css">
+<link href="assets/css/news-cycle.css" rel="stylesheet" type="text/css"/>
+<link rel="stylesheet" href="assets/css/main.css">
</head>
<body>
@@ -26,7 +26,7 @@
<div class="inner-wrap">
<div id="menu" class="column collapsed-nav no-padding">
<a class="left-off-canvas-logo" href="#">
- <img id="pixelated-logo" src="images/pixelated-logo_symbol_orange.svg" alt="Pixelated">
+ <img id="pixelated-logo" src="assets/images/pixelated-logo_symbol_orange.svg" alt="Pixelated">
</a>
<a class="left-off-canvas-toggle" href="#">
<i class=" toggle fa fa-navicon"></i>
@@ -71,16 +71,16 @@
</div>
<!--usemin_start-->
-<script src="/bower_components/modernizr/modernizr.js"></script>
-<script src="/bower_components/lodash/dist/lodash.js"></script>
-<script src="/bower_components/jquery/dist/jquery.js"></script>
-<script src="/js/lib/highlightRegex.js"></script>
-<script src="/bower_components/handlebars/handlebars.min.js"></script>
-<script src="/bower_components/typeahead.js/dist/typeahead.bundle.min.js"></script>
-<script src="/bower_components/foundation/js/foundation.js" ></script>
-<script src="/bower_components/foundation/js/foundation/foundation.reveal.js" ></script>
-<script src="/bower_components/foundation/js/foundation/foundation.offcanvas.js"></script>
-<script src="/bower_components/requirejs/require.js" data-main="js/main.js"></script>
+<script src="assets/bower_components/modernizr/modernizr.js"></script>
+<script src="assets/bower_components/lodash/dist/lodash.js"></script>
+<script src="assets/bower_components/jquery/dist/jquery.js"></script>
+<script src="assets/js/lib/highlightRegex.js"></script>
+<script src="assets/bower_components/handlebars/handlebars.min.js"></script>
+<script src="assets/bower_components/typeahead.js/dist/typeahead.bundle.min.js"></script>
+<script src="assets/bower_components/foundation/js/foundation.js" ></script>
+<script src="assets/bower_components/foundation/js/foundation/foundation.reveal.js" ></script>
+<script src="assets/bower_components/foundation/js/foundation/foundation.offcanvas.js"></script>
+<script src="assets/bower_components/requirejs/require.js" data-main="assets/js/main.js"></script>
<!--usemin_end-->
diff --git a/web-ui/app/js/main.js b/web-ui/app/js/main.js
index 6f3b3e8c..06eca8dc 100644
--- a/web-ui/app/js/main.js
+++ b/web-ui/app/js/main.js
@@ -17,7 +17,7 @@
'use strict';
requirejs.config({
- baseUrl: '../',
+ baseUrl: '../assets/',
paths: {
'mail_list': 'js/mail_list',
'page': 'js/page',
diff --git a/web-ui/app/js/views/i18n.js b/web-ui/app/js/views/i18n.js
index b09490f5..19327c27 100644
--- a/web-ui/app/js/views/i18n.js
+++ b/web-ui/app/js/views/i18n.js
@@ -24,7 +24,7 @@ define(['i18next'], function(i18n) {
self.get = self;
self.init = function(path) {
- i18n.init({detectLngQS: 'lang', fallbackLng: 'en', lowerCaseLng: true, getAsync: false, resGetPath: path + 'locales/__lng__/__ns__.json'});
+ i18n.init({detectLngQS: 'lang', fallbackLng: 'en', lowerCaseLng: true, getAsync: false, resGetPath: path + 'assets/locales/__lng__/__ns__.json'});
Handlebars.registerHelper('t', self.get.bind(self));
};
diff --git a/web-ui/app/scss/news-cycle.scss b/web-ui/app/scss/news-cycle.scss
index 8f813996..ecca383c 100644
--- a/web-ui/app/scss/news-cycle.scss
+++ b/web-ui/app/scss/news-cycle.scss
@@ -1,13 +1,13 @@
@font-face {
font-family: 'News Cycle';
- src: url('/fonts/NewsCycleRegular.ttf') format('truetype');
+ src: url('assets/fonts/NewsCycleRegular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'News Cycle';
- src: url('/fonts/NewsCycleBold.ttf') format('truetype');
+ src: url('assets/fonts/NewsCycleBold.ttf') format('truetype');
font-weight: bold;
font-style: normal;
}
diff --git a/web-ui/app/scss/opensans.scss b/web-ui/app/scss/opensans.scss
index 5d5c7ff5..84529c41 100644
--- a/web-ui/app/scss/opensans.scss
+++ b/web-ui/app/scss/opensans.scss
@@ -2,60 +2,60 @@
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
- src: local('Open Sans Light'), local('OpenSans-Light'), url('/fonts/OpenSans-Light.woff') format('woff');
+ src: local('Open Sans Light'), local('OpenSans-Light'), url('/assets/fonts/OpenSans-Light.woff') format('woff');
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
- src: local('Open Sans'), local('OpenSans'), url('/fonts/OpenSans.woff') format('woff');
+ src: local('Open Sans'), local('OpenSans'), url('/assets/fonts/OpenSans.woff') format('woff');
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
- src: local('Open Sans Semibold'), local('OpenSans-Semibold'), url('/fonts/OpenSans-Semibold.woff') format('woff');
+ src: local('Open Sans Semibold'), local('OpenSans-Semibold'), url('/assets/fonts/OpenSans-Semibold.woff') format('woff');
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
- src: local('Open Sans Bold'), local('OpenSans-Bold'), url('/fonts/OpenSans-Bold.woff') format('woff');
+ src: local('Open Sans Bold'), local('OpenSans-Bold'), url('/assets/fonts/OpenSans-Bold.woff') format('woff');
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 800;
- src: local('Open Sans Extrabold'), local('OpenSans-Extrabold'), url('/fonts/OpenSans-Extrabold.woff') format('woff');
+ src: local('Open Sans Extrabold'), local('OpenSans-Extrabold'), url('/assets/fonts/OpenSans-Extrabold.woff') format('woff');
}
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 300;
- src: local('Open Sans Light Italic'), local('OpenSansLight-Italic'), url('/fonts/OpenSansLight-Italic.woff') format('woff');
+ src: local('Open Sans Light Italic'), local('OpenSansLight-Italic'), url('/assets/fonts/OpenSansLight-Italic.woff') format('woff');
}
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 400;
- src: local('Open Sans Italic'), local('OpenSans-Italic'), url('/fonts/OpenSans-Italic.woff') format('woff');
+ src: local('Open Sans Italic'), local('OpenSans-Italic'), url('/assets/fonts/OpenSans-Italic.woff') format('woff');
}
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 600;
- src: local('Open Sans Semibold Italic'), local('OpenSans-SemiboldItalic'), url('/fonts/OpenSans-SemiboldItalic.woff
+ src: local('Open Sans Semibold Italic'), local('OpenSans-SemiboldItalic'), url('/assets/fonts/OpenSans-SemiboldItalic.woff
') format('woff');
}
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 700;
- src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url('/fonts/OpenSans-BoldItalic.woff') format('woff');
+ src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url('/assets/fonts/OpenSans-BoldItalic.woff') format('woff');
}
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 800;
- src: local('Open Sans Extrabold Italic'), local('OpenSans-ExtraboldItalic'), url('/fonts/OpenSans-ExtraboldItalic.woff') format('woff');
+ src: local('Open Sans Extrabold Italic'), local('OpenSans-ExtraboldItalic'), url('/assets/fonts/OpenSans-ExtraboldItalic.woff') format('woff');
}