diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..5a0e65c --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,81 @@ +version: 2 + +jobs: + flake8: + docker: + - image: circleci/python:3.8 + steps: + - checkout + - run: pip install flake8 + - run: flake8 wagtail_ab_testing + + prettier: + docker: + - image: circleci/node:14 + steps: + - checkout: + + - type: cache-restore + keys: + - node-modules-{{ .Branch }}-{{ checksum "package-lock.json" }} + - node-modules-{{ .Branch }}- + - node-modules-master- + + - run: npm install + - run: npm run lint + + - type: cache-save + key: node-modules-{{ .Branch }}-{{ checksum "package-lock.json" }} + paths: + - "node_modules" + + test: + docker: + - image: circleci/python:3.8 + steps: + - checkout + + - type: cache-restore + keys: + - pip-{{ .Branch }}- + - pip-master- + + - run: pip install -e .[testing] + + - type: cache-save + key: pip-{{ .Branch }}-{{ epoch }} + paths: + - "~/.cache/pip" + + - run: python testmanage.py test + + nightly-wagtail-test: + docker: + - image: circleci/python:3.8 + steps: + - checkout + - run: git clone git@github.com:wagtail/wagtail.git + + - run: pip install -e .[testing] + - run: pip install ./wagtail + + - run: python testmanage.py test + +workflows: + version: 2 + test: + jobs: + - flake8 + - prettier + - test + + nightly: + jobs: + - nightly-wagtail-test + triggers: + - schedule: + cron: "0 0 * * *" + filters: + branches: + only: + - master diff --git a/.circleci/trigger-nightly-build.sh b/.circleci/trigger-nightly-build.sh new file mode 100755 index 0000000..35beb09 --- /dev/null +++ b/.circleci/trigger-nightly-build.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# Triggers a test run against the master version of Wagtail + +# This job will is scheduled in the config.yml, this script is here to help test the job + +curl -u ${CIRCLE_API_USER_TOKEN}: \ + -d build_parameters[CIRCLE_JOB]=nightly-wagtail-test \ + https://circleci.com/api/v1.1/project/github/wagtail/wagtail-ab-testing}/tree/master diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..a86701e --- /dev/null +++ b/.coveragerc @@ -0,0 +1,24 @@ +[run] +branch = True +include = wagtail_ab_testing/* +omit = */migrations/*,*/tests/* + +[report] +# Regexes for lines to exclude from consideration +exclude_lines = + # Have to re-enable the standard pragma + pragma: no cover + + # Don't complain about missing debug-only code: + def __repr__ + if self\.debug + + # Don't complain if tests don't hit defensive assertion code: + raise AssertionError + raise NotImplementedError + + # Don't complain if non-runnable code isn't run: + if 0: + if __name__ == .__main__.: + +ignore_errors = True diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..793d978 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,24 @@ +module.exports = { + parser: '@typescript-eslint/parser', + extends: [ + 'torchbox', + 'plugin:@typescript-eslint/recommended', + ], + parserOptions: { + ecmaVersion: 2018, + sourceType: 'module', + }, + rules: { + '@typescript-eslint/explicit-member-accessibility': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-explicit-any': 'off', + 'react/jsx-filename-extension': [1, { 'extensions': ['.jsx', '.tsx'] }], + }, + settings: { + 'import/resolver': { + 'node': { + 'extensions': ['.js', '.jsx', '.ts', '.tsx'] + } + } + }, +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2ee18de --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +*.pyc +/build +/dist +/wagtail_ab_testing.egg-info +/.coverage +/htmlcov +/.tox +/venv +/.vscode +/site +/test_wagtail_ab_testing.db +/node_modules diff --git a/.prettierrc.toml b/.prettierrc.toml new file mode 100644 index 0000000..009474b --- /dev/null +++ b/.prettierrc.toml @@ -0,0 +1,2 @@ +tabWidth = 4 +singleQuote = true diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..0b769e4 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,44 @@ +dist: focal +language: python +cache: pip + +services: + - postgresql + +matrix: + include: + - env: TOXENV=py37-dj22-wamaster-postgres + python: 3.7 + + - env: TOXENV=py38-dj22-wamaster-postgres + python: 3.8 + + - env: TOXENV=py38-dj22-wamaster-sqlite + python: 3.8 + + - env: TOXENV=py37-dj30-wamaster-postgres + python: 3.7 + + - env: TOXENV=py38-dj30-wamaster-postgres + python: 3.8 + + - env: TOXENV=py38-dj31-wamaster-postgres + python: 3.8 + + - env: TOXENV=py38-dj31-wamaster-sqlite + python: 3.8 + +# Not currently needed as we test against Wagtail master anyway +# When Wagtail 2.11 is released and the above environments are updated. Uncomment this. +# +# - env: TOXENV=py38-dj31-wamaster-postgres +# python: 3.8 +# +# allow_failures: +# - env: TOXENV=py38-dj31-wamaster-postgres + +install: + - pip install tox + +script: + - tox diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e11a878 --- /dev/null +++ b/LICENSE @@ -0,0 +1,33 @@ + + + +BSD License + +Copyright (c) 2020, Karl Hobley +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..e486d42 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,4 @@ +include LICENSE *.rst *.txt *.md +graft wagtail_ab_testing +global-exclude __pycache__ +global-exclude *.py[co] diff --git a/package.json b/package.json new file mode 100644 index 0000000..bf49e17 --- /dev/null +++ b/package.json @@ -0,0 +1,45 @@ +{ + "name": "wagtail-ab-testing", + "version": "1.0.0", + "description": "", + "main": "wagtail_ab_testing/static_src/main.tsx", + "scripts": { + "build": "webpack --mode=production", + "start": "webpack --mode=development --watch", + "format": "prettier --write wagtail_ab_testing/**/*.{ts,tsx,scss}", + "lint": "prettier --check wagtail_ab_testing/**/*.{ts,tsx,scss}" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@svgr/webpack": "^5.4.0", + "@types/react": "^16.8.19", + "@types/react-dom": "^16.8.19", + "@types/styled-components": "^5.1.2", + "@typescript-eslint/eslint-plugin": "^1.12.0", + "@typescript-eslint/parser": "^1.12.0", + "css-loader": "^2.1.1", + "eslint": "^5.16.0", + "eslint-config-airbnb": "^17.1.1", + "eslint-config-prettier": "^6.0.0", + "eslint-config-torchbox": "^0.2.0", + "eslint-plugin-import": "^2.18.0", + "eslint-plugin-jsx-a11y": "^6.2.3", + "eslint-plugin-react": "^7.14.2", + "file-loader": "^3.0.1", + "node-sass": "^4.13.1", + "prettier": "1.18.2", + "sass": "^1.20.3", + "sass-loader": "^7.1.0", + "style-loader": "^0.23.1", + "ts-loader": "^6.0.2", + "typescript": "^3.5.1", + "webpack": "^4.32.2", + "webpack-cli": "^3.3.2" + }, + "dependencies": { + "react": "^16.13.1", + "react-dom": "^16.13.1", + "styled-components": "^5.1.1" + } +} diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..9b57c42 --- /dev/null +++ b/setup.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python + +import sys, os + +from setuptools import setup, find_packages + +# Hack to prevent "TypeError: 'NoneType' object is not callable" error +# in multiprocessing/util.py _exit_function when setup.py exits +# (see http://www.eby-sarna.com/pipermail/peak/2010-May/003357.html) +try: + import multiprocessing +except ImportError: + pass + + +setup( + name="wagtail-ab-testing", + version="0.1", + description="A/B Testing for Wagtail", + author="Karl Hobley", + author_email="karl@torchbox.com", + url="", + packages=find_packages(), + include_package_data=True, + license="BSD", + classifiers=[ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Framework :: Django", + "Framework :: Django :: 2.2", + "Framework :: Django :: 3.0", + "Framework :: Django :: 3.1", + "Framework :: Wagtail", + "Framework :: Wagtail :: 2", + ], + install_requires=["Django>=2.2,<3.2", "Wagtail>=2.11,<2.12"], + extras_require={ + "testing": ["dj-database-url==0.5.0", "freezegun==0.3.15"], + }, + zip_safe=False, +) diff --git a/testmanage.py b/testmanage.py new file mode 100644 index 0000000..581d8e5 --- /dev/null +++ b/testmanage.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +import argparse +import os +import shutil +import sys +import warnings + +from django.core.management import execute_from_command_line + + +os.environ["DJANGO_SETTINGS_MODULE"] = "wagtail_ab_testing.test.settings" + + +def make_parser(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--deprecation", + choices=["all", "pending", "imminent", "none"], + default="imminent", + ) + return parser + + +def parse_args(args=None): + return make_parser().parse_known_args(args) + + +def runtests(): + args, rest = parse_args() + + only_wagtail = r"^wagtail(\.|$)" + if args.deprecation == "all": + # Show all deprecation warnings from all packages + warnings.simplefilter("default", DeprecationWarning) + warnings.simplefilter("default", PendingDeprecationWarning) + elif args.deprecation == "pending": + # Show all deprecation warnings from wagtail + warnings.filterwarnings( + "default", category=DeprecationWarning, module=only_wagtail + ) + warnings.filterwarnings( + "default", category=PendingDeprecationWarning, module=only_wagtail + ) + elif args.deprecation == "imminent": + # Show only imminent deprecation warnings from wagtail + warnings.filterwarnings( + "default", category=DeprecationWarning, module=only_wagtail + ) + elif args.deprecation == "none": + # Deprecation warnings are ignored by default + pass + + argv = [sys.argv[0]] + rest + + try: + execute_from_command_line(argv) + finally: + from wagtail.tests.settings import STATIC_ROOT, MEDIA_ROOT + + shutil.rmtree(STATIC_ROOT, ignore_errors=True) + shutil.rmtree(MEDIA_ROOT, ignore_errors=True) + + +if __name__ == "__main__": + runtests() diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..277ab1d --- /dev/null +++ b/tox.ini @@ -0,0 +1,41 @@ +[tox] +skipsdist = True +usedevelop = True + +envlist = py{37,38}-dj{22,30,31,master}-wa{211,master}-{sqlite,postgres} + +[flake8] +# E501: Line too long +# W503: line break before binary operator (superseded by W504 line break after binary operator) +ignore = E501,W503 +exclude = migrations,node_modules + +[testenv] +install_command = pip install -e ".[testing]" -U {opts} {packages} +commands = coverage run testmanage.py test --deprecation all + +basepython = + py37: python3.7 + py38: python3.8 + +deps = + coverage + + dj22: Django>=2.2,<2.3 + dj30: Django>=3.0,<3.1 + dj31: Django>=3.1,<3.2 + djmaster: git+https://github.com/django/django.git@master#egg=Django + djmaster: git+https://github.com/wagtail/django-modelcluster.git + + wa211: wagtail>=2.11,<2.12 + wamaster: git+https://github.com/wagtail/wagtail.git + + postgres: psycopg2>=2.6 + +setenv = + postgres: DATABASE_URL=postgres:///wagtail_ab_testing + +[testenv:flake8] +basepython=python3.7 +deps=flake8>=2.2.0 +commands=flake8 wagtail_ab_testing diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..b555e52 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "jsx": "react", + "lib": ["es2015", "dom"], + "noImplicitAny": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "strictNullChecks": true, + "esModuleInterop": true + }, + "files": [ + "wagtail_ab_testing/static_src/main.tsx", + "wagtail_ab_testing/static_src/custom.d.ts" + ] +} diff --git a/wagtail_ab_testing/__init__.py b/wagtail_ab_testing/__init__.py new file mode 100644 index 0000000..6d160f3 --- /dev/null +++ b/wagtail_ab_testing/__init__.py @@ -0,0 +1 @@ +default_app_config = "wagtail_ab_testing.apps.WagtailAbTestingAppConfig" diff --git a/wagtail_ab_testing/apps.py b/wagtail_ab_testing/apps.py new file mode 100644 index 0000000..6699c4d --- /dev/null +++ b/wagtail_ab_testing/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + + +class WagtailAbTestingAppConfig(AppConfig): + label = "wagtail_ab_testing" + name = "wagtail_ab_testing" + verbose_name = "A/B Testing" diff --git a/wagtail_ab_testing/static/wagtail_ab_testing/js/.gitignore b/wagtail_ab_testing/static/wagtail_ab_testing/js/.gitignore new file mode 100644 index 0000000..9816ee3 --- /dev/null +++ b/wagtail_ab_testing/static/wagtail_ab_testing/js/.gitignore @@ -0,0 +1 @@ +/wagtail-ab-testing.js diff --git a/wagtail_ab_testing/static_src/custom.d.ts b/wagtail_ab_testing/static_src/custom.d.ts new file mode 100644 index 0000000..0515baa --- /dev/null +++ b/wagtail_ab_testing/static_src/custom.d.ts @@ -0,0 +1,73 @@ +export {}; + +// Allows SVG files to be imported and used in TypeScript +declare module '*.svg' { + const content: any; + export default content; +} + +// Declare globals provided by Django's JavaScript Catalog +// For more information, see: https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#module-django.views.i18n +declare global { + // Wagtail globals + + interface WagtailConfig { + ADMIN_API: { + PAGES: string; + DOCUMENTS: string; + IMAGES: string; + EXTRA_CHILDREN_PARAMETERS: string; + }; + + I18N_ENABLED: boolean; + LOCALES: { + code: string; + /* eslint-disable-next-line camelcase */ + display_name: string; + }[]; + } + + const wagtailConfig: WagtailConfig; + + // Django i18n utilities + + // https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#gettext + function gettext(text: string): string; + + // https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#ngettext + function ngettext(singular: string, plural: string, count: number): string; + + // https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#interpolate + // FIXME export default function interpolate(...): string; + + // https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#get-format + type FormatType = + | 'DATE_FORMAT' + | 'DATE_INPUT_FORMATS' + | 'DATETIME_FORMAT' + | 'DATETIME_INPUT_FORMATS' + | 'DECIMAL_SEPARATOR' + | 'FIRST_DAY_OF_WEEK' + | 'MONTH_DAY_FORMAT' + | 'NUMBER_GROUPING' + | 'SHORT_DATE_FORMAT' + | 'SHORT_DATETIME_FORMAT' + | 'THOUSAND_SEPARATOR' + | 'TIME_FORMAT' + | 'TIME_INPUT_FORMATS' + | 'YEAR_MONTH_FORMAT'; + + function get_format(formatType: FormatType): string; + + // https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#gettext_noop + function gettext_noop(text: string): string; + + // https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#pgettext + function pgettext(context: string, text: string): string; + + // https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#npgettext + function pgettext(context: string, text: string, count: number): string; + + // https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#pluralidx + function pluralidx(count: number): boolean; +} diff --git a/wagtail_ab_testing/static_src/main.tsx b/wagtail_ab_testing/static_src/main.tsx new file mode 100644 index 0000000..e69de29 diff --git a/wagtail_ab_testing/test/__init__.py b/wagtail_ab_testing/test/__init__.py new file mode 100644 index 0000000..3d623a3 --- /dev/null +++ b/wagtail_ab_testing/test/__init__.py @@ -0,0 +1 @@ +default_app_config = "wagtail_ab_testing.test.apps.WagtailAbTestingTestAppConfig" diff --git a/wagtail_ab_testing/test/apps.py b/wagtail_ab_testing/test/apps.py new file mode 100644 index 0000000..1091d90 --- /dev/null +++ b/wagtail_ab_testing/test/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + + +class WagtailAbTestingTestAppConfig(AppConfig): + label = "wagtail_ab_testing_test" + name = "wagtail_ab_testing.test" + verbose_name = "A/B Testing tests" diff --git a/wagtail_ab_testing/test/settings.py b/wagtail_ab_testing/test/settings.py new file mode 100644 index 0000000..7036de9 --- /dev/null +++ b/wagtail_ab_testing/test/settings.py @@ -0,0 +1,157 @@ +""" +Django settings for temp project. + +Generated by 'django-admin startproject' using Django 1.10.5. + +For more information on this file, see +https://docs.djangoproject.com/en/1.10/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.10/ref/settings/ +""" + +import os +import dj_database_url + +# Build paths inside the project like this: os.path.join(PROJECT_DIR, ...) +PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +BASE_DIR = os.path.dirname(PROJECT_DIR) + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = "c6u0-9c!7nilj_ysatsda0(f@e_2mws2f!6m0n^o*4#*q#kzp)" + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = ["localhost", "testserver"] + + +# Application definition + +INSTALLED_APPS = [ + "wagtail_ab_testing", + "wagtail_ab_testing.test", + "wagtail.contrib.search_promotions", + "wagtail.contrib.forms", + "wagtail.contrib.redirects", + "wagtail.embeds", + "wagtail.users", + "wagtail.snippets", + "wagtail.documents", + "wagtail.images", + "wagtail.search", + "wagtail.admin", + "wagtail.api.v2", + "wagtail.contrib.modeladmin", + "wagtail.contrib.routable_page", + "wagtail.contrib.styleguide", + "wagtail.sites", + "wagtail.core", + "taggit", + "rest_framework", + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "django.contrib.sitemaps", +] + +MIDDLEWARE = [ + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "wagtail.contrib.redirects.middleware.RedirectMiddleware", +] + +ROOT_URLCONF = "wagtail_ab_testing.test.urls" + +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ] + }, + } +] + + +# Using DatabaseCache to make sure that the cache is cleared between tests. +# This prevents false-positives in some wagtail core tests where we are +# changing the 'wagtail_root_paths' key which may cause future tests to fail. +CACHES = { + "default": { + "BACKEND": "django.core.cache.backends.db.DatabaseCache", + "LOCATION": "cache", + } +} + + +# don't use the intentionally slow default password hasher +PASSWORD_HASHERS = ("django.contrib.auth.hashers.MD5PasswordHasher",) + + +# Database +# https://docs.djangoproject.com/en/1.10/ref/settings/#databases + +DATABASES = { + "default": dj_database_url.config(default="sqlite:///test_wagtail_ab_testing.db"), +} + + +# Password validation +# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + "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.NumericPasswordValidator"}, +] + + +# Internationalization +# https://docs.djangoproject.com/en/1.10/topics/i18n/ + +LANGUAGE_CODE = "en-us" + +TIME_ZONE = "UTC" + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.8/howto/static-files/ + +STATICFILES_FINDERS = [ + "django.contrib.staticfiles.finders.FileSystemFinder", + "django.contrib.staticfiles.finders.AppDirectoriesFinder", +] + +STATICFILES_DIRS = [os.path.join(PROJECT_DIR, "static")] + +STATIC_ROOT = os.path.join(PROJECT_DIR, "collect_static") +STATIC_URL = "/static/" + +MEDIA_ROOT = os.path.join(PROJECT_DIR, "media") diff --git a/wagtail_ab_testing/test/tests/__init__.py b/wagtail_ab_testing/test/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wagtail_ab_testing/test/urls.py b/wagtail_ab_testing/test/urls.py new file mode 100644 index 0000000..ceabbf1 --- /dev/null +++ b/wagtail_ab_testing/test/urls.py @@ -0,0 +1,15 @@ +from django.conf.urls import include, url +from django.conf.urls.i18n import i18n_patterns +from django.contrib import admin + +from wagtail.admin import urls as wagtailadmin_urls +from wagtail.documents import urls as wagtaildocs_urls +from wagtail.core import urls as wagtail_urls + +urlpatterns = [ + url(r"^django-admin/", admin.site.urls), + url(r"^admin/", include(wagtailadmin_urls)), + url(r"^documents/", include(wagtaildocs_urls)), +] + +urlpatterns += i18n_patterns(url(r"", include(wagtail_urls))) diff --git a/wagtail_ab_testing/wagtail_hooks.py b/wagtail_ab_testing/wagtail_hooks.py new file mode 100644 index 0000000..d1886d0 --- /dev/null +++ b/wagtail_ab_testing/wagtail_hooks.py @@ -0,0 +1,24 @@ +from django.urls import path, include +from django.views.i18n import JavaScriptCatalog + +from wagtail.core import hooks + + +@hooks.register("register_admin_urls") +def register_admin_urls(): + urls = [ + path('jsi18n/', JavaScriptCatalog.as_view(packages=['wagtail_ab_testing']), name='javascript_catalog'), + + # Add your other URLs here, and they will appear under `/admin/ab_testing/` + # Note: you do not need to check for authentication in views added here, Wagtail does this for you! + ] + + return [ + path( + "ab_testing/", + include( + (urls, "wagtail_ab_testing"), + namespace="wagtail_ab_testing", + ), + ) + ] diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..abe17af --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,52 @@ +const path = require('path'); + +module.exports = { + entry: './wagtail_ab_testing/static_src/main.tsx', + module: { + rules: [ + { + test: /\.tsx?$/, + use: 'ts-loader', + exclude: /node_modules/ + }, + { + test: /\.scss$/, + use: [ + 'style-loader', + 'css-loader', + 'sass-loader' + ] + }, + { + test: /\.css$/, + use: [ + 'style-loader', + 'css-loader', + ] + }, + { + test: /\.svg$/, + use: ['@svgr/webpack'], + }, + { + test: /\.(png|jpg|gif)$/, + use: [ + 'file-loader' + ] + } + ] + }, + resolve: { + extensions: [ '.tsx', '.ts', '.js' ] + }, + externals: { + /* These are provided by Wagtail */ + 'react': 'React', + 'react-dom': 'ReactDOM', + 'gettext': 'gettext', + }, + output: { + path: path.resolve(__dirname, 'wagtail_ab_testing/static/wagtail_ab_testing/js'), + filename: 'wagtail-ab-testing.js' + } +};