diff --git a/etc/fixtures/demo_data.json b/etc/fixtures/demo_data.json index 26c0e7765..171fe253f 100644 --- a/etc/fixtures/demo_data.json +++ b/etc/fixtures/demo_data.json @@ -31,6 +31,38 @@ "order" : 0 } ] + }, + { + "name" : "author", + "email" : "author@example.com", + "gravatar_email": "rochacbruno+quokka@gmail.com", + "password" : "author", + "roles" : [ "author" ], + "bio": "Quokka Example Author", + "tagline": "It is nice to write in Quokka CMS!", + "links": [ + { + "title" : "facebook", + "link" : "http://facebook.com/quokkaproject", + "icon" : "facebook", + "css_class" : "facebook", + "order" : 0 + }, + { + "title" : "github", + "link" : "http://github.com/quokkaproject", + "icon" : "github", + "css_class" : "github", + "order" : 0 + }, + { + "title" : "twitter", + "link" : "http://twitter.com/quokkaproject", + "icon" : "twitter", + "css_class" : "twitter", + "order" : 0 + } + ] } ], "configs": [ diff --git a/etc/fixtures/initial_data.json b/etc/fixtures/initial_data.json index d8d178609..ca048991e 100644 --- a/etc/fixtures/initial_data.json +++ b/etc/fixtures/initial_data.json @@ -31,6 +31,38 @@ "order" : 0 } ] + }, + { + "name" : "author", + "email" : "author@example.com", + "gravatar_email": "rochacbruno+quokka@gmail.com", + "password" : "author", + "roles" : [ "author" ], + "bio": "Quokka Example Author", + "tagline": "It is nice to write in Quokka CMS!", + "links": [ + { + "title" : "facebook", + "link" : "http://facebook.com/quokkaproject", + "icon" : "facebook", + "css_class" : "facebook", + "order" : 0 + }, + { + "title" : "github", + "link" : "http://github.com/quokkaproject", + "icon" : "github", + "css_class" : "github", + "order" : 0 + }, + { + "title" : "twitter", + "link" : "http://twitter.com/quokkaproject", + "icon" : "twitter", + "css_class" : "twitter", + "order" : 0 + } + ] } ], "configs": [ diff --git a/quokka/__init__.py b/quokka/__init__.py index 84b836be4..91545fbce 100644 --- a/quokka/__init__.py +++ b/quokka/__init__.py @@ -11,13 +11,12 @@ __copyright__ = "Copyright 2014, Quokka Project" -try: - from .core.admin import create_admin - from .core.app import QuokkaApp - # from .core.middleware import HTTPMethodOverrideMiddleware - admin = create_admin() -except: - pass +from quokka.core.admin import create_admin +from quokka.core.app import QuokkaApp +from quokka.core.middleware import HTTPMethodOverrideMiddleware +from quokka.ext import configure_extensions + +admin = create_admin() def create_app_base(config=None, test=False, admin_instance=None, **settings): @@ -32,9 +31,10 @@ def create_app(config=None, test=False, admin_instance=None, **settings): app = create_app_base( config=config, test=test, admin_instance=admin_instance, **settings ) - from .ext import configure_extensions + configure_extensions(app, admin_instance or admin) - # app.wsgi_app = HTTPMethodOverrideMiddleware(app.wsgi_app) + if app.config.get("HTTP_PROXY_METHOD_OVERRIDE"): + app.wsgi_app = HTTPMethodOverrideMiddleware(app.wsgi_app) return app diff --git a/quokka/core/admin/__init__.py b/quokka/core/admin/__init__.py index a23ab5dd6..25383045f 100644 --- a/quokka/core/admin/__init__.py +++ b/quokka/core/admin/__init__.py @@ -1,7 +1,5 @@ #!/usr/bin/env python # -*- coding: utf-8 -* - -import logging from werkzeug.utils import import_string from flask import request, session @@ -26,8 +24,6 @@ ''' _n # noqa -logger = logging.getLogger() - class QuokkaAdmin(Admin): registered = [] @@ -85,8 +81,8 @@ def get_locale(): return session.get('lang', 'en') admin.locale_selector(get_locale) - except: - pass # Exception: Can not add locale_selector second time. + except Exception as e: + app.logger.info('Cannot add locale_selector. %s' % e) for entry in app.config.get('FILE_ADMIN', []): try: @@ -102,8 +98,7 @@ def get_locale(): ) ) except Exception as e: - logger.info(e) - # need to check blueprint endpoisnt colision + app.logger.info(e) # register all themes in file manager for k, theme in app.theme_manager.themes.items(): @@ -142,8 +137,12 @@ def get_locale(): 'DEFAULT_EDITABLE_EXTENSIONS') ) ) - except: - pass + except Exception as e: + app.logger.warning( + 'Error registering %s folder to file admin %s' % ( + theme.identifier, e + ) + ) # adding views admin.add_view(InspectorView(category=_l("Settings"), diff --git a/quokka/core/admin/fields.py b/quokka/core/admin/fields.py index 62dbcbe6a..805880798 100644 --- a/quokka/core/admin/fields.py +++ b/quokka/core/admin/fields.py @@ -21,15 +21,18 @@ class ThumbWidget(ImageUploadInput): ' ' '') - def get_url(self, field): - if field.thumbnail_size: - filename = field.thumbnail_fn(field.data) - else: - filename = field.data - - if field.url_relative_path: - filename = urljoin(field.url_relative_path, filename) - + @staticmethod + def get_url(field): + ''' + This meethod is not used, but is here for compatibility + ''' + # if field.thumbnail_size: + # filename = field.thumbnail_fn(field.data) + # else: + # filename = field.data + # + # if field.url_relative_path: + # filename = urljoin(field.url_relative_path, filename) return field.data diff --git a/quokka/core/admin/models.py b/quokka/core/admin/models.py index 58de2507e..095a9e1bc 100644 --- a/quokka/core/admin/models.py +++ b/quokka/core/admin/models.py @@ -39,7 +39,7 @@ def render(self, template, **kwargs): kwargs['h'] = h # Contribute extra arguments kwargs.update(self._template_args) - theme = current_app.config.get('ADMIN_THEME', None) + theme = current_app.config.get('ADMIN_THEME') return render_template(template, theme=theme, **kwargs) @@ -49,7 +49,7 @@ def is_accessible(self): roles_accepted = getattr(self, 'roles_accepted', None) return is_accessible(roles_accepted=roles_accepted, user=current_user) - def _handle_view(self, name, *args, **kwargs): + def _handle_view(self, *args, **kwargs): if not current_user.is_authenticated(): return redirect(url_for_security('login', next="/admin")) if not self.is_accessible(): @@ -107,7 +107,7 @@ def format_status(self, request, obj, fieldname, *args, **kwargs): def get_url(self, request, obj, fieldname, *args, **kwargs): column_formatters_args = getattr(self, 'column_formatters_args', {}) _args = column_formatters_args.get('get_url', {}).get(fieldname, {}) - attribute = _args.get('attribute', None) + attribute = _args.get('attribute') method = _args.get('method', 'get_absolute_url') text = getattr(obj, fieldname, '') if attribute: diff --git a/quokka/core/config.py b/quokka/core/config.py index 715d27e3c..b018dff14 100644 --- a/quokka/core/config.py +++ b/quokka/core/config.py @@ -1,8 +1,12 @@ import os +import logging +import quokka.core.models as m from flask.config import Config from quokka.utils import parse_conf_data from cached_property import cached_property_ttl, cached_property +logger = logging.getLogger() + class QuokkaConfig(Config): """A Config object for Flask that tries to ger vars from @@ -26,12 +30,14 @@ def all_setings_from_db(self): and Make it possible to use REDIS as a cache """ try: - from quokka.core.models import Config return { item.name: item.value - for item in Config.objects.get(group='settings').values + for item in m.config.Config.objects.get( + group='settings' + ).values } - except: + except Exception as e: + logger.warning('Error reading all settings from db: %s' % e) return {} def get_from_db(self, key, default=None): diff --git a/quokka/core/fields.py b/quokka/core/fields.py index 35457784f..d57c54fd8 100644 --- a/quokka/core/fields.py +++ b/quokka/core/fields.py @@ -9,7 +9,8 @@ class MultipleObjectsReturned(Exception): def match_all(i, kwargs): - return all([getattr(i, k) == v for k, v in kwargs.items()]) + # use generator expression? + return all(getattr(i, k) == v for k, v in kwargs.items()) def getinstance(_instance): diff --git a/quokka/core/middleware.py b/quokka/core/middleware.py index 3f8e494d0..dab626275 100644 --- a/quokka/core/middleware.py +++ b/quokka/core/middleware.py @@ -40,7 +40,7 @@ def _get_from_querystring(self, environ): return None def _get_method_override(self, environ): - return environ.get(self.header_name, None) or \ + return environ.get(self.header_name) or \ self._get_from_querystring(environ) or '' def __call__(self, environ, start_response): diff --git a/quokka/core/models/channel.py b/quokka/core/models/channel.py index 60487983b..61449c645 100644 --- a/quokka/core/models/channel.py +++ b/quokka/core/models/channel.py @@ -162,7 +162,7 @@ def get_canonical_url(self, *args, **kwargs): def get_http_url(self): site_url = Config.get('site', 'site_domain', request.url_root) - return u"{}{}".format(site_url, self.get_absolute_url()) + return u"{0}{1}".format(site_url, self.get_absolute_url()) def clean(self): homepage = Channel.objects(is_homepage=True) diff --git a/quokka/core/models/content.py b/quokka/core/models/content.py index ddaad46cb..83401bdee 100644 --- a/quokka/core/models/content.py +++ b/quokka/core/models/content.py @@ -106,7 +106,7 @@ def get_http_url(self): site_url = get_site_url() absolute_url = self.get_absolute_url() absolute_url = absolute_url[1:] - return u"{}{}".format(site_url, absolute_url) + return u"{0}{1}".format(site_url, absolute_url) def get_absolute_url(self, endpoint='quokka.core.detail'): if self.channel.is_homepage: diff --git a/quokka/core/models/signature.py b/quokka/core/models/signature.py index b4eabc006..b07d5f4f5 100644 --- a/quokka/core/models/signature.py +++ b/quokka/core/models/signature.py @@ -37,7 +37,7 @@ class Owned(object): last_updated_by = db.ReferenceField(User) authors = db.ListField(db.ReferenceField(User)) - def get_authors(self, sortedf=None): + def get_authors(self): return set(self.authors + [self.created_by]) @property diff --git a/quokka/core/views.py b/quokka/core/views.py index b86a4904a..059f405f1 100644 --- a/quokka/core/views.py +++ b/quokka/core/views.py @@ -219,7 +219,8 @@ def get_template_names(self): return names - def get_filters(self): + @staticmethod + def get_filters(): now = datetime.now() filters = { 'published': True, @@ -227,7 +228,8 @@ def get_filters(self): } return filters - def check_if_is_accessible(self, content): + @staticmethod + def check_if_is_accessible(content): if not content.channel.is_available: return abort(404) @@ -283,10 +285,13 @@ def get(self, long_slug, render_content=False): class ContentDetailPreview(ContentDetail): - def get_filters(self): + + @staticmethod + def get_filters(): return {} - def check_if_is_accessible(self, content): + @staticmethod + def check_if_is_accessible(content): if not content.channel.published: return abort(404) @@ -357,7 +362,8 @@ def cdata(data): class BaseFeed(MethodView): - def make_external_url(self, url): + @staticmethod + def make_external_url(url): return urljoin(request.url_root, url) def make_atom(self, feed_name, contents): diff --git a/quokka/ext/__init__.py b/quokka/ext/__init__.py index e409d319a..4d89841df 100644 --- a/quokka/ext/__init__.py +++ b/quokka/ext/__init__.py @@ -17,21 +17,14 @@ def configure_extensions(app, admin): Mail(app) error_handlers.configure(app) db.init_app(app) - - themes.configure(app, db) # Themes should be configured after db - + themes.configure(app) context_processors.configure(app) template_filters.configure(app) - security.configure(app, db) - fixtures.configure(app, db) - blueprints.load_from_packages(app) + # blueprints.load_from_packages(app) blueprints.load_from_folder(app) - - # enable .pdf support for posts weasyprint.configure(app) - configure_admin(app, admin) if app.config.get('DEBUG_TOOLBAR_ENABLED'): @@ -39,11 +32,10 @@ def configure_extensions(app, admin): from flask_debugtoolbar import DebugToolbarExtension DebugToolbarExtension(app) except: - pass + app.logger.info('flask_debugtoolbar is not installed') before_request.configure(app) views.configure(app) - oauthlib.configure(app) if app.config.get('SENTRY_ENABLED', False): @@ -53,7 +45,7 @@ def configure_extensions(app, admin): return app -def configure_extensions_min(app, admin): +def configure_extensions_min(app, *args, **kwargs): db.init_app(app) security.init_app(app, db) return app diff --git a/quokka/ext/blueprints.py b/quokka/ext/blueprints.py index 5b1d95f65..60be30ad3 100644 --- a/quokka/ext/blueprints.py +++ b/quokka/ext/blueprints.py @@ -4,8 +4,8 @@ from quokka.ext.commands_collector import CommandsCollector -def load_from_packages(app): - pass +# def load_from_packages(app): +# pass def load_from_folder(app): diff --git a/quokka/ext/commands_collector.py b/quokka/ext/commands_collector.py index 722d26508..ff323fb02 100644 --- a/quokka/ext/commands_collector.py +++ b/quokka/ext/commands_collector.py @@ -16,7 +16,7 @@ def __init__(self, modules_path, base_module_name, **attrs): self.base_module_name = base_module_name self.modules_path = modules_path - def list_commands(self, ctx): + def list_commands(self, *args, **kwargs): commands = [] for _path, _dir, _ in os.walk(self.modules_path): if 'commands' not in _dir: @@ -25,7 +25,7 @@ def list_commands(self, ctx): if filename.endswith('.py') and filename != '__init__.py': cmd = filename[:-3] _, module_name = os.path.split(_path) - commands.append('{}_{}'.format(module_name, cmd)) + commands.append('{0}_{1}'.format(module_name, cmd)) commands.sort() return commands @@ -39,7 +39,7 @@ def get_command(self, ctx, name): module_name, command_name = splitted if not all([module_name, command_name]): return - module = '{}.{}.commands.{}'.format( + module = '{0}.{1}.commands.{2}'.format( self.base_module_name, module_name, command_name) diff --git a/quokka/ext/error_handlers.py b/quokka/ext/error_handlers.py index 78685cdab..56244ecd6 100644 --- a/quokka/ext/error_handlers.py +++ b/quokka/ext/error_handlers.py @@ -4,7 +4,7 @@ def configure(app): @app.errorhandler(403) - def forbidden_page(error): + def forbidden_page(*args, **kwargs): """ The server understood the request, but is refusing to fulfill it. Authorization will not help and the request SHOULD NOT be repeated. @@ -18,7 +18,7 @@ def forbidden_page(error): return render_template("errors/access_forbidden.html"), 403 @app.errorhandler(404) - def page_not_found(error): + def page_not_found(*args, **kwargs): """ The server has not found anything matching the Request-URI. No indication @@ -35,7 +35,7 @@ def page_not_found(error): return render_template("errors/page_not_found.html"), 404 @app.errorhandler(405) - def method_not_allowed_page(error): + def method_not_allowed_page(*args, **kwargs): """ The method specified in the Request-Line is not allowed for the resource @@ -46,11 +46,11 @@ def method_not_allowed_page(error): return render_template("errors/method_not_allowed.html"), 405 @app.errorhandler(500) - def server_error_page(error): + def server_error_page(*args, **kwargs): return render_template("errors/server_error.html"), 500 # URLBUILD Error Handlers - def admin_icons_error_handler(error, endpoint, values): + def admin_icons_error_handler(error, endpoint, *args, **kwargs): "when some of default dashboard button is deactivated, avoids error" if endpoint in [item[0] for item in app.config.get('ADMIN_ICONS', [])]: return '/admin' diff --git a/quokka/ext/fixtures.py b/quokka/ext/fixtures.py index 30f2931bd..24ebb1fd0 100644 --- a/quokka/ext/fixtures.py +++ b/quokka/ext/fixtures.py @@ -17,10 +17,12 @@ def configure(app, db): populate.create_purposes() populate.create_channel_types() populate.create_base_channels() + populate.role("admin") + populate.role("author") try: with app.app_context(): user_data, user_obj = populate.create_initial_superuser() populate.create_initial_post(user_data, user_obj) - except: - app.logger.warning("Could not create initial user and post") + except Exception as e: + app.logger.warning("Cant create initial user and post: %s" % e) Quokka.objects.create(slug="is_installed") diff --git a/quokka/ext/themes.py b/quokka/ext/themes.py index 206bee566..a88dbf9ce 100644 --- a/quokka/ext/themes.py +++ b/quokka/ext/themes.py @@ -3,15 +3,6 @@ from quokka_themes import Themes -def configure(app, db=None): +def configure(app): themes = Themes() themes.init_themes(app, app_identifier="quokka") - - # The code below is no longer needed since config reads database - # try: - # from quokka.core.models import Config - # s = Config.objects.get(group='settings') - # settings = {i.name: i.value for i in s.values} - # app.config.update(settings) - # except Exception as e: - # app.logger.error(str(e)) diff --git a/quokka/modules/accounts/models.py b/quokka/modules/accounts/models.py index 02402f389..d17e3af4f 100644 --- a/quokka/modules/accounts/models.py +++ b/quokka/modules/accounts/models.py @@ -103,22 +103,14 @@ def get_gravatar_email(self): def clean(self, *args, **kwargs): if not self.username: self.username = User.generate_username(self.name) - - try: - super(User, self).clean(*args, **kwargs) - except: - pass + super(User, self).clean(*args, **kwargs) @classmethod def generate_username(cls, name): - # username = email.lower() - # for item in ['@', '.', '-', '+']: - # username = username.replace(item, '_') - # return username name = name or '' username = slugify(name) if cls.objects.filter(username=username).count(): - username = "{}{}".format(username, randint(1, 1000)) + username = "{0}{1}".format(username, randint(1, 1000)) return username def set_password(self, password, save=False): diff --git a/quokka/modules/accounts/views.py b/quokka/modules/accounts/views.py index 791abf9fc..478a21fa7 100644 --- a/quokka/modules/accounts/views.py +++ b/quokka/modules/accounts/views.py @@ -56,7 +56,8 @@ class ProfileEditView(MethodView): ] ) - def needs_login(self, **kwargs): + @staticmethod + def needs_login(**kwargs): if not current_user.is_authenticated(): nex = kwargs.get( 'next', diff --git a/quokka/modules/comments/models.py b/quokka/modules/comments/models.py index 33e804cb3..d86bca4d3 100644 --- a/quokka/modules/comments/models.py +++ b/quokka/modules/comments/models.py @@ -40,7 +40,7 @@ def __unicode__(self): return u"{0} - {1}...".format(self.author_name, self.body[:15]) def get_canonical_url(self): - return "/{}.{}".format( + return "/{0}.{1}".format( self.path, get_setting_value('CONTENT_EXTENSION', 'html') ) if not self.path.startswith("/") else self.path diff --git a/quokka/modules/comments/views.py b/quokka/modules/comments/views.py index 3fadd3ddb..703f690e8 100644 --- a/quokka/modules/comments/views.py +++ b/quokka/modules/comments/views.py @@ -16,7 +16,8 @@ class CommentView(MethodView): only=['author_name', 'author_email', 'body'] ) - def render_context(self, path, form): + @staticmethod + def render_context(path, form): comments = Comment.objects(path=path, published=True) return render_template('content/comments.html', comments=comments, diff --git a/quokka/modules/media/admin.py b/quokka/modules/media/admin.py index 2b9960817..5a077d5cb 100644 --- a/quokka/modules/media/admin.py +++ b/quokka/modules/media/admin.py @@ -95,7 +95,8 @@ class ImageAdmin(MediaAdmin): form_columns = ['title', 'slug', 'path', 'channel', 'content_format', 'comments_enabled', 'summary', 'published'] - def _list_thumbnail(self, context, model, name): + @staticmethod + def _list_thumbnail(context, model, name): if not model.path: return '' diff --git a/quokka/modules/media/views.py b/quokka/modules/media/views.py index 5213c6eb5..86c924b66 100644 --- a/quokka/modules/media/views.py +++ b/quokka/modules/media/views.py @@ -20,7 +20,8 @@ def get(self): class DetailView(MethodView): - def get_context(self, slug): + @staticmethod + def get_context(slug): media = Media.objects.get_or_404(slug=slug) context = { diff --git a/quokka/settings.py b/quokka/settings.py index a657a0f7b..68694cd61 100644 --- a/quokka/settings.py +++ b/quokka/settings.py @@ -94,15 +94,6 @@ ) FILE_ADMIN = [ - { - "name": "Template files", - "category": "Files", - "path": os.path.join(PROJECT_ROOT, 'templates'), - "url": "/template_files/", # create nginx rule - "endpoint": "template_files", - "roles_accepted": ("admin", "editor"), - "editable_extensions": DEFAULT_EDITABLE_EXTENSIONS - }, { "name": "Static files", "category": "Files", @@ -312,3 +303,16 @@ "bitly_token": "9964d1f9c8c8b4215f7690449f0980c4fe1a6906", "bitly_login": "quokkabitly"} + + +""" +Some HTTP proxies do not support arbitrary HTTP methods or newer HTTP methods +(such as PATCH). +In that case it’s possible to “proxy” HTTP methods through another HTTP method +in total violation of the protocol. + +The way this works is by letting the client do an HTTP POST request and +set the X-HTTP-Method-Override header and set the value to the intended +HTTP method (such as PATCH). +""" +HTTP_PROXY_METHOD_OVERRIDE = False diff --git a/quokka/utils/__init__.py b/quokka/utils/__init__.py index 2e2a2c3dc..f2a5f8526 100644 --- a/quokka/utils/__init__.py +++ b/quokka/utils/__init__.py @@ -36,7 +36,8 @@ def get_current_user_for_models(): if not user.is_authenticated(): return None return user - except: + except Exception as e: + logger.info('Cant access is_authenticated method: %s' % e) return None @@ -45,9 +46,7 @@ def is_accessible(roles_accepted=None, user=None): if user.has_role('admin'): return True if roles_accepted: - accessible = any( - [user.has_role(role) for role in roles_accepted] - ) + accessible = any(user.has_role(role) for role in roles_accepted) return accessible return True diff --git a/quokka/utils/populate.py b/quokka/utils/populate.py index de7580fb0..de01fa06e 100644 --- a/quokka/utils/populate.py +++ b/quokka/utils/populate.py @@ -26,8 +26,8 @@ def __init__(self, db, *args, **kwargs): self.purposes = {} self.custom_values = {} self.load_fixtures() - self.baseurl = self.kwargs.get('baseurl', None) - self.app = self.kwargs.get('app', None) + self.baseurl = self.kwargs.get('baseurl') + self.app = self.kwargs.get('app') def __call__(self, *args, **kwargs): if self.baseurl and self.app: @@ -122,7 +122,8 @@ def create_users(self, data=None): for data in self.users_data: self.create_user(data) - def create_config(self, data): + @staticmethod + def create_config(data): try: return Config.objects.get(group=data.get('group')) except: @@ -259,13 +260,13 @@ def create_initial_post(self, user_data=None, user_obj=None): "content_format": "markdown" } post_data['channel'] = self.channels.get("home") - post_data["created_by"] = user_obj or User.objects.first() + post_data["created_by"] = user_obj or self.users.get('author') post = self.create_post(post_data) return post def create_post(self, data): if not data.get('created_by'): - data['created_by'] = self.users.get('admin') + data['created_by'] = self.users.get('author') data['last_updated_by'] = data['created_by'] data['published'] = True diff --git a/quokka/utils/settings.py b/quokka/utils/settings.py index 8ec90bcd1..268c7369d 100644 --- a/quokka/utils/settings.py +++ b/quokka/utils/settings.py @@ -1,8 +1,11 @@ +import logging import quokka.core.models as m from flask import current_app, request from quokka.core.db import db from quokka.core.app import QuokkaApp +logger = logging.getLogger() + def create_app_min(config=None, test=False): app = QuokkaApp('quokka') @@ -24,8 +27,8 @@ def get_site_url(): def get_setting_value(key, default=None): try: return current_app.config.get(key, default) - except RuntimeError: - pass + except RuntimeError as e: + logger.warning('current_app is inaccessible: %s' % e) try: app = create_app_min()