diff options
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'); } |