Add open graph data to the wiki and other pages

This commit is contained in:
Oliver Marks 2018-09-15 08:52:50 +01:00
parent 83c71485db
commit aecd76b567
9 changed files with 495 additions and 318 deletions

View File

@ -16,11 +16,13 @@ import environ
import socket import socket
# from spirit.settings import * # from spirit.settings import *
ROOT_DIR = environ.Path(__file__) - 3 # (mhackspace/config/settings/common.py - 3 = mhackspace/) ROOT_DIR = (
APPS_DIR = ROOT_DIR.path('mhackspace') environ.Path(__file__) - 3
) # (mhackspace/config/settings/common.py - 3 = mhackspace/)
APPS_DIR = ROOT_DIR.path("mhackspace")
env = environ.Env() env = environ.Env()
env.read_env('%s/.env' % ROOT_DIR) env.read_env("%s/.env" % ROOT_DIR)
# Start ST is Spirit forum software config # Start ST is Spirit forum software config
ST_TOPIC_PRIVATE_CATEGORY_PK = 1 ST_TOPIC_PRIVATE_CATEGORY_PK = 1
@ -53,102 +55,100 @@ ST_BASE_DIR = os.path.dirname(__file__)
# END ST is Spirit forum software config # 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 = { HAYSTACK_CONNECTIONS = {
'default': { "default": {
'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine', "ENGINE": "haystack.backends.whoosh_backend.WhooshEngine",
'PATH': os.path.join(os.path.dirname(__file__), 'search', 'whoosh_index'), "PATH": os.path.join(
}, os.path.dirname(__file__), "search", "whoosh_index"
),
}
} }
# APP CONFIGURATION # APP CONFIGURATION
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
DJANGO_APPS = ( DJANGO_APPS = (
# Default Django apps: # Default Django apps:
'django.contrib.auth', "django.contrib.auth",
'django.contrib.contenttypes', "django.contrib.contenttypes",
'django.contrib.sessions', "django.contrib.sessions",
'django.contrib.sites', "django.contrib.sites",
'django.contrib.messages', "django.contrib.messages",
'django.contrib.sitemaps', "django.contrib.sitemaps",
'django.contrib.staticfiles', "django.contrib.staticfiles",
'django.contrib.humanize', "django.contrib.humanize",
'django.contrib.admin', "django.contrib.admin",
) )
THIRD_PARTY_APPS = ( THIRD_PARTY_APPS = (
'crispy_forms', # Form layouts "crispy_forms", # Form layouts
'allauth', # registration "allauth", # registration
'allauth.account', # registration "allauth.account", # registration
'allauth.socialaccount', # registration "allauth.socialaccount", # registration
'allauth.socialaccount.providers.google', # registration "allauth.socialaccount.providers.google", # registration
'allauth.socialaccount.providers.github', # registration "allauth.socialaccount.providers.github", # registration
# 'allauth.socialaccount.providers.facebook', # registration # 'allauth.socialaccount.providers.facebook', # registration
'whitenoise.runserver_nostatic', "whitenoise.runserver_nostatic",
'stdimage', "stdimage",
'rest_framework', "rest_framework",
'django_filters', "django_filters",
'martor', "martor",
'haystack', "haystack",
'djconfig', "djconfig",
"corsheaders",
'corsheaders', "spirit.core",
"spirit.admin",
'spirit.core', "spirit.search",
'spirit.admin', "spirit.user",
'spirit.search', "spirit.user.admin",
"spirit.user.auth",
'spirit.user', "spirit.category",
'spirit.user.admin', "spirit.category.admin",
'spirit.user.auth', "spirit.topic",
"spirit.topic.admin",
'spirit.category', "spirit.topic.favorite",
'spirit.category.admin', "spirit.topic.moderate",
"spirit.topic.notification",
'spirit.topic', "spirit.topic.poll", # todo: remove in Spirit v0.6
'spirit.topic.admin', "spirit.topic.private",
'spirit.topic.favorite', "spirit.topic.unread",
'spirit.topic.moderate', "spirit.comment",
'spirit.topic.notification', "spirit.comment.bookmark",
'spirit.topic.poll', # todo: remove in Spirit v0.6 "spirit.comment.flag",
'spirit.topic.private', "spirit.comment.flag.admin",
'spirit.topic.unread', "spirit.comment.history",
"spirit.comment.like",
'spirit.comment', "spirit.comment.poll",
'spirit.comment.bookmark', "django_nyt",
'spirit.comment.flag', "mptt",
'spirit.comment.flag.admin', "sekizai",
'spirit.comment.history', "sorl.thumbnail",
'spirit.comment.like', "wiki",
'spirit.comment.poll', "wiki.plugins.attachments",
"wiki.plugins.notifications",
'django_nyt', "wiki.plugins.images",
'mptt', "wiki.plugins.macros",
'sekizai',
'sorl.thumbnail',
'wiki',
'wiki.plugins.attachments',
'wiki.plugins.notifications',
'wiki.plugins.images',
'wiki.plugins.macros',
) )
# Apps specific for this project go here. # Apps specific for this project go here.
LOCAL_APPS = ( LOCAL_APPS = (
# custom users app # custom users app
# Your stuff: custom apps go here # Your stuff: custom apps go here
'mhackspace.users.apps.UsersConfig', "mhackspace.users.apps.UsersConfig",
'mhackspace.base', "mhackspace.base",
'mhackspace.subscriptions', "mhackspace.subscriptions",
'mhackspace.feeds', "mhackspace.feeds",
'mhackspace.contact', "mhackspace.contact",
'mhackspace.members', "mhackspace.members",
'mhackspace.blog', "mhackspace.blog",
'mhackspace.core', "mhackspace.core",
'mhackspace.requests', "mhackspace.requests",
'mhackspace.register', "mhackspace.register",
'mhackspace.ldapsync', "mhackspace.ldapsync",
'mhackspace.rfid', "mhackspace.rfid",
) )
# See: https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps # 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 CONFIGURATION
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
MIDDLEWARE = ( MIDDLEWARE = (
'django.middleware.security.SecurityMiddleware', "django.middleware.security.SecurityMiddleware",
'whitenoise.middleware.WhiteNoiseMiddleware', "whitenoise.middleware.WhiteNoiseMiddleware",
'django.contrib.sessions.middleware.SessionMiddleware', "django.contrib.sessions.middleware.SessionMiddleware",
'corsheaders.middleware.CorsMiddleware', "corsheaders.middleware.CorsMiddleware",
'django.middleware.common.CommonMiddleware', "django.middleware.common.CommonMiddleware",
'django.middleware.csrf.CsrfViewMiddleware', "django.middleware.csrf.CsrfViewMiddleware",
'django.contrib.auth.middleware.AuthenticationMiddleware', "django.contrib.auth.middleware.AuthenticationMiddleware",
'django.contrib.messages.middleware.MessageMiddleware', "django.contrib.messages.middleware.MessageMiddleware",
'django.middleware.clickjacking.XFrameOptionsMiddleware', "django.middleware.clickjacking.XFrameOptionsMiddleware",
# fix for ip logging behind a proxy
#fix for ip logging behind a proxy "x_forwarded_for.middleware.XForwardedForMiddleware",
'x_forwarded_for.middleware.XForwardedForMiddleware', "djconfig.middleware.DjConfigMiddleware",
'djconfig.middleware.DjConfigMiddleware', "spirit.user.middleware.TimezoneMiddleware",
"spirit.user.middleware.LastIPMiddleware",
'spirit.user.middleware.TimezoneMiddleware', "spirit.user.middleware.LastSeenMiddleware",
'spirit.user.middleware.LastIPMiddleware', "spirit.user.middleware.ActiveUserMiddleware",
'spirit.user.middleware.LastSeenMiddleware', "spirit.core.middleware.PrivateForumMiddleware",
'spirit.user.middleware.ActiveUserMiddleware',
'spirit.core.middleware.PrivateForumMiddleware',
) )
# MIGRATIONS CONFIGURATION # MIGRATIONS CONFIGURATION
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
MIGRATION_MODULES = { MIGRATION_MODULES = {"sites": "mhackspace.contrib.sites.migrations"}
'sites': 'mhackspace.contrib.sites.migrations'
}
# DEBUG # DEBUG
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# See: https://docs.djangoproject.com/en/dev/ref/settings/#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 # FIXTURE CONFIGURATION
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# See: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FIXTURE_DIRS # See: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FIXTURE_DIRS
FIXTURE_DIRS = ( FIXTURE_DIRS = (str(APPS_DIR.path("fixtures")),)
str(APPS_DIR.path('fixtures')),
)
# EMAIL CONFIGURATION # 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_PORT = 1025
EMAIL_HOST = env("EMAIL_HOST", default='mailhog') EMAIL_HOST = env("EMAIL_HOST", default="mailhog")
MSG_PREFIX = env("EMAIL_HOST", default='MHT') MSG_PREFIX = env("EMAIL_HOST", default="MHT")
# MANAGER CONFIGURATION # MANAGER CONFIGURATION
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# See: https://docs.djangoproject.com/en/dev/ref/settings/#admins # See: https://docs.djangoproject.com/en/dev/ref/settings/#admins
ADMINS = ( ADMINS = (("""Maidstone Hackspace""", "support@maidstone-hackspace.org.uk"),)
("""Maidstone Hackspace""", 'support@maidstone-hackspace.org.uk'),
)
# See: https://docs.djangoproject.com/en/dev/ref/settings/#managers # See: https://docs.djangoproject.com/en/dev/ref/settings/#managers
MANAGERS = ADMINS MANAGERS = ADMINS
@ -217,9 +212,9 @@ MANAGERS = ADMINS
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# See: https://docs.djangoproject.com/en/dev/ref/settings/#databases # See: https://docs.djangoproject.com/en/dev/ref/settings/#databases
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 # GENERAL CONFIGURATION
@ -228,10 +223,10 @@ DATABASES['default']['ATOMIC_REQUESTS'] = True
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems. # although not all choices may be available on all operating systems.
# In a Windows environment this must be set to your system time zone. # 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 # 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 # See: https://docs.djangoproject.com/en/dev/ref/settings/#site-id
SITE_ID = 1 SITE_ID = 1
@ -251,79 +246,84 @@ USE_TZ = True
TEMPLATES = [ TEMPLATES = [
{ {
# See: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-TEMPLATES-BACKEND # 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 # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-dirs
'DIRS': [ "DIRS": [str(APPS_DIR.path("templates"))],
str(APPS_DIR.path('templates')), "OPTIONS": {
],
'OPTIONS': {
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-debug # 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 # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-loaders
# https://docs.djangoproject.com/en/dev/ref/templates/api/#loader-types # https://docs.djangoproject.com/en/dev/ref/templates/api/#loader-types
'loaders': [ "loaders": [
'django.template.loaders.filesystem.Loader', "django.template.loaders.filesystem.Loader",
'django.template.loaders.app_directories.Loader', "django.template.loaders.app_directories.Loader",
], ],
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-context-processors # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-context-processors
'context_processors': [ "context_processors": [
'django.template.context_processors.debug', "django.template.context_processors.debug",
'django.template.context_processors.request', "django.template.context_processors.request",
'django.contrib.auth.context_processors.auth', "django.contrib.auth.context_processors.auth",
'django.template.context_processors.i18n', "django.template.context_processors.i18n",
'django.template.context_processors.media', "django.template.context_processors.media",
'django.template.context_processors.static', "django.template.context_processors.static",
'django.template.context_processors.tz', "django.template.context_processors.tz",
'django.contrib.messages.context_processors.messages', "django.contrib.messages.context_processors.messages",
'sekizai.context_processors.sekizai', "sekizai.context_processors.sekizai",
# Your stuff: custom template context processors go here # 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 # See: http://django-crispy-forms.readthedocs.io/en/latest/install.html#template-packs
CRISPY_TEMPLATE_PACK = 'bootstrap4' CRISPY_TEMPLATE_PACK = "bootstrap4"
# STATIC FILE CONFIGURATION # STATIC FILE CONFIGURATION
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# See: https://docs.djangoproject.com/en/dev/ref/settings/#static-root # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-root
STATIC_ROOT = str(ROOT_DIR('static')) STATIC_ROOT = str(ROOT_DIR("static"))
# See: https://docs.djangoproject.com/en/dev/ref/settings/#static-url
STATIC_URL = '/static/'
# See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS # See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS
STATICFILES_DIRS = ( STATICFILES_DIRS = (str(APPS_DIR.path("static")),)
str(APPS_DIR.path('static')),
)
# See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders # See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders
STATICFILES_FINDERS = ( STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder', "django.contrib.staticfiles.finders.FileSystemFinder",
'django.contrib.staticfiles.finders.AppDirectoriesFinder', "django.contrib.staticfiles.finders.AppDirectoriesFinder",
'sass_processor.finders.CssFinder', "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 # MEDIA CONFIGURATION
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# See: https://docs.djangoproject.com/en/dev/ref/settings/#media-root # 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/")) MEDIA_ROOT = str(APPS_DIR("media"))
MARTOR_UPLOAD_URL = '/api/uploader/' # change to local uploader
MARTOR_MARKDOWN_BASE_EMOJI_URL = '/static/images/emojis/'
MAX_IMAGE_UPLOAD_SIZE = 5242880 # 5MB MAX_IMAGE_UPLOAD_SIZE = 5242880 # 5MB
# See: https://docs.djangoproject.com/en/dev/ref/settings/#media-url # See: https://docs.djangoproject.com/en/dev/ref/settings/#media-url
MEDIA_URL = '/media/' MEDIA_URL = "/media/"
# URL Configuration # URL Configuration
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
ROOT_URLCONF = 'config.urls' ROOT_URLCONF = "config.urls"
# See: https://docs.djangoproject.com/en/dev/ref/settings/#wsgi-application # See: https://docs.djangoproject.com/en/dev/ref/settings/#wsgi-application
WSGI_APPLICATION = 'config.wsgi.application' WSGI_APPLICATION = "config.wsgi.application"
# PASSWORD VALIDATION # PASSWORD VALIDATION
@ -332,50 +332,50 @@ WSGI_APPLICATION = 'config.wsgi.application'
AUTH_PASSWORD_VALIDATORS = [ 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.NumericPasswordValidator"
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
}, },
] ]
# PASSWORD HASHING # PASSWORD HASHING
PASSWORD_HASHERS = [ PASSWORD_HASHERS = [
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', "django.contrib.auth.hashers.BCryptSHA256PasswordHasher",
'django.contrib.auth.hashers.Argon2PasswordHasher', "django.contrib.auth.hashers.Argon2PasswordHasher",
'django.contrib.auth.hashers.PBKDF2PasswordHasher', "django.contrib.auth.hashers.PBKDF2PasswordHasher",
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', "django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher",
'django.contrib.auth.hashers.BCryptPasswordHasher', "django.contrib.auth.hashers.BCryptPasswordHasher",
] ]
# AUTHENTICATION CONFIGURATION # AUTHENTICATION CONFIGURATION
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
AUTHENTICATION_BACKENDS = ( AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend', "django.contrib.auth.backends.ModelBackend",
'allauth.account.auth_backends.AuthenticationBackend', "allauth.account.auth_backends.AuthenticationBackend",
) )
# Some really nice defaults # Some really nice defaults
ACCOUNT_AUTHENTICATION_METHOD = 'username' ACCOUNT_AUTHENTICATION_METHOD = "username"
ACCOUNT_EMAIL_REQUIRED = True ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_EMAIL_VERIFICATION = 'mandatory' ACCOUNT_EMAIL_VERIFICATION = "mandatory"
ACCOUNT_ALLOW_REGISTRATION = env.bool('DJANGO_ACCOUNT_ALLOW_REGISTRATION', True) ACCOUNT_ALLOW_REGISTRATION = env.bool(
ACCOUNT_ADAPTER = 'mhackspace.users.adapters.AccountAdapter' "DJANGO_ACCOUNT_ALLOW_REGISTRATION", True
SOCIALACCOUNT_ADAPTER = 'mhackspace.users.adapters.SocialAccountAdapter' )
ACCOUNT_ADAPTER = "mhackspace.users.adapters.AccountAdapter"
SOCIALACCOUNT_ADAPTER = "mhackspace.users.adapters.SocialAccountAdapter"
SOCIALACCOUNT_QUERY_EMAIL = True SOCIALACCOUNT_QUERY_EMAIL = True
# Custom user app defaults # Custom user app defaults
# Select the correct user model # Select the correct user model
AUTH_USER_MODEL = 'users.User' AUTH_USER_MODEL = "users.User"
LOGIN_REDIRECT_URL = 'users:redirect' LOGIN_REDIRECT_URL = "users:redirect"
WIKI_ACCOUNT_HANDLING = False WIKI_ACCOUNT_HANDLING = False
# WIKI_EDITOR = 'wiki.editors.martor.Martor' # WIKI_EDITOR = 'wiki.editors.martor.Martor'
# WIKI_EDITOR_INCLUDE_JAVASCRIPT = False # WIKI_EDITOR_INCLUDE_JAVASCRIPT = False
@ -392,80 +392,80 @@ MARTOR_UPLOAD_URL = '/api/uploader/' # change to local uploader
MAX_IMAGE_UPLOAD_SIZE = 10485760 # 10MB MAX_IMAGE_UPLOAD_SIZE = 10485760 # 10MB
# SLUGLIFIER # SLUGLIFIER
AUTOSLUG_SLUGIFY_FUNCTION = 'slugify.slugify' AUTOSLUG_SLUGIFY_FUNCTION = "slugify.slugify"
# CELERY # CELERY
CELERY_BROKER_URL = env('CELERY_BROKER_URL', default='redis://redis:6379/0') CELERY_BROKER_URL = env("CELERY_BROKER_URL", default="redis://redis:6379/0")
CELERY_RESULT_BACKEND = 'django-db' CELERY_RESULT_BACKEND = "django-db"
CELERY_IGNORE_RESULT = False CELERY_IGNORE_RESULT = False
CELERY_REDIS_HOST = "redis" CELERY_REDIS_HOST = "redis"
CELERY_REDIS_PORT = 6379 CELERY_REDIS_PORT = 6379
CELERY_REDIS_DB = 0 CELERY_REDIS_DB = 0
CELERY_BEAT_SCHEDULER = 'django_celery_beat.schedulers:DatabaseScheduler' CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler"
INSTALLED_APPS += ('django_celery_results', 'django_celery_beat',) INSTALLED_APPS += ("django_celery_results", "django_celery_beat")
CELERY_TIMEZONE = 'UTC' CELERY_TIMEZONE = "UTC"
CELERY_ENABLE_UTC = True CELERY_ENABLE_UTC = True
CELERY_TASK_SERIALIZER = 'json' CELERY_TASK_SERIALIZER = "json"
CELERY_RESULT_SERIALIZER = 'json' CELERY_RESULT_SERIALIZER = "json"
# END CELERY # END CELERY
# django-compressor # django-compressor
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
INSTALLED_APPS += ("compressor", 'sass_processor',) INSTALLED_APPS += ("compressor", "sass_processor")
INSTALLED_APPS += ('django_extensions', ) INSTALLED_APPS += ("django_extensions",)
INSTALLED_APPS += ('storages', ) INSTALLED_APPS += ("storages",)
INSTALLED_APPS += ('gunicorn', ) INSTALLED_APPS += ("gunicorn",)
STATICFILES_FINDERS += ("compressor.finders.CompressorFinder", ) STATICFILES_FINDERS += ("compressor.finders.CompressorFinder",)
#STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' DEFAULT_FILE_STORAGE = "mhackspace.core.storage.MediaStorage"
DEFAULT_FILE_STORAGE = 'mhackspace.core.storage.MediaStorage' STATICFILES_STORAGE = "mhackspace.core.storage.StaticStorage"
STATICFILES_STORAGE = 'mhackspace.core.storage.StaticStorage' # COMPRESS_STORAGE = STATICFILES_STORAGE
#COMPRESS_STORAGE = STATICFILES_STORAGE
# Location of root django.contrib.admin URL, use {% url 'admin:index' %} # 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 # Your common stuff: Below this line define 3rd party library settings
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
PAYMENT_PROVIDERS = { PAYMENT_PROVIDERS = {
'braintree': { "braintree": {
'mode': env('PAYMENT_ENVIRONMENT'), "mode": env("PAYMENT_ENVIRONMENT"),
'redirect_url': env('PAYMENT_REDIRECT_URL'), "redirect_url": env("PAYMENT_REDIRECT_URL"),
'credentials': { "credentials": {
'merchant_id': env('BRAINTREE_MERCHANT_ID'), "merchant_id": env("BRAINTREE_MERCHANT_ID"),
'public_key': env('BRAINTREE_PUBLIC_KEY'), "public_key": env("BRAINTREE_PUBLIC_KEY"),
'private_key': env('BRAINTREE_PRIVATE_KEY'), "private_key": env("BRAINTREE_PRIVATE_KEY"),
} },
}, },
'paypal': { "paypal": {
"mode": env('PAYMENT_ENVIRONMENT'), # sandbox or live "mode": env("PAYMENT_ENVIRONMENT"), # sandbox or live
'redirect_url': env('PAYMENT_REDIRECT_URL'), "redirect_url": env("PAYMENT_REDIRECT_URL"),
'credentials': { "credentials": {
"mode": "sandbox", # sandbox or live "mode": "sandbox", # sandbox or live
"client_id": env('PAYPAL_CLIENT_ID'), "client_id": env("PAYPAL_CLIENT_ID"),
"client_secret": env('PAYPAL_CLIENT_SECRET')} "client_secret": env("PAYPAL_CLIENT_SECRET"),
}, },
'gocardless': { },
'environment': env('PAYMENT_ENVIRONMENT'), "gocardless": {
'redirect_url': env('PAYMENT_REDIRECT_URL'), "environment": env("PAYMENT_ENVIRONMENT"),
'credentials': { "redirect_url": env("PAYMENT_REDIRECT_URL"),
'app_id': env('GOCARDLESS_APP_ID'), "credentials": {
'app_secret': env('GOCARDLESS_APP_SECRET'), "app_id": env("GOCARDLESS_APP_ID"),
'access_token': env('GOCARDLESS_ACCESS_TOKEN'), "app_secret": env("GOCARDLESS_APP_SECRET"),
'merchant_id': env('GOCARDLESS_MERCHANT_ID'), "access_token": env("GOCARDLESS_ACCESS_TOKEN"),
"merchant_id": env("GOCARDLESS_MERCHANT_ID"),
}, },
} },
} }
SASS_PRECISION = 8 SASS_PRECISION = 8
# Important this fixes permission issues by compiling in a temporary folder, instead of inside your project # 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 = [ SASS_PROCESSOR_INCLUDE_DIRS = [
str(APPS_DIR) + '/static/sass', str(APPS_DIR) + "/static/sass",
str(ROOT_DIR) + '/node_modules', str(ROOT_DIR) + "/node_modules",
] ]
@ -473,98 +473,151 @@ SASS_PROCESSOR_ENABLED = True
SASS_PROCESSOR_AUTO_INCLUDE = True SASS_PROCESSOR_AUTO_INCLUDE = True
EMAIL_NOTIFY = True EMAIL_NOTIFY = True
EMAIL_SUPPORT = 'support@maidstone-hackspace.org.uk' EMAIL_SUPPORT = "support@maidstone-hackspace.org.uk"
EMAIL_MAILING_LIST = 'maidstone-hackspace@googlegroups.com' EMAIL_MAILING_LIST = "maidstone-hackspace@googlegroups.com"
REST_FRAMEWORK = { REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ( "DEFAULT_FILTER_BACKENDS": (
'rest_framework.filters.SearchFilter', "rest_framework.filters.SearchFilter",
'django_filters.rest_framework.DjangoFilterBackend', "django_filters.rest_framework.DjangoFilterBackend",
'rest_framework.filters.OrderingFilter' "rest_framework.filters.OrderingFilter",
), ),
'DEFAULT_PERMISSION_CLASSES': ( "DEFAULT_PERMISSION_CLASSES": (
# 'rest_framework.permissions.IsAuthenticated', # 'rest_framework.permissions.IsAuthenticated',
# 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly', # 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly',
), ),
'DEFAULT_AUTHENTICATION_CLASSES': ( "DEFAULT_AUTHENTICATION_CLASSES": (
# 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', # 'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
# 'rest_framework.authentication.BasicAuthentication', # 'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication', "rest_framework.authentication.SessionAuthentication",
'rest_framework.authentication.TokenAuthentication', "rest_framework.authentication.TokenAuthentication",
), ),
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
'PAGE_SIZE': 50 "PAGE_SIZE": 50,
} }
# Deprecated need removing, sorl plugin still expects TEMPLATE_DEBUG so for now we need it just for this plugin # Deprecated need removing, sorl plugin still expects TEMPLATE_DEBUG so for now we need it just for this plugin
TEMPLATE_DEBUG = False TEMPLATE_DEBUG = False
CORS_ORIGIN_WHITELIST = ( CORS_ORIGIN_WHITELIST = ("matrix.org", "vector.im", "riot.im")
'matrix.org',
'vector.im',
'riot.im'
)
# Matrix chat settings # Matrix chat settings
MATRIX_USER=env('MATRIX_USERNAME') MATRIX_USER = env("MATRIX_USERNAME")
MATRIX_PASSWORD=env('MATRIX_PASSWORD') MATRIX_PASSWORD = env("MATRIX_PASSWORD")
MATRIX_ROOM={ MATRIX_ROOM = {
'default': env('MATRIX_ROOM', default='fmCpNwqgIiuwATlcdw:matrix.org'), "default": env("MATRIX_ROOM", default="fmCpNwqgIiuwATlcdw:matrix.org"),
'admin': 'SiUlbxziFQjndQQTvl:matrix.org', "admin": "SiUlbxziFQjndQQTvl:matrix.org",
'piwars': 'ilIDnMSGUKsejBFkmh:matrix.org' "piwars": "ilIDnMSGUKsejBFkmh:matrix.org",
} }
MSG_PREFIX = 'MH' MSG_PREFIX = "MH"
X_FRAME_OPTIONS = 'SAMEORIGIN' X_FRAME_OPTIONS = "SAMEORIGIN"
#Twitter messageing settings # Twitter messageing settings
TWITTER_CONSUMER_KEY=env('TWITTER_CONSUMER_KEY') TWITTER_CONSUMER_KEY = env("TWITTER_CONSUMER_KEY")
TWITTER_CONSUMER_SECRET=env('TWITTER_CONSUMER_SECRET') TWITTER_CONSUMER_SECRET = env("TWITTER_CONSUMER_SECRET")
TWITTER_ACCESS_TOKEN=env('TWITTER_ACCESS_TOKEN') TWITTER_ACCESS_TOKEN = env("TWITTER_ACCESS_TOKEN")
TWITTER_ACCESS_SECRET=env('TWITTER_ACCESS_SECRET') TWITTER_ACCESS_SECRET = env("TWITTER_ACCESS_SECRET")
LOCATION_PREFIX = env('BUCKET_PREFIX_PATH', default='') LOCATION_PREFIX = env("BUCKET_PREFIX_PATH", default="")
MEDIAFILE_LOCATION = LOCATION_PREFIX + 'media' MEDIAFILE_LOCATION = LOCATION_PREFIX + "media"
STATICFILE_LOCATION = LOCATION_PREFIX + 'static' STATICFILE_LOCATION = LOCATION_PREFIX + "static"
AWS_DEFAULT_ACL = 'public-read'
AWS_S3_OBJECT_PARAMETERS = {
'CacheControl': 'max-age=86400',
}
COMPRESS_URL = 'cache/' COMPRESS_URL = "cache/"
# django-debug-toolbar ALLOWED_HOSTS = ["*"]
# ---------------------MDVTDNXFTRJSJBX9KWOJTMCGSNMYASEFNBPDUZJMGSPPCVMQRUZMZAEXDTIGHPZCP9JBGLVKGSJMZKPVV--------------------------------------------------------- INTERNAL_IPS = [
"127.0.0.1",
ALLOWED_HOSTS = ['*'] "10.0.2.2",
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'] "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 # 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()) ip = socket.gethostbyname(socket.gethostname())
INTERNAL_IPS += [ip[:-1] + "1"] INTERNAL_IPS += [ip[:-1] + "1"]
# CACHING # 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 # Heroku URL does not pass the DB number, so we parse it in
CACHES = { CACHES = {
'default': { "default": {
'BACKEND': 'django_redis.cache.RedisCache', "BACKEND": "django_redis.cache.RedisCache",
'LOCATION': REDIS_LOCATION, "LOCATION": REDIS_LOCATION,
'OPTIONS': { "OPTIONS": {
'CLIENT_CLASS': 'django_redis.client.DefaultClient', "CLIENT_CLASS": "django_redis.client.DefaultClient",
'IGNORE_EXCEPTIONS': True, # mimics memcache behavior. "IGNORE_EXCEPTIONS": True, # mimics memcache behavior.
# http://niwinz.github.io/django-redis/latest/#_memcached_exceptions_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_SERVER = env("LDAP_SERVER", default="172.19.0.6")
LDAP_PASSWORD = env('LDAP_ADMIN_PASSWORD', default='secretldappassword') LDAP_PASSWORD = env("LDAP_ADMIN_PASSWORD", default="secretldappassword")
LDAP_ROOT = env('LDAP_ROOT', default='dc=maidstone-hackspace, dc=org, dc=uk') 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

View File

@ -1,4 +1,5 @@
from collections import OrderedDict from collections import OrderedDict
from django.utils.html import escape
from django.contrib.syndication.views import Feed, add_domain from django.contrib.syndication.views import Feed, add_domain
from django.contrib.sites.shortcuts import get_current_site from django.contrib.sites.shortcuts import get_current_site
from django.utils import timezone from django.utils import timezone
@ -54,7 +55,7 @@ class RssFeed(Feed):
return post.get('title') return post.get('title')
def item_description(self, post): def item_description(self, post):
return post.get('description') return escape(post.get('description'))
def item_author_name(self, post): def item_author_name(self, post):
return post.get('author') return post.get('author')
@ -102,7 +103,7 @@ class BlogFeed(Feed):
return post.title return post.title
def item_description(self, post): def item_description(self, post):
return post.description return escape(post.description)
def item_author_name(self, post): def item_author_name(self, post):
return post.author.name return post.author.name
@ -146,4 +147,4 @@ class BlogCategoryFeed(BlogFeed):
return "Maidstone Hackspace Blog: %s" % category.name return "Maidstone Hackspace Blog: %s" % category.name
def description(self, category): def description(self, category):
return category.description return escape(category.description)

View File

@ -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)

View File

@ -3,10 +3,14 @@ import os
import logging import logging
import feedparser import feedparser
from time import mktime
from datetime import datetime
from urllib.request import urlretrieve from urllib.request import urlretrieve
from django.core.files import File from django.core.files import File
from django.utils.timezone import make_aware from django.utils.timezone import make_aware
from django.utils import timezone
from stdimage.utils import render_variations from stdimage.utils import render_variations
from mhackspace.feeds.reader import fetch_feeds
# from scaffold.readers.rss_reader import feed_reader # from scaffold.readers.rss_reader import feed_reader
@ -18,24 +22,29 @@ logger = logging.getLogger(__name__)
def feed_reader(feeds): def feed_reader(feeds):
for feed in feeds: for feed in feeds:
print(feed) print(feed)
yield feedparser.parse(feed['url']) yield feedparser.parse(feed["url"])
def import_feeds(feed=False): def import_feeds(feed=False):
remove_old_articles() 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 = [] articles = []
for article in rss_articles: for article in rss_articles:
print(article) date = datetime.fromtimestamp(mktime(article["date"]))
print(article["title"])
print(article["image"])
print('#############')
articles.append( articles.append(
Article( Article(
url=article["url"].decode(), url=article["url"],
feed=Feed.objects.get(pk=article["id"]), feed=Feed.objects.get(pk=article["feed"]),
title=article["title"].decode(), title=article["title"][0:100],
original_image=article["image"], original_image=article["image"][0:100],
description=article["description"].decode(), description=article["description"],
date=make_aware(article["date"]), date=date,
) )
) )

131
mhackspace/feeds/reader.py Normal file
View File

@ -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("<div>" + content + "</div>", 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

View File

@ -15,6 +15,7 @@
<a target="_blank" href="https://riot.im/app/#/room/#maidstone-hackspace:matrix.org" style="width:100%;height:600px;">Full screen chat</a> <a target="_blank" href="https://riot.im/app/#/room/#maidstone-hackspace:matrix.org" style="width:100%;height:600px;">Full screen chat</a>
<p> <p>
Connect directly using irc.freenode.org and the #maidstone-hackspace channel via an irc client. Connect directly using irc.freenode.org and the #maidstone-hackspace channel via an irc client.
</p> </p>

View File

@ -7,11 +7,10 @@
<h2>Contact us</h2> <h2>Contact us</h2>
Please fill in details below, and we will get back to you when possible, alternatively try our live chat.</br> Please fill in details below, and we will get back to you when possible, alternatively try our live chat.</br>
<a href="https://hangouts.google.com/group/oDcAL0nDfQYfO3qq1"> <a href="https://riot.im/app/#/room/#maidstone-hackspace:matrix.org">
Click here to chat with us Click here to chat with us
</a> </a>
<form method="POST" action="{% url 'contact' %}" class="contact_us_form"> <form method="POST" action="{% url 'contact' %}" class="contact_us_form">
{% csrf_token %} {% csrf_token %}
{{ form|crispy }} {{ form|crispy }}

View File

@ -19,7 +19,7 @@
<h3>Introduction</h3> <h3>Introduction</h3>
<p>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.</p> <p>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.</p>
<p>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 <a href="https://groups.google.com/forum/#!forum/maidstone-hackspace">mailing list</a>, email, and <a href="https://hangouts.google.com/group/oDcAL0nDfQYfO3qq1">Chat</a>. If you're at all intrested please join our mailing list and say hi.</p> <p>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 <a href="https://groups.google.com/forum/#!forum/maidstone-hackspace">mailing list</a>, email, and <a href="https://riot.im/app/#/room/#maidstone-hackspace:matrix.org">Chat</a>. If you're at all intrested please join our mailing list and say hi.</p>
<p>If you would like to visit us <a href="https://www.google.co.uk/maps/place/Maidstone+Community+Support+Centre/@51.2743759,0.5252557,17z/data=!3m1!4b1!4m5!3m4!1s0x47df32307962b7b3:0x8478b1177ec21ef7!8m2!3d51.2743759!4d0.5274444">click here</a> for a map, and check the <a href="https://groups.google.com/forum/#!forum/maidstone-hackspace">mailing list</a> for when we are at the space, we recommend contacting us to avoid disapointment.</p> <p>If you would like to visit us <a href="https://www.google.co.uk/maps/place/Maidstone+Community+Support+Centre/@51.2743759,0.5252557,17z/data=!3m1!4b1!4m5!3m4!1s0x47df32307962b7b3:0x8478b1177ec21ef7!8m2!3d51.2743759!4d0.5274444">click here</a> for a map, and check the <a href="https://groups.google.com/forum/#!forum/maidstone-hackspace">mailing list</a> for when we are at the space, we recommend contacting us to avoid disapointment.</p>

View File

@ -83,9 +83,8 @@ django-filter==2.0.0
coreapi==2.3.3 coreapi==2.3.3
# api libraries end # api libraries end
martor==1.3.2 #martor==1.3.2
git+git://github.com/olymk2/django-markdown-editor.git
#git+git://github.com/olymk2/django-markdown-editor.git
django-spirit==0.6.1 django-spirit==0.6.1
django-djconfig==0.8.0 django-djconfig==0.8.0