diff --git a/config/settings/common.py b/config/settings/common.py index 2cbaa82..ccd66db 100644 --- a/config/settings/common.py +++ b/config/settings/common.py @@ -16,11 +16,13 @@ import environ import socket # from spirit.settings import * -ROOT_DIR = environ.Path(__file__) - 3 # (mhackspace/config/settings/common.py - 3 = mhackspace/) -APPS_DIR = ROOT_DIR.path('mhackspace') +ROOT_DIR = ( + environ.Path(__file__) - 3 +) # (mhackspace/config/settings/common.py - 3 = mhackspace/) +APPS_DIR = ROOT_DIR.path("mhackspace") env = environ.Env() -env.read_env('%s/.env' % ROOT_DIR) +env.read_env("%s/.env" % ROOT_DIR) # Start ST is Spirit forum software config ST_TOPIC_PRIVATE_CATEGORY_PK = 1 @@ -53,102 +55,100 @@ ST_BASE_DIR = os.path.dirname(__file__) # END ST is Spirit forum software config -SECRET_KEY = env('DJANGO_SECRET_KEY', default='wq)sg12k&5&adv)e%56n5e97o@))6xu90b**=-w+)d^c+cd9%1') +SECRET_KEY = env( + "DJANGO_SECRET_KEY", + default="wq)sg12k&5&adv)e%56n5e97o@))6xu90b**=-w+)d^c+cd9%1", +) HAYSTACK_CONNECTIONS = { - 'default': { - 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine', - 'PATH': os.path.join(os.path.dirname(__file__), 'search', 'whoosh_index'), - }, + "default": { + "ENGINE": "haystack.backends.whoosh_backend.WhooshEngine", + "PATH": os.path.join( + os.path.dirname(__file__), "search", "whoosh_index" + ), + } } # APP CONFIGURATION # ------------------------------------------------------------------------------ DJANGO_APPS = ( # Default Django apps: - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.sites', - 'django.contrib.messages', - 'django.contrib.sitemaps', - 'django.contrib.staticfiles', - 'django.contrib.humanize', - 'django.contrib.admin', + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.sites", + "django.contrib.messages", + "django.contrib.sitemaps", + "django.contrib.staticfiles", + "django.contrib.humanize", + "django.contrib.admin", ) THIRD_PARTY_APPS = ( - 'crispy_forms', # Form layouts - 'allauth', # registration - 'allauth.account', # registration - 'allauth.socialaccount', # registration - 'allauth.socialaccount.providers.google', # registration - 'allauth.socialaccount.providers.github', # registration + "crispy_forms", # Form layouts + "allauth", # registration + "allauth.account", # registration + "allauth.socialaccount", # registration + "allauth.socialaccount.providers.google", # registration + "allauth.socialaccount.providers.github", # registration # 'allauth.socialaccount.providers.facebook', # registration - 'whitenoise.runserver_nostatic', - 'stdimage', - 'rest_framework', - 'django_filters', - 'martor', - 'haystack', - 'djconfig', - - 'corsheaders', - - 'spirit.core', - 'spirit.admin', - 'spirit.search', - - 'spirit.user', - 'spirit.user.admin', - 'spirit.user.auth', - - 'spirit.category', - 'spirit.category.admin', - - 'spirit.topic', - 'spirit.topic.admin', - 'spirit.topic.favorite', - 'spirit.topic.moderate', - 'spirit.topic.notification', - 'spirit.topic.poll', # todo: remove in Spirit v0.6 - 'spirit.topic.private', - 'spirit.topic.unread', - - 'spirit.comment', - 'spirit.comment.bookmark', - 'spirit.comment.flag', - 'spirit.comment.flag.admin', - 'spirit.comment.history', - 'spirit.comment.like', - 'spirit.comment.poll', - - 'django_nyt', - 'mptt', - 'sekizai', - 'sorl.thumbnail', - 'wiki', - 'wiki.plugins.attachments', - 'wiki.plugins.notifications', - 'wiki.plugins.images', - 'wiki.plugins.macros', + "whitenoise.runserver_nostatic", + "stdimage", + "rest_framework", + "django_filters", + "martor", + "haystack", + "djconfig", + "corsheaders", + "spirit.core", + "spirit.admin", + "spirit.search", + "spirit.user", + "spirit.user.admin", + "spirit.user.auth", + "spirit.category", + "spirit.category.admin", + "spirit.topic", + "spirit.topic.admin", + "spirit.topic.favorite", + "spirit.topic.moderate", + "spirit.topic.notification", + "spirit.topic.poll", # todo: remove in Spirit v0.6 + "spirit.topic.private", + "spirit.topic.unread", + "spirit.comment", + "spirit.comment.bookmark", + "spirit.comment.flag", + "spirit.comment.flag.admin", + "spirit.comment.history", + "spirit.comment.like", + "spirit.comment.poll", + "django_nyt", + "mptt", + "sekizai", + "sorl.thumbnail", + "wiki", + "wiki.plugins.attachments", + "wiki.plugins.notifications", + "wiki.plugins.images", + "wiki.plugins.macros", ) # Apps specific for this project go here. LOCAL_APPS = ( # custom users app # Your stuff: custom apps go here - 'mhackspace.users.apps.UsersConfig', - 'mhackspace.base', - 'mhackspace.subscriptions', - 'mhackspace.feeds', - 'mhackspace.contact', - 'mhackspace.members', - 'mhackspace.blog', - 'mhackspace.core', - 'mhackspace.requests', - 'mhackspace.register', - 'mhackspace.ldapsync', - 'mhackspace.rfid', + "mhackspace.users.apps.UsersConfig", + "mhackspace.base", + "mhackspace.subscriptions", + "mhackspace.feeds", + "mhackspace.contact", + "mhackspace.members", + "mhackspace.blog", + "mhackspace.core", + "mhackspace.requests", + "mhackspace.register", + "mhackspace.ldapsync", + "mhackspace.rfid", ) # See: https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps @@ -157,58 +157,53 @@ INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS # MIDDLEWARE CONFIGURATION # ------------------------------------------------------------------------------ MIDDLEWARE = ( - 'django.middleware.security.SecurityMiddleware', - 'whitenoise.middleware.WhiteNoiseMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'corsheaders.middleware.CorsMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - - #fix for ip logging behind a proxy - 'x_forwarded_for.middleware.XForwardedForMiddleware', - 'djconfig.middleware.DjConfigMiddleware', - - 'spirit.user.middleware.TimezoneMiddleware', - 'spirit.user.middleware.LastIPMiddleware', - 'spirit.user.middleware.LastSeenMiddleware', - 'spirit.user.middleware.ActiveUserMiddleware', - 'spirit.core.middleware.PrivateForumMiddleware', + "django.middleware.security.SecurityMiddleware", + "whitenoise.middleware.WhiteNoiseMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "corsheaders.middleware.CorsMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + # fix for ip logging behind a proxy + "x_forwarded_for.middleware.XForwardedForMiddleware", + "djconfig.middleware.DjConfigMiddleware", + "spirit.user.middleware.TimezoneMiddleware", + "spirit.user.middleware.LastIPMiddleware", + "spirit.user.middleware.LastSeenMiddleware", + "spirit.user.middleware.ActiveUserMiddleware", + "spirit.core.middleware.PrivateForumMiddleware", ) # MIGRATIONS CONFIGURATION # ------------------------------------------------------------------------------ -MIGRATION_MODULES = { - 'sites': 'mhackspace.contrib.sites.migrations' -} +MIGRATION_MODULES = {"sites": "mhackspace.contrib.sites.migrations"} # DEBUG # ------------------------------------------------------------------------------ # See: https://docs.djangoproject.com/en/dev/ref/settings/#debug -DEBUG = env.bool('DJANGO_DEBUG', False) +DEBUG = env.bool("DJANGO_DEBUG", False) # FIXTURE CONFIGURATION # ------------------------------------------------------------------------------ # See: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FIXTURE_DIRS -FIXTURE_DIRS = ( - str(APPS_DIR.path('fixtures')), -) +FIXTURE_DIRS = (str(APPS_DIR.path("fixtures")),) # EMAIL CONFIGURATION # ------------------------------------------------------------------------------ -EMAIL_BACKEND = env('DJANGO_EMAIL_BACKEND', default='django.core.mail.backends.smtp.EmailBackend') +EMAIL_BACKEND = env( + "DJANGO_EMAIL_BACKEND", + default="django.core.mail.backends.smtp.EmailBackend", +) EMAIL_PORT = 1025 -EMAIL_HOST = env("EMAIL_HOST", default='mailhog') -MSG_PREFIX = env("EMAIL_HOST", default='MHT') +EMAIL_HOST = env("EMAIL_HOST", default="mailhog") +MSG_PREFIX = env("EMAIL_HOST", default="MHT") # MANAGER CONFIGURATION # ------------------------------------------------------------------------------ # See: https://docs.djangoproject.com/en/dev/ref/settings/#admins -ADMINS = ( - ("""Maidstone Hackspace""", 'support@maidstone-hackspace.org.uk'), -) +ADMINS = (("""Maidstone Hackspace""", "support@maidstone-hackspace.org.uk"),) # See: https://docs.djangoproject.com/en/dev/ref/settings/#managers MANAGERS = ADMINS @@ -217,9 +212,9 @@ MANAGERS = ADMINS # ------------------------------------------------------------------------------ # See: https://docs.djangoproject.com/en/dev/ref/settings/#databases DATABASES = { - 'default': env.db('DATABASE_URL', default='postgres:///mhackspace'), + "default": env.db("DATABASE_URL", default="postgres:///mhackspace") } -DATABASES['default']['ATOMIC_REQUESTS'] = True +DATABASES["default"]["ATOMIC_REQUESTS"] = True # GENERAL CONFIGURATION @@ -228,10 +223,10 @@ DATABASES['default']['ATOMIC_REQUESTS'] = True # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name # although not all choices may be available on all operating systems. # In a Windows environment this must be set to your system time zone. -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" # See: https://docs.djangoproject.com/en/dev/ref/settings/#language-code -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" # See: https://docs.djangoproject.com/en/dev/ref/settings/#site-id SITE_ID = 1 @@ -251,79 +246,84 @@ USE_TZ = True TEMPLATES = [ { # See: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-TEMPLATES-BACKEND - 'BACKEND': 'django.template.backends.django.DjangoTemplates', + "BACKEND": "django.template.backends.django.DjangoTemplates", # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-dirs - 'DIRS': [ - str(APPS_DIR.path('templates')), - ], - 'OPTIONS': { + "DIRS": [str(APPS_DIR.path("templates"))], + "OPTIONS": { # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-debug - 'debug': DEBUG, + "debug": DEBUG, # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-loaders # https://docs.djangoproject.com/en/dev/ref/templates/api/#loader-types - 'loaders': [ - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', + "loaders": [ + "django.template.loaders.filesystem.Loader", + "django.template.loaders.app_directories.Loader", ], # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-context-processors - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.template.context_processors.i18n', - 'django.template.context_processors.media', - 'django.template.context_processors.static', - 'django.template.context_processors.tz', - 'django.contrib.messages.context_processors.messages', - 'sekizai.context_processors.sekizai', + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.template.context_processors.i18n", + "django.template.context_processors.media", + "django.template.context_processors.static", + "django.template.context_processors.tz", + "django.contrib.messages.context_processors.messages", + "sekizai.context_processors.sekizai", # Your stuff: custom template context processors go here - 'djconfig.context_processors.config', + "djconfig.context_processors.config", ], }, - }, + } ] # See: http://django-crispy-forms.readthedocs.io/en/latest/install.html#template-packs -CRISPY_TEMPLATE_PACK = 'bootstrap4' +CRISPY_TEMPLATE_PACK = "bootstrap4" # STATIC FILE CONFIGURATION # ------------------------------------------------------------------------------ # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-root -STATIC_ROOT = str(ROOT_DIR('static')) - -# See: https://docs.djangoproject.com/en/dev/ref/settings/#static-url -STATIC_URL = '/static/' +STATIC_ROOT = str(ROOT_DIR("static")) # See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS -STATICFILES_DIRS = ( - str(APPS_DIR.path('static')), -) +STATICFILES_DIRS = (str(APPS_DIR.path("static")),) # See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders STATICFILES_FINDERS = ( - 'django.contrib.staticfiles.finders.FileSystemFinder', - 'django.contrib.staticfiles.finders.AppDirectoriesFinder', - 'sass_processor.finders.CssFinder', + "django.contrib.staticfiles.finders.FileSystemFinder", + "django.contrib.staticfiles.finders.AppDirectoriesFinder", + "sass_processor.finders.CssFinder", ) + +AWS_DEFAULT_ACL = "public-read" +AWS_S3_SECURE_URLS = False +AWS_ACCESS_KEY_ID = env("MINIO_ACCESS_KEY") +AWS_SECRET_ACCESS_KEY = env("MINIO_SECRET_KEY") +AWS_STORAGE_BUCKET_NAME = "mhackspace-local" +AWS_S3_ENDPOINT_URL = "http://%s:9000" % socket.gethostbyname("bucket") +AWS_S3_OBJECT_PARAMETERS = {"CacheControl": "max-age=86400"} +AWS_LOCATION = "dev" +AWS_S3_SECURE_URLS = True +STATIC_URL = "%s/%s/" % (AWS_S3_ENDPOINT_URL, AWS_STORAGE_BUCKET_NAME) + # MEDIA CONFIGURATION # ------------------------------------------------------------------------------ # See: https://docs.djangoproject.com/en/dev/ref/settings/#media-root -MEDIA_ROOT = str(APPS_DIR('media')) -MARTOR_UPLOAD_PATH = 'images/uploads/{}'.format(time.strftime("%Y/%m/%d/")) -MARTOR_UPLOAD_URL = '/api/uploader/' # change to local uploader -MARTOR_MARKDOWN_BASE_EMOJI_URL = '/static/images/emojis/' + +MEDIA_ROOT = str(APPS_DIR("media")) + + MAX_IMAGE_UPLOAD_SIZE = 5242880 # 5MB # See: https://docs.djangoproject.com/en/dev/ref/settings/#media-url -MEDIA_URL = '/media/' +MEDIA_URL = "/media/" # URL Configuration # ------------------------------------------------------------------------------ -ROOT_URLCONF = 'config.urls' +ROOT_URLCONF = "config.urls" # See: https://docs.djangoproject.com/en/dev/ref/settings/#wsgi-application -WSGI_APPLICATION = 'config.wsgi.application' +WSGI_APPLICATION = "config.wsgi.application" # PASSWORD VALIDATION @@ -332,50 +332,50 @@ WSGI_APPLICATION = 'config.wsgi.application' AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator" + }, + {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"}, + { + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator" }, { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator" }, ] # PASSWORD HASHING PASSWORD_HASHERS = [ - 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', - 'django.contrib.auth.hashers.Argon2PasswordHasher', - 'django.contrib.auth.hashers.PBKDF2PasswordHasher', - 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', - 'django.contrib.auth.hashers.BCryptPasswordHasher', + "django.contrib.auth.hashers.BCryptSHA256PasswordHasher", + "django.contrib.auth.hashers.Argon2PasswordHasher", + "django.contrib.auth.hashers.PBKDF2PasswordHasher", + "django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher", + "django.contrib.auth.hashers.BCryptPasswordHasher", ] # AUTHENTICATION CONFIGURATION # ------------------------------------------------------------------------------ AUTHENTICATION_BACKENDS = ( - 'django.contrib.auth.backends.ModelBackend', - 'allauth.account.auth_backends.AuthenticationBackend', + "django.contrib.auth.backends.ModelBackend", + "allauth.account.auth_backends.AuthenticationBackend", ) # Some really nice defaults -ACCOUNT_AUTHENTICATION_METHOD = 'username' +ACCOUNT_AUTHENTICATION_METHOD = "username" ACCOUNT_EMAIL_REQUIRED = True -ACCOUNT_EMAIL_VERIFICATION = 'mandatory' +ACCOUNT_EMAIL_VERIFICATION = "mandatory" -ACCOUNT_ALLOW_REGISTRATION = env.bool('DJANGO_ACCOUNT_ALLOW_REGISTRATION', True) -ACCOUNT_ADAPTER = 'mhackspace.users.adapters.AccountAdapter' -SOCIALACCOUNT_ADAPTER = 'mhackspace.users.adapters.SocialAccountAdapter' +ACCOUNT_ALLOW_REGISTRATION = env.bool( + "DJANGO_ACCOUNT_ALLOW_REGISTRATION", True +) +ACCOUNT_ADAPTER = "mhackspace.users.adapters.AccountAdapter" +SOCIALACCOUNT_ADAPTER = "mhackspace.users.adapters.SocialAccountAdapter" SOCIALACCOUNT_QUERY_EMAIL = True # Custom user app defaults # Select the correct user model -AUTH_USER_MODEL = 'users.User' -LOGIN_REDIRECT_URL = 'users:redirect' +AUTH_USER_MODEL = "users.User" +LOGIN_REDIRECT_URL = "users:redirect" WIKI_ACCOUNT_HANDLING = False # WIKI_EDITOR = 'wiki.editors.martor.Martor' # WIKI_EDITOR_INCLUDE_JAVASCRIPT = False @@ -392,80 +392,80 @@ MARTOR_UPLOAD_URL = '/api/uploader/' # change to local uploader MAX_IMAGE_UPLOAD_SIZE = 10485760 # 10MB # SLUGLIFIER -AUTOSLUG_SLUGIFY_FUNCTION = 'slugify.slugify' +AUTOSLUG_SLUGIFY_FUNCTION = "slugify.slugify" # CELERY -CELERY_BROKER_URL = env('CELERY_BROKER_URL', default='redis://redis:6379/0') -CELERY_RESULT_BACKEND = 'django-db' +CELERY_BROKER_URL = env("CELERY_BROKER_URL", default="redis://redis:6379/0") +CELERY_RESULT_BACKEND = "django-db" CELERY_IGNORE_RESULT = False CELERY_REDIS_HOST = "redis" CELERY_REDIS_PORT = 6379 CELERY_REDIS_DB = 0 -CELERY_BEAT_SCHEDULER = 'django_celery_beat.schedulers:DatabaseScheduler' -INSTALLED_APPS += ('django_celery_results', 'django_celery_beat',) -CELERY_TIMEZONE = 'UTC' +CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler" +INSTALLED_APPS += ("django_celery_results", "django_celery_beat") +CELERY_TIMEZONE = "UTC" CELERY_ENABLE_UTC = True -CELERY_TASK_SERIALIZER = 'json' -CELERY_RESULT_SERIALIZER = 'json' +CELERY_TASK_SERIALIZER = "json" +CELERY_RESULT_SERIALIZER = "json" # END CELERY # django-compressor # ------------------------------------------------------------------------------ -INSTALLED_APPS += ("compressor", 'sass_processor',) -INSTALLED_APPS += ('django_extensions', ) -INSTALLED_APPS += ('storages', ) -INSTALLED_APPS += ('gunicorn', ) -STATICFILES_FINDERS += ("compressor.finders.CompressorFinder", ) -#STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' -DEFAULT_FILE_STORAGE = 'mhackspace.core.storage.MediaStorage' -STATICFILES_STORAGE = 'mhackspace.core.storage.StaticStorage' -#COMPRESS_STORAGE = STATICFILES_STORAGE +INSTALLED_APPS += ("compressor", "sass_processor") +INSTALLED_APPS += ("django_extensions",) +INSTALLED_APPS += ("storages",) +INSTALLED_APPS += ("gunicorn",) +STATICFILES_FINDERS += ("compressor.finders.CompressorFinder",) +DEFAULT_FILE_STORAGE = "mhackspace.core.storage.MediaStorage" +STATICFILES_STORAGE = "mhackspace.core.storage.StaticStorage" +# COMPRESS_STORAGE = STATICFILES_STORAGE # Location of root django.contrib.admin URL, use {% url 'admin:index' %} -ADMIN_URL = '^trustee/' +ADMIN_URL = "^trustee/" # Your common stuff: Below this line define 3rd party library settings # ------------------------------------------------------------------------------ PAYMENT_PROVIDERS = { - 'braintree': { - 'mode': env('PAYMENT_ENVIRONMENT'), - 'redirect_url': env('PAYMENT_REDIRECT_URL'), - 'credentials': { - 'merchant_id': env('BRAINTREE_MERCHANT_ID'), - 'public_key': env('BRAINTREE_PUBLIC_KEY'), - 'private_key': env('BRAINTREE_PRIVATE_KEY'), - } + "braintree": { + "mode": env("PAYMENT_ENVIRONMENT"), + "redirect_url": env("PAYMENT_REDIRECT_URL"), + "credentials": { + "merchant_id": env("BRAINTREE_MERCHANT_ID"), + "public_key": env("BRAINTREE_PUBLIC_KEY"), + "private_key": env("BRAINTREE_PRIVATE_KEY"), + }, }, - 'paypal': { - "mode": env('PAYMENT_ENVIRONMENT'), # sandbox or live - 'redirect_url': env('PAYMENT_REDIRECT_URL'), - 'credentials': { + "paypal": { + "mode": env("PAYMENT_ENVIRONMENT"), # sandbox or live + "redirect_url": env("PAYMENT_REDIRECT_URL"), + "credentials": { "mode": "sandbox", # sandbox or live - "client_id": env('PAYPAL_CLIENT_ID'), - "client_secret": env('PAYPAL_CLIENT_SECRET')} + "client_id": env("PAYPAL_CLIENT_ID"), + "client_secret": env("PAYPAL_CLIENT_SECRET"), }, - 'gocardless': { - 'environment': env('PAYMENT_ENVIRONMENT'), - 'redirect_url': env('PAYMENT_REDIRECT_URL'), - 'credentials': { - 'app_id': env('GOCARDLESS_APP_ID'), - 'app_secret': env('GOCARDLESS_APP_SECRET'), - 'access_token': env('GOCARDLESS_ACCESS_TOKEN'), - 'merchant_id': env('GOCARDLESS_MERCHANT_ID'), + }, + "gocardless": { + "environment": env("PAYMENT_ENVIRONMENT"), + "redirect_url": env("PAYMENT_REDIRECT_URL"), + "credentials": { + "app_id": env("GOCARDLESS_APP_ID"), + "app_secret": env("GOCARDLESS_APP_SECRET"), + "access_token": env("GOCARDLESS_ACCESS_TOKEN"), + "merchant_id": env("GOCARDLESS_MERCHANT_ID"), }, - } + }, } SASS_PRECISION = 8 # Important this fixes permission issues by compiling in a temporary folder, instead of inside your project -SASS_PROCESSOR_ROOT = os.path.join('/tmp', 'sass') +SASS_PROCESSOR_ROOT = os.path.join("/tmp", "sass") SASS_PROCESSOR_INCLUDE_DIRS = [ - str(APPS_DIR) + '/static/sass', - str(ROOT_DIR) + '/node_modules', + str(APPS_DIR) + "/static/sass", + str(ROOT_DIR) + "/node_modules", ] @@ -473,98 +473,151 @@ SASS_PROCESSOR_ENABLED = True SASS_PROCESSOR_AUTO_INCLUDE = True EMAIL_NOTIFY = True -EMAIL_SUPPORT = 'support@maidstone-hackspace.org.uk' -EMAIL_MAILING_LIST = 'maidstone-hackspace@googlegroups.com' +EMAIL_SUPPORT = "support@maidstone-hackspace.org.uk" +EMAIL_MAILING_LIST = "maidstone-hackspace@googlegroups.com" REST_FRAMEWORK = { - 'DEFAULT_FILTER_BACKENDS': ( - 'rest_framework.filters.SearchFilter', - 'django_filters.rest_framework.DjangoFilterBackend', - 'rest_framework.filters.OrderingFilter' + "DEFAULT_FILTER_BACKENDS": ( + "rest_framework.filters.SearchFilter", + "django_filters.rest_framework.DjangoFilterBackend", + "rest_framework.filters.OrderingFilter", ), - 'DEFAULT_PERMISSION_CLASSES': ( + "DEFAULT_PERMISSION_CLASSES": ( # 'rest_framework.permissions.IsAuthenticated', # 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly', ), - 'DEFAULT_AUTHENTICATION_CLASSES': ( + "DEFAULT_AUTHENTICATION_CLASSES": ( # 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', # 'rest_framework.authentication.BasicAuthentication', - 'rest_framework.authentication.SessionAuthentication', - 'rest_framework.authentication.TokenAuthentication', + "rest_framework.authentication.SessionAuthentication", + "rest_framework.authentication.TokenAuthentication", ), - 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', - 'PAGE_SIZE': 50 + "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination", + "PAGE_SIZE": 50, } # Deprecated need removing, sorl plugin still expects TEMPLATE_DEBUG so for now we need it just for this plugin TEMPLATE_DEBUG = False -CORS_ORIGIN_WHITELIST = ( - 'matrix.org', - 'vector.im', - 'riot.im' -) +CORS_ORIGIN_WHITELIST = ("matrix.org", "vector.im", "riot.im") # Matrix chat settings -MATRIX_USER=env('MATRIX_USERNAME') -MATRIX_PASSWORD=env('MATRIX_PASSWORD') -MATRIX_ROOM={ - 'default': env('MATRIX_ROOM', default='fmCpNwqgIiuwATlcdw:matrix.org'), - 'admin': 'SiUlbxziFQjndQQTvl:matrix.org', - 'piwars': 'ilIDnMSGUKsejBFkmh:matrix.org' +MATRIX_USER = env("MATRIX_USERNAME") +MATRIX_PASSWORD = env("MATRIX_PASSWORD") +MATRIX_ROOM = { + "default": env("MATRIX_ROOM", default="fmCpNwqgIiuwATlcdw:matrix.org"), + "admin": "SiUlbxziFQjndQQTvl:matrix.org", + "piwars": "ilIDnMSGUKsejBFkmh:matrix.org", } -MSG_PREFIX = 'MH' -X_FRAME_OPTIONS = 'SAMEORIGIN' +MSG_PREFIX = "MH" +X_FRAME_OPTIONS = "SAMEORIGIN" -#Twitter messageing settings -TWITTER_CONSUMER_KEY=env('TWITTER_CONSUMER_KEY') -TWITTER_CONSUMER_SECRET=env('TWITTER_CONSUMER_SECRET') -TWITTER_ACCESS_TOKEN=env('TWITTER_ACCESS_TOKEN') -TWITTER_ACCESS_SECRET=env('TWITTER_ACCESS_SECRET') +# Twitter messageing settings +TWITTER_CONSUMER_KEY = env("TWITTER_CONSUMER_KEY") +TWITTER_CONSUMER_SECRET = env("TWITTER_CONSUMER_SECRET") +TWITTER_ACCESS_TOKEN = env("TWITTER_ACCESS_TOKEN") +TWITTER_ACCESS_SECRET = env("TWITTER_ACCESS_SECRET") -LOCATION_PREFIX = env('BUCKET_PREFIX_PATH', default='') -MEDIAFILE_LOCATION = LOCATION_PREFIX + 'media' -STATICFILE_LOCATION = LOCATION_PREFIX + 'static' -AWS_DEFAULT_ACL = 'public-read' -AWS_S3_OBJECT_PARAMETERS = { - 'CacheControl': 'max-age=86400', -} +LOCATION_PREFIX = env("BUCKET_PREFIX_PATH", default="") +MEDIAFILE_LOCATION = LOCATION_PREFIX + "media" +STATICFILE_LOCATION = LOCATION_PREFIX + "static" -COMPRESS_URL = 'cache/' +COMPRESS_URL = "cache/" -# django-debug-toolbar -# ---------------------MDVTDNXFTRJSJBX9KWOJTMCGSNMYASEFNBPDUZJMGSPPCVMQRUZMZAEXDTIGHPZCP9JBGLVKGSJMZKPVV--------------------------------------------------------- - -ALLOWED_HOSTS = ['*'] -INTERNAL_IPS = ['127.0.0.1', '10.0.2.2', '172.22.0.9', '192.168.1.113', '172.22.0.4', '0.0.0.0', '192.168.1.64'] +ALLOWED_HOSTS = ["*"] +INTERNAL_IPS = [ + "127.0.0.1", + "10.0.2.2", + "172.22.0.9", + "192.168.1.113", + "172.22.0.4", + "0.0.0.0", + "192.168.1.64", +] # tricks to have debug toolbar when developing with docker -if os.environ.get('USE_DOCKER') == 'yes': +if os.environ.get("USE_DOCKER") == "yes": ip = socket.gethostbyname(socket.gethostname()) INTERNAL_IPS += [ip[:-1] + "1"] # CACHING # ------------------------------------------------------------------------------ -REDIS_LOCATION = '{0}/{1}'.format(env('REDIS_URL', default='redis://redis:6379'), 0) +REDIS_LOCATION = "{0}/{1}".format( + env("REDIS_URL", default="redis://redis:6379"), 0 +) # Heroku URL does not pass the DB number, so we parse it in CACHES = { - 'default': { - 'BACKEND': 'django_redis.cache.RedisCache', - 'LOCATION': REDIS_LOCATION, - 'OPTIONS': { - 'CLIENT_CLASS': 'django_redis.client.DefaultClient', - 'IGNORE_EXCEPTIONS': True, # mimics memcache behavior. - # http://niwinz.github.io/django-redis/latest/#_memcached_exceptions_behavior - } + "default": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": REDIS_LOCATION, + "OPTIONS": { + "CLIENT_CLASS": "django_redis.client.DefaultClient", + "IGNORE_EXCEPTIONS": True, # mimics memcache behavior. + # http://niwinz.github.io/django-redis/latest/#_memcached_exceptions_behavior + }, + }, + "st_rate_limit": { + "BACKEND": "django.core.cache.backends.locmem.LocMemCache", + "LOCATION": "spirit_rl_cache", + "TIMEOUT": None, }, - 'st_rate_limit': { - 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', - 'LOCATION': 'spirit_rl_cache', - 'TIMEOUT': None - } } -LDAP_SERVER = env('LDAP_SERVER', default='172.19.0.6') -LDAP_PASSWORD = env('LDAP_ADMIN_PASSWORD', default='secretldappassword') -LDAP_ROOT = env('LDAP_ROOT', default='dc=maidstone-hackspace, dc=org, dc=uk') +LDAP_SERVER = env("LDAP_SERVER", default="172.19.0.6") +LDAP_PASSWORD = env("LDAP_ADMIN_PASSWORD", default="secretldappassword") +LDAP_ROOT = env("LDAP_ROOT", default="dc=maidstone-hackspace, dc=org, dc=uk") + +# Start Martor markdown editor settings +MARTOR_ENABLE_CONFIGS = { + "imgur": "true", # to enable/disable imgur uploader/custom uploader. + "mention": "true", # to enable/disable mention + "jquery": "true", # to include/revoke jquery (require for admin default django) +} +MARTOR_UPLOAD_PATH = "images/uploads/{}".format(time.strftime("%Y/%m/%d/")) +MARTOR_UPLOAD_URL = "/api/uploader/" # change to local uploader +MARTOR_UPLOAD_PATH = "images/uploads/{}".format(time.strftime("%Y/%m/%d/")) +MARTOR_UPLOAD_URL = "/api/uploader/" # change to local uploader + +MARTOR_MARKDOWN_BASE_EMOJI_USE_STATIC = True +MARTOR_MARKDOWN_BASE_EMOJI_URL = "images/emojis/" + +# End Martor markdown editor settings + +# Start ST is Spirit forum software config +ST_TOPIC_PRIVATE_CATEGORY_PK = 1 +ST_RATELIMIT_ENABLE = True +ST_RATELIMIT_CACHE_PREFIX = "srl" +ST_RATELIMIT_CACHE = "default" +ST_RATELIMIT_SKIP_TIMEOUT_CHECK = False +ST_NOTIFICATIONS_PER_PAGE = 20 +ST_COMMENT_MAX_LEN = 3000 +ST_MENTIONS_PER_COMMENT = 30 +ST_DOUBLE_POST_THRESHOLD_MINUTES = 30 +ST_YT_PAGINATOR_PAGE_RANGE = 3 +ST_SEARCH_QUERY_MIN_LEN = 3 +ST_USER_LAST_SEEN_THRESHOLD_MINUTES = 1 +ST_PRIVATE_FORUM = False +ST_ALLOWED_UPLOAD_IMAGE_FORMAT = ("jpeg", "png", "gif") +ST_ALLOWED_URL_PROTOCOLS = { + "http", + "https", + "mailto", + "ftp", + "ftps", + "git", + "svn", + "magnet", + "irc", + "ircs", +} + +ST_UNICODE_SLUGS = True +ST_UNIQUE_EMAILS = True +ST_CASE_INSENSITIVE_EMAILS = True +ST_UPLOAD_IMAGE_ENABLED = True +ST_UPLOAD_FILE_ENABLED = True + +ST_TESTS_RATELIMIT_NEVER_EXPIRE = False +ST_BASE_DIR = os.path.dirname(__file__) +# ST is Spirit forum software config diff --git a/mhackspace/blog/feeds.py b/mhackspace/blog/feeds.py index c78b121..963a0a6 100644 --- a/mhackspace/blog/feeds.py +++ b/mhackspace/blog/feeds.py @@ -1,4 +1,5 @@ from collections import OrderedDict +from django.utils.html import escape from django.contrib.syndication.views import Feed, add_domain from django.contrib.sites.shortcuts import get_current_site from django.utils import timezone @@ -54,7 +55,7 @@ class RssFeed(Feed): return post.get('title') def item_description(self, post): - return post.get('description') + return escape(post.get('description')) def item_author_name(self, post): return post.get('author') @@ -102,7 +103,7 @@ class BlogFeed(Feed): return post.title def item_description(self, post): - return post.description + return escape(post.description) def item_author_name(self, post): return post.author.name @@ -146,4 +147,4 @@ class BlogCategoryFeed(BlogFeed): return "Maidstone Hackspace Blog: %s" % category.name def description(self, category): - return category.description + return escape(category.description) diff --git a/mhackspace/blog/reader.py b/mhackspace/blog/reader.py deleted file mode 100644 index 2b5d876..0000000 --- a/mhackspace/blog/reader.py +++ /dev/null @@ -1,16 +0,0 @@ -import feedparser - -urls = [ - 'https://feeds.feedburner.com/projects-jl', - 'https://hackaday.com/tag/emf-camp-2018/feed/', - 'https://maidstone-hackspace.org.uk/blog/rss/', - 'http://webboggles.com/feed/', - 'https://blog.digitaloctave.com/rss.xml', -] - -for url in urls: - print(url) - parsed = feedparser.parse(url) - - for post in parsed.entries: - print(post.title) diff --git a/mhackspace/feeds/helper.py b/mhackspace/feeds/helper.py index 0aa4176..baf12b4 100644 --- a/mhackspace/feeds/helper.py +++ b/mhackspace/feeds/helper.py @@ -3,10 +3,14 @@ import os import logging import feedparser +from time import mktime +from datetime import datetime from urllib.request import urlretrieve from django.core.files import File from django.utils.timezone import make_aware +from django.utils import timezone from stdimage.utils import render_variations +from mhackspace.feeds.reader import fetch_feeds # from scaffold.readers.rss_reader import feed_reader @@ -18,24 +22,29 @@ logger = logging.getLogger(__name__) def feed_reader(feeds): for feed in feeds: print(feed) - yield feedparser.parse(feed['url']) + yield feedparser.parse(feed["url"]) def import_feeds(feed=False): remove_old_articles() - rss_articles = feed_reader(get_active_feeds(feed)) + + print([f.get("url") for f in get_active_feeds(feed)]) + rss_articles = fetch_feeds(get_active_feeds(feed)) articles = [] for article in rss_articles: - print(article) + date = datetime.fromtimestamp(mktime(article["date"])) + print(article["title"]) + print(article["image"]) + print('#############') articles.append( Article( - url=article["url"].decode(), - feed=Feed.objects.get(pk=article["id"]), - title=article["title"].decode(), - original_image=article["image"], - description=article["description"].decode(), - date=make_aware(article["date"]), + url=article["url"], + feed=Feed.objects.get(pk=article["feed"]), + title=article["title"][0:100], + original_image=article["image"][0:100], + description=article["description"], + date=date, ) ) diff --git a/mhackspace/feeds/reader.py b/mhackspace/feeds/reader.py new file mode 100644 index 0000000..c0df1b4 --- /dev/null +++ b/mhackspace/feeds/reader.py @@ -0,0 +1,131 @@ +import lxml +import feedparser +from operator import itemgetter +from lxml import etree +from lxml.html.clean import Cleaner +from io import StringIO, BytesIO + +namespaces = {} +urls = [ + "https://feeds.feedburner.com/projects-jl", + "https://hackaday.com/tag/emf-camp-2018/feed/", + "https://maidstone-hackspace.org.uk/blog/rss/", + "http://webboggles.com/feed/", + "https://blog.digitaloctave.com/rss.xml", +] +html_parser = lxml.etree.HTMLParser() + + +def parse_content(content): + headers = { + "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20100101 Firefox/10.0" + } + html_cleaner = Cleaner() + html_cleaner.javascript = True + html_cleaner.style = True + html_cleaner.remove_tags = [ + "script", + "iframe", + "link", + "style", + "img", + "div", + ] + # ~ html_cleaner.allow_tags = ['a', 'p', 'strong'] + + html_img_cleaner = Cleaner(allow_tags=["img"], remove_unknown_tags=False) + html_img_cleaner.allow_tags = ["img"] + + xml_parser = lxml.etree.XMLParser( + remove_blank_text=True, ns_clean=True, encoding="utf-8" + ) + + print("------------------") + print(content) + + dom = lxml.etree.XML("
" + content + "
", xml_parser) + return dom + + +def fetch_image_from_node_text(text): + description = lxml.etree.parse(text, html_parser) + for image in description.xpath(".//img"): + print('fetch image from node text') + return image.get("src") + return None + + +def fetch_node_text(node, name, default=u""): + """fetch the text from the node we are given, we are working in unicode + so decode byte strings to unicode""" + result = node.xpath("./%s" % name) + if result is None or len(result) is 0: + return default + + if type(result[-1].text) is str: + return result[-1].text.encode("utf-8") + else: + return result[-1].text + + +def fetch_image(post, node, namespaces): + """Try and get an image from an item in the feed, use various fall back methods""" + if hasattr(post, "media_thumbnail"): + print('media') + + image = post.media_thumbnail + print(image) + + if image: + return image[0].get("url") + + if hasattr(post, "content"): + print('content') + content = " ".join(c.value for c in post.content) + image = fetch_image_from_node_text(content) + if image: + return image + + # final attempt at getting an image from the item using description + result = fetch_node_text(node, "description") + if result: + print('description') + image = fetch_image_from_node_text(result) + if image: + return image + + # no image so lets fall back to the channel image if it exists + return None + + +def fetch_feeds(feeds): + for feed in feeds: + url = feed.get('url') + print(url) + parsed = feedparser.parse(url) + namespaces = {} + if hasattr(parsed, "namespaces"): + namespaces = parsed.namespaces + feed_image = "" + if hasattr(parsed.feed, "image"): + feed_image = parsed.feed.image.get('href') + articles = [] + for post in parsed.entries: + print(post.published) + print(feed_image) + root_node = parse_content(post.description) + image = fetch_image(post, root_node, namespaces) #or feed_image + + articles.append( + { + "url": post.link, + "feed": feed.get('id'), + "title": post.title, + "original_image": image, + "description": post.description, + "date": post.published_parsed, + "image": feed_image, + } + ) + print(articles[-1]) + return articles diff --git a/mhackspace/templates/pages/chat.html b/mhackspace/templates/pages/chat.html index b26f718..2066070 100644 --- a/mhackspace/templates/pages/chat.html +++ b/mhackspace/templates/pages/chat.html @@ -15,6 +15,7 @@ Full screen chat +

Connect directly using irc.freenode.org and the #maidstone-hackspace channel via an irc client.

diff --git a/mhackspace/templates/pages/contact.html b/mhackspace/templates/pages/contact.html index f03d8c0..b237c26 100644 --- a/mhackspace/templates/pages/contact.html +++ b/mhackspace/templates/pages/contact.html @@ -7,11 +7,10 @@

Contact us

Please fill in details below, and we will get back to you when possible, alternatively try our live chat.
- + Click here to chat with us -
{% csrf_token %} {{ form|crispy }} diff --git a/mhackspace/templates/pages/home.html b/mhackspace/templates/pages/home.html index e35ba55..7c961c7 100644 --- a/mhackspace/templates/pages/home.html +++ b/mhackspace/templates/pages/home.html @@ -19,7 +19,7 @@

Introduction

Hackspaces (also known as makerspaces) are a shared space where artists, designers, makers, hackers, programmers, tinkerers, professionals and hobbyists can work on their projects, share knowledge and collaborate.

-

We now how a space as of March 2017, we are now in the process of kitting it out with tools to facilitate working on your projects. At the moment, communication is via a mailing list, email, and Chat. If you're at all intrested please join our mailing list and say hi.

+

We now how a space as of March 2017, we are now in the process of kitting it out with tools to facilitate working on your projects. At the moment, communication is via a mailing list, email, and Chat. If you're at all intrested please join our mailing list and say hi.

If you would like to visit us click here for a map, and check the mailing list for when we are at the space, we recommend contacting us to avoid disapointment.

diff --git a/requirements/base.txt b/requirements/base.txt index 478642b..b2bbb7d 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -83,9 +83,8 @@ django-filter==2.0.0 coreapi==2.3.3 # api libraries end -martor==1.3.2 - -#git+git://github.com/olymk2/django-markdown-editor.git +#martor==1.3.2 +git+git://github.com/olymk2/django-markdown-editor.git django-spirit==0.6.1 django-djconfig==0.8.0