diff --git a/.gitmodules b/.gitmodules index 404d309..da33747 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "pagedown/static/pagedown"] path = pagedown/static/pagedown - url = git://github.com/timmyomahony/pagedown.git + url = git://github.com/StackExchange/pagedown.git [submodule "pagedown/static/pagedown-extra"] path = pagedown/static/pagedown-extra url = https://github.com/jmcmanus/pagedown-extra.git \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt index acfd230..6c44785 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2018, Timmy O'Mahony +Copyright (c) 2019, Timmy O'Mahony All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index 2d09769..ca713eb 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,25 @@ Django Pagedown =============== -A django app that allows the easy addition of [Stack Overflow's "Pagedown" Markdown editor](http://code.google.com/p/pagedown/) to a django form field, whether in a custom app or the Django Admin +Add [Stack Overflow's "Pagedown" Markdown editor](https://github.com/StackExchange/pagedown/) to your Django Admin or custom form. -![Screenshot of Django Admin with Pagedown initialised](https://github.com/timmyomahony/django-pagedown/blob/master/django-pagedown-screenshot.png?raw=true "A screenshot of Pagedown in Django's admin") +![Screenshot of Django Admin with Pagedown initialised](https://github.com/timmyomahony/django-pagedown/blob/master/screenshot.png?raw=true "A screenshot of Pagedown in Django's admin") -## Installation - -1. Get the code: `pip install django-pagedown` -2. Add `pagedown` to your `INSTALLED_APPS` -3. Make sure to collect the static files: `python manage.py collectstatic --noinput` (and if you are working in a development environment, make sure [you are properly serving your static files](https://docs.djangoproject.com/en/1.9/howto/static-files/#serving-static-files-during-development)) - -Note that this package will install a cloned copy (git submodule) of the Pagedown library from [http://github.com/timmyomahony/pagedown/](http://github.com/timmyomahony/pagedown/). - -#### Alternative Installation - -If you don't like PyPi (or are having problems with it) you can manually install the pacakge: +## Requirements - - via pip from GitHub: `pip install -e git+https://timmyomahony@github.com/timmyomahony/django-pagedown.git#egg=django-pagedown` - - manually clone from Github: - - `git clone https://timmyomahony@github.com/timmyomahony/django-pagedown.git` - - `cd django-pagedown` - - `git submodule update --init` +Version >= 2.0.0 of `django-pagedown` requires Django 2.1.0 or above (previous versions should support Django all the way back to around 1.1). -## Markdown Safety +## Installation -Remember that this library doesn't render your markdown for you outside of the admin widget nor does it do any internal sanitization. Markdown can accept any valid HTML so you have to be careful and make sure you are rendering the output of any untrusted input safely (with [`django-markdown-deux`](https://github.com/trentm/django-markdown-deux) for example), otherwise you could have users embedding scripts in your pagedown text areas +1. Get the code: `pip install django-pagedown` +2. Add `pagedown.apps.PagedownConfig` to your `INSTALLED_APPS` +3. Collect the static files: `python manage.py collectstatic` ## Usage The widget can be used both inside the django admin or independendly. -#### Inside the Django Admin: +### Inside the Django Admin: If you want to use the pagedown editor in a django admin field, there are numerous possible approaches: @@ -81,7 +69,7 @@ If you want to use the pagedown editor in a django admin field, there are numero fields = "__all__" ``` -#### Outside the Django Admin: +### Outside the Django Admin: To use the widget outside of the django admin, first create a form similar to the above but using the basic `PagedownWidget`: @@ -130,80 +118,25 @@ then create the template and load the javascipt and css required to create the e ``` -## Showing/Hiding the Preview Box - -You can control whether or not to show the dynamically rendered preview box below the pagedown widget in two ways: - - - **Globally:** by using the `PAGEDOWN_SHOW_PREVIEW` option in your `settings.py` (this is mentioned further down the page). This will enable/disable the preview for *all* pagedown widgets throughout your application. - - - **Per Widget:** by supplying a `show_preview` keyword argument when initialising your widget instance in your form. This gives you finer control over which of the fields can make use of the preview when rendering the pagedown widget. Note that this approach will take preference over the `PAGEDOWN_SHOW_PREVIEW` option. - - ```python - # ... - - class AlbumForm(forms.ModelForm): - # ... - description = forms.CharField(widget=PagedownWidget(show_preview=False)) - - class Meta: - model = Album - fields = ['description', ] - ``` - -## Customizing the Widget Template/HTML - -If you want to customize the HTML used to render the pagedown widget altogether, you can. There are two ways: - -- **Globally:** by default, the template used to render the pagedown widget is located at `pagedown/widgets/default.html`. - - You can override this template by creating `pagedown/widgets/default.html` within your own template directory. This will take preference if you are using Django's default template loading system - - You can use the `PAGEDOWN_WIDGET_TEMPLATE` settings to point to a different template file -- **Per Widget:** by supplying a `template` keyword argument when initialising your widget instance in your form. This should be the path to the template you wish to use to render this instance. +## Customizing the Widget - ```python - # ... +If you want to customize the widget, the easiest way is to simply extend it: - class AlbumForm(forms.ModelForm): - # ... - description = forms.CharField(widget=PagedownWidget(template="path/to/template.html")) - - class Meta: - model = Album - fields = ['description', ] - ``` - -## Customizing the CSS - -If you want to change the CSS used to display the widgets, you also can. Again, there are two ways: - - - **Globally:** You can specify the CSS files to be included by the widget by providing a tuple of paths via a `PAGEDOWN_WIDGET_CSS` variable in your `settings.py` - - # Import the default pagedown css first, then our custom CSS sheet - # to avoid having to specify all the default styles - PAGEDOWN_WIDGET_CSS = ('pagedown/demo/browser/demo.css', "pagedown/custom.css",) - -- **Per Widget:** by supplying a `css` keyword argument when initialising your widget instance in your form - - ```python - # ... - - class AlbumForm(forms.ModelForm): - # ... - description = forms.CharField(widget=PagedownWidget(css=("custom/css1.css", "custom/css2.css"))) - - class Meta: - model = Album - fields = ['description', ] - ``` +```py +from pagedown.widgets import PagedownWidget -## Options -The following options can be added to your default `settings.py` file to control certain aspects of `django-pagedown`. Note that changing these will affect **all** instances of the pagedown widget throughout your app.: +class MyNewWidget(PagedownWidget): + template_name = '/custom/template.html' -- `PAGEDOWN_SHOW_PREVIEW` (boolean): whether or not to show the dynamic markdown preview below the markdown text area for the pagedown widgets. The default is `True`. -- `PAGEDOWN_WIDGET_TEMPLATE` (string): the template used to render the pagedown widget. The default template is located in `pagedown/widgets/default.html`. -- `PAGEDOWN_WIDGET_CSS` (tuple): the path to the CSS file to be used by the pagedown widget. The default path is `pagedown/ + class Media: + css = { + 'all': ('custom/stylesheets.css,) + } + js = ('custom/javascript.js',) +``` -## Rendering Markdown In Your Template +## Rendering Markdown `contrib.markdown` was [depreciated in Django 1.5](https://code.djangoproject.com/ticket/18054) meaning you can no longer use the `markdown` filter in your template by default. @@ -217,7 +150,3 @@ The following options can be added to your default `settings.py` file to control

{{ entry.body|markdown }}

... ``` - -## TODO - -- Add support for images uploading or hooks into the likes of `django-filer` etc. diff --git a/django-pagedown-screenshot.png b/django-pagedown-screenshot.png deleted file mode 100644 index f6efd19..0000000 Binary files a/django-pagedown-screenshot.png and /dev/null differ diff --git a/example/.gitignore b/example/.gitignore deleted file mode 100644 index 0890ec5..0000000 --- a/example/.gitignore +++ /dev/null @@ -1,94 +0,0 @@ -app/pagedown -app/db.sqlite3 -var/ - - -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*,cover -.hypothesis/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# IPython Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# dotenv -.env - -# virtualenv -venv/ -ENV/ - -# Spyder project settings -.spyderproject - -# Rope project settings -.ropeproject diff --git a/example/README.md b/example/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/example/app/app/__init__.py b/example/app/app/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/example/app/app/settings.py b/example/app/app/settings.py deleted file mode 100644 index e8494a8..0000000 --- a/example/app/app/settings.py +++ /dev/null @@ -1,131 +0,0 @@ -""" -Django settings for app project. - -Generated by 'django-admin startproject' using Django 1.11. - -For more information on this file, see -https://docs.djangoproject.com/en/1.11/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/1.11/ref/settings/ -""" - -import os - -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = '(#nmk0-=4k(5d61sj&$0k%0+ci$sko#6r00m1x0)@mg_f1xo3+' - -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True - -ALLOWED_HOSTS = [] - - -# Application definition - -INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - - 'music', - 'pagedown' -] - -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', -] - -ROOT_URLCONF = 'app.urls' - -TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [ - os.path.join(BASE_DIR, 'templates/') - ], - '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', - ], - }, - }, -] - -WSGI_APPLICATION = 'app.wsgi.application' - - -# Database -# https://docs.djangoproject.com/en/1.11/ref/settings/#databases - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), - } -} - - -# Password validation -# https://docs.djangoproject.com/en/1.11/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.11/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.11/howto/static-files/ - -STATIC_URL = '/static/' -STATIC_ROOT = os.path.join(os.path.split(BASE_DIR)[0], 'var/static/') -STATICFILES_DIRS = () -STATICFILES_FINDERS = ( - 'django.contrib.staticfiles.finders.FileSystemFinder', - 'django.contrib.staticfiles.finders.AppDirectoriesFinder' -) diff --git a/example/app/app/urls.py b/example/app/app/urls.py deleted file mode 100644 index d078e4e..0000000 --- a/example/app/app/urls.py +++ /dev/null @@ -1,22 +0,0 @@ -"""app URL Configuration - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/1.11/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') -Including another URLconf - 1. Import the include() function: from django.conf.urls import url, include - 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) -""" -from django.conf.urls import url, include -from django.contrib import admin - -urlpatterns = [ - url(r'^admin/', admin.site.urls), - url(r'^music/', include('music.urls')), -] diff --git a/example/app/app/wsgi.py b/example/app/app/wsgi.py deleted file mode 100644 index cefd1ed..0000000 --- a/example/app/app/wsgi.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -WSGI config for app project. - -It exposes the WSGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ -""" - -import os - -from django.core.wsgi import get_wsgi_application - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings") - -application = get_wsgi_application() diff --git a/example/app/manage.py b/example/app/manage.py deleted file mode 100755 index 7525f87..0000000 --- a/example/app/manage.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -import os -import sys - -if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings") - try: - from django.core.management import execute_from_command_line - except ImportError: - # The above import may fail for some other reason. Ensure that the - # issue is really that Django is missing to avoid masking other - # exceptions on Python 2. - try: - import django - except ImportError: - raise ImportError( - "Couldn't import Django. Are you sure it's installed and " - "available on your PYTHONPATH environment variable? Did you " - "forget to activate a virtual environment?" - ) - raise - execute_from_command_line(sys.argv) diff --git a/example/app/music/__init__.py b/example/app/music/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/example/app/music/admin.py b/example/app/music/admin.py deleted file mode 100644 index 2ca8115..0000000 --- a/example/app/music/admin.py +++ /dev/null @@ -1,24 +0,0 @@ -from django.contrib import admin -from django.db import models - -from music.models import Artist, Song, Album -from music.forms import AdminAlbumForm - -from pagedown.widgets import AdminPagedownWidget - - -@admin.register(Artist) -class ArtistAdmin(admin.ModelAdmin): - pass - - -@admin.register(Song) -class SongAdmin(admin.ModelAdmin): - formfield_overrides = { - models.TextField: {'widget': AdminPagedownWidget} - } - - -@admin.register(Album) -class AlbumAdmin(admin.ModelAdmin): - form = AdminAlbumForm diff --git a/example/app/music/apps.py b/example/app/music/apps.py deleted file mode 100644 index d909c7f..0000000 --- a/example/app/music/apps.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.apps import AppConfig - - -class MusicConfig(AppConfig): - name = 'music' diff --git a/example/app/music/forms.py b/example/app/music/forms.py deleted file mode 100644 index 5187dfa..0000000 --- a/example/app/music/forms.py +++ /dev/null @@ -1,42 +0,0 @@ -from django import forms - -from music.models import Artist, Album, Song - -from pagedown.widgets import AdminPagedownWidget, PagedownWidget - - -class AdminAlbumForm(forms.ModelForm): - '''An admin form for Albums''' - descripion = forms.CharField(widget=AdminPagedownWidget()) - - class Meta: - model = Album - fields = '__all__' - - -class ArtistForm(forms.ModelForm): - '''A form for Artists''' - about = forms.CharField(widget=PagedownWidget()) - - class Meta: - model = Artist - fields = '__all__' - - -class AlbumForm(forms.ModelForm): - '''A form for Albums''' - descripion = forms.CharField(widget=PagedownWidget()) - - class Meta: - model = Album - fields = '__all__' - - -class SongForm(forms.ModelForm): - '''A form for Songs''' - descripion = forms.CharField(widget=PagedownWidget()) - lyrics = forms.CharField(widget=PagedownWidget()) - - class Meta: - model = Song - fields = '__all__' diff --git a/example/app/music/migrations/0001_initial.py b/example/app/music/migrations/0001_initial.py deleted file mode 100644 index 1379f8c..0000000 --- a/example/app/music/migrations/0001_initial.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11 on 2017-04-11 13:48 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ] - - operations = [ - migrations.CreateModel( - name='Album', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=128)), - ('description', models.TextField(blank=True)), - ], - ), - migrations.CreateModel( - name='Song', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=128)), - ('description', models.TextField(blank=True)), - ('album', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='music.Album')), - ], - ), - ] diff --git a/example/app/music/migrations/0002_auto_20170411_1350.py b/example/app/music/migrations/0002_auto_20170411_1350.py deleted file mode 100644 index f5eb516..0000000 --- a/example/app/music/migrations/0002_auto_20170411_1350.py +++ /dev/null @@ -1,30 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11 on 2017-04-11 13:50 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('music', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='Artist', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=128)), - ('about', models.TextField(blank=True)), - ], - ), - migrations.AddField( - model_name='album', - name='artist', - field=models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, to='music.Artist'), - preserve_default=False, - ), - ] diff --git a/example/app/music/migrations/0003_song_lyrics.py b/example/app/music/migrations/0003_song_lyrics.py deleted file mode 100644 index 85febb5..0000000 --- a/example/app/music/migrations/0003_song_lyrics.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11 on 2017-04-11 13:51 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('music', '0002_auto_20170411_1350'), - ] - - operations = [ - migrations.AddField( - model_name='song', - name='lyrics', - field=models.TextField(blank=True), - ), - ] diff --git a/example/app/music/migrations/__init__.py b/example/app/music/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/example/app/music/models.py b/example/app/music/models.py deleted file mode 100644 index cf3cc06..0000000 --- a/example/app/music/models.py +++ /dev/null @@ -1,22 +0,0 @@ -from django.db import models - - -class Artist(models.Model): - '''A music artist''' - name = models.CharField(max_length=128) - about = models.TextField(blank=True) - - -class Album(models.Model): - '''A music album.''' - name = models.CharField(max_length=128) - description = models.TextField(blank=True) - artist = models.ForeignKey(Artist, on_delete=models.CASCADE) - - -class Song(models.Model): - '''A music song.''' - name = models.CharField(max_length=128) - description = models.TextField(blank=True) - lyrics = models.TextField(blank=True) - album = models.ForeignKey(Album, on_delete=models.CASCADE) diff --git a/example/app/music/templates/music/album_form.html b/example/app/music/templates/music/album_form.html deleted file mode 100644 index baf5208..0000000 --- a/example/app/music/templates/music/album_form.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends "base.html" %} - -{% block extra_head %} - {{ form.media }} -{% endblock %} - -{% block content %} -
{% csrf_token %} - {{ form.as_p }} - -
-{% endblock %} \ No newline at end of file diff --git a/example/app/music/templates/music/artist_form.html b/example/app/music/templates/music/artist_form.html deleted file mode 100644 index baf5208..0000000 --- a/example/app/music/templates/music/artist_form.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends "base.html" %} - -{% block extra_head %} - {{ form.media }} -{% endblock %} - -{% block content %} -
{% csrf_token %} - {{ form.as_p }} - -
-{% endblock %} \ No newline at end of file diff --git a/example/app/music/templates/music/song_form.html b/example/app/music/templates/music/song_form.html deleted file mode 100644 index c4c1c52..0000000 --- a/example/app/music/templates/music/song_form.html +++ /dev/null @@ -1,13 +0,0 @@ -{% extends "base.html" %} - -{% block extra_head %} - {{ form.media }} -{% endblock %} - -{% block content %} -

Song

-
{% csrf_token %} - {{ form.as_p }} - -
-{% endblock %} \ No newline at end of file diff --git a/example/app/music/tests.py b/example/app/music/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/example/app/music/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/example/app/music/urls.py b/example/app/music/urls.py deleted file mode 100644 index 7a340ba..0000000 --- a/example/app/music/urls.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.conf.urls import url - -from music.views import CreateArtist, CreateAlbum, CreateSong - - -urlpatterns = [ - url(r'^songs/create$', CreateSong.as_view()), - url(r'^albums/create$', CreateAlbum.as_view()), - url(r'^artists/create$', CreateArtist.as_view()), -] diff --git a/example/app/music/views.py b/example/app/music/views.py deleted file mode 100644 index c94160a..0000000 --- a/example/app/music/views.py +++ /dev/null @@ -1,22 +0,0 @@ -from django.views.generic.edit import CreateView - -from music.models import Artist, Album, Song -from music.forms import ArtistForm, AlbumForm, SongForm - - -class CreateArtist(CreateView): - '''Create an artist view.''' - model = Artist - form_class = ArtistForm - - -class CreateAlbum(CreateView): - '''Create an artist view.''' - model = Album - form_class = AlbumForm - - -class CreateSong(CreateView): - '''Create an artist view.''' - model = Song - form_class = SongForm diff --git a/example/app/templates/base.html b/example/app/templates/base.html deleted file mode 100644 index c69277e..0000000 --- a/example/app/templates/base.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - Django Pagedown - - - - - - - - {% block extra_head %}{% endblock %} - - - - {% block content %}{% endblock %} - - \ No newline at end of file diff --git a/example/requirements.txt b/example/requirements.txt deleted file mode 100644 index 16d2bbd..0000000 --- a/example/requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -appdirs==1.4.3 -Django==2.0 -django-pagedown==0.1.3 -packaging==16.8 -pyparsing==2.2.0 -pytz==2017.2 -six==1.10.0 diff --git a/pagedown/__init__.py b/pagedown/__init__.py index 418ca37..ee4bafc 100644 --- a/pagedown/__init__.py +++ b/pagedown/__init__.py @@ -1 +1,3 @@ -VERSION = ("1", "0", "6") +VERSION = ('2', '0', '0') + +default_app_config = 'pagedown.apps.PagedownConfig' diff --git a/pagedown/apps.py b/pagedown/apps.py new file mode 100644 index 0000000..a7b74f7 --- /dev/null +++ b/pagedown/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class PagedownConfig(AppConfig): + name = 'pagedown' + verbose_name = 'Pagedown' diff --git a/pagedown/forms.py b/pagedown/forms.py index 11752a5..9f1e512 100644 --- a/pagedown/forms.py +++ b/pagedown/forms.py @@ -13,12 +13,3 @@ class AdminPagedownField(forms.CharField): """ A simple CharField that allows us avoid having to write widget code """ widget = AdminPagedownWidget - - -try: - from south.modelsinspector import add_introspection_rules - - add_introspection_rules([], ["^pagedown\.forms\.PagedownField"]) - add_introspection_rules([], ["^pagedown\.forms\.AdminPagedownField"]) -except ImportError: - raise diff --git a/pagedown/settings.py b/pagedown/settings.py deleted file mode 100644 index 7b706d6..0000000 --- a/pagedown/settings.py +++ /dev/null @@ -1,8 +0,0 @@ -from django.conf import settings - -SHOW_PREVIEW = getattr(settings, "PAGEDOWN_SHOW_PREVIEW", True) -WIDGET_TEMPLATE = getattr( - settings, "PAGEDOWN_WIDGET_TEMPLATE", "pagedown/widgets/default.html") -WIDGET_CSS = getattr( - settings, "PAGEDOWN_WIDGET_CSS", ("pagedown/demo/browser/demo.css",)) -EXTENSIONS = getattr(settings, "PAGEDOWN_EXTENSIONS", []) diff --git a/pagedown/static/admin/css/pagedown.css b/pagedown/static/admin/css/pagedown.css index 588dabb..55109ad 100644 --- a/pagedown/static/admin/css/pagedown.css +++ b/pagedown/static/admin/css/pagedown.css @@ -2,39 +2,54 @@ display: inline-block; } +@media (max-width: 767px) { + .wmd-wrapper{ + width: 100%; + } +} + .wmd-panel{ - margin-left: 0; + display: flex; + flex-direction: column; + + max-width: 100%; + min-width: auto; + width: 100%; + margin: 0; } - .wmd-panel textarea { - outline: 0; - } + +.wmd-button-bar { + background-color: white; +} .wmd-button-row{ margin-left: 0 !important; } -.wmd-input{ +.wmd-input { + width: 100%; background-color: white; border: 1px solid #ccc; - border-radius: 4px; - padding: 5px 6px; - margin-top: 0; } -.wmd-preview-title { - margin: 10px 0 5px 0 !important; - padding-left: 0px !important; +@media (max-width: 767px) { + .colM .aligned .vLargeTextField.wmd-input { + width: 100%; + } } .wmd-preview{ - width: 625px; + width: 100%; + max-height: 400px; - overflow-y: scroll; + overflow-y: auto; box-sizing: border-box; padding: 12px 14px; - margin: 0; + margin: 10px 0 0 0; + border: 1px solid #eee; + border-radius: 4px; background: #f8f8f8; } @@ -127,8 +142,9 @@ .wmd-preview blockquote{ margin: 0 0 15px 0; - padding-left: 1em; - border-left: 0.5em #ddd solid; + padding: 10px; + border-left: 2px #ddd solid; + background: #eee; } .wmd-preview blockquote p { @@ -138,7 +154,7 @@ .wmd-preview hr { display: block; - height: 0px; + height: 0; border: 0; font-style: italic; border-bottom: 1px solid #ddd; @@ -156,8 +172,8 @@ /* Code inline */ .wmd-preview code { - margin: 0 2px; - padding: 0px 5px; + margin: 2px; + padding: 0px 3px; border: 1px solid #ddd; background-color: #f8f8f8; border-radius: 2px; @@ -182,6 +198,7 @@ padding-left: 1em; background-color: #eee; } + .wmd-preview pre code { background-color: transparent; border: 0; @@ -244,6 +261,6 @@ .wmd-preview td { vertical-align: top; } /* Inlines */ -.inline-related .wmd-input { +.inline-related textarea { height: 150px; } diff --git a/pagedown/static/pagedown b/pagedown/static/pagedown index 77f33b4..32c0bdb 160000 --- a/pagedown/static/pagedown +++ b/pagedown/static/pagedown @@ -1 +1 @@ -Subproject commit 77f33b47bc670b50a3b19947a7a434f7138feabd +Subproject commit 32c0bdb121de04e0e956ddde0391022644ff1a76 diff --git a/pagedown/static/pagedown_init.js b/pagedown/static/pagedown_init.js index 593fb82..8d2924a 100644 --- a/pagedown/static/pagedown_init.js +++ b/pagedown/static/pagedown_init.js @@ -9,21 +9,14 @@ DjangoPagedown = (function() { var that = this; var isPagedownable = function(el) { - if ( (' ' + el.className + ' ').indexOf(' wmd-input ') > -1 ) { - return true; - } - return false; + return el.id.indexOf('wmd-input') > -1; }; var createEditor = function(el) { - if ( isPagedownable(el) ) { - if ( ! that.editors.hasOwnProperty(el.id) ) { - var selectors = { - input : el.id, - button : el.id + "_wmd_button_bar", - preview : el.id + "_wmd_preview", - }; - that.editors[el.id] = new Markdown.Editor(that.converter, "", selectors); + if (isPagedownable(el)) { + if (! that.editors.hasOwnProperty(el.id)) { + var id = el.id.substr(9); + that.editors[el.id] = new Markdown.Editor(that.converter, id, {}); that.editors[el.id].run(); return true; } else { diff --git a/pagedown/templates/pagedown/forms/widgets/attrs.html b/pagedown/templates/pagedown/forms/widgets/attrs.html new file mode 100644 index 0000000..6da6eb7 --- /dev/null +++ b/pagedown/templates/pagedown/forms/widgets/attrs.html @@ -0,0 +1 @@ +{% for name, value in widget.attrs.items %}{% if value is not False %} {{ name }}{% if value is not True %}="{% if name == 'id' %}wmd-input-{% endif %}{{ value|stringformat:'s' }}"{% endif %}{% endif %}{% endfor %} \ No newline at end of file diff --git a/pagedown/templates/pagedown/forms/widgets/default.html b/pagedown/templates/pagedown/forms/widgets/default.html new file mode 100644 index 0000000..9c24516 --- /dev/null +++ b/pagedown/templates/pagedown/forms/widgets/default.html @@ -0,0 +1,7 @@ +
+
+
+ {% include 'pagedown/forms/widgets/textarea.html' %} +
+
+
diff --git a/pagedown/templates/pagedown/forms/widgets/textarea.html b/pagedown/templates/pagedown/forms/widgets/textarea.html new file mode 100644 index 0000000..758d5fd --- /dev/null +++ b/pagedown/templates/pagedown/forms/widgets/textarea.html @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/pagedown/templates/pagedown/widgets/default.html b/pagedown/templates/pagedown/widgets/default.html deleted file mode 100644 index 6f1bf78..0000000 --- a/pagedown/templates/pagedown/widgets/default.html +++ /dev/null @@ -1,12 +0,0 @@ -
-
-
- -
- {% if show_preview %} -

- HTML Preview: -

-
- {% endif %} -
diff --git a/pagedown/utils.py b/pagedown/utils.py deleted file mode 100644 index 86d2877..0000000 --- a/pagedown/utils.py +++ /dev/null @@ -1,31 +0,0 @@ -from django import VERSION -from django.conf import settings - - -def compatible_staticpath(path): - """ - Try to return a path to static the static files compatible all - the way back to Django 1.2. If anyone has a cleaner or better - way to do this let me know! - """ - - if VERSION >= (1, 10): - # Since Django 1.10, forms.Media automatically invoke static - # lazily on the path if it is relative. - return path - try: - # >= 1.4 - from django.templatetags.static import static - return static(path) - except ImportError: - pass - try: - # >= 1.3 - return '%s/%s' % (settings.STATIC_URL.rstrip('/'), path) - except AttributeError: - pass - try: - return '%s/%s' % (settings.PAGEDOWN_URL.rstrip('/'), path) - except AttributeError: - pass - return '%s/%s' % (settings.MEDIA_URL.rstrip('/'), path) diff --git a/pagedown/widgets.py b/pagedown/widgets.py index 21459bb..fe11fd4 100644 --- a/pagedown/widgets.py +++ b/pagedown/widgets.py @@ -1,95 +1,31 @@ -from django import VERSION, forms -from django.contrib.admin import widgets as admin_widgets -from django.utils.html import conditional_escape -from django.template import Context, loader - -# Django 1.7 compatibility -try: - from django.forms.utils import flatatt -except ImportError: - from django.forms.util import flatatt - -from pagedown import settings as pagedown_settings -from pagedown.utils import compatible_staticpath - -# Python 3 compatibility -# https://docs.djangoproject.com/en/1.5/topics/python3/#string-handling -try: - from django.utils.encoding import force_unicode -except ImportError: - from django.utils.encoding import force_text as force_unicode +from django import forms +from django.contrib.admin import widgets class PagedownWidget(forms.Textarea): + template_name = 'pagedown/forms/widgets/default.html' - def __init__(self, *args, **kwargs): - self.show_preview = kwargs.pop( - "show_preview", pagedown_settings.SHOW_PREVIEW) - self.template = kwargs.pop( - "template", pagedown_settings.WIDGET_TEMPLATE) - self.css = kwargs.pop("css", pagedown_settings.WIDGET_CSS) - super(PagedownWidget, self).__init__(*args, **kwargs) + def __init__(self, attrs=None): + super(PagedownWidget, self).__init__(attrs=attrs) + # Add wmd-input class for easier styling + self.attrs['class'] = '{} wmd-input'.format( + self.attrs.get('class', '')) - def _media(self): - return forms.Media( - css={ - "all": self.css - }, - js=( - compatible_staticpath("pagedown/Markdown.Converter.js"), - compatible_staticpath( - "pagedown-extra/pagedown/Markdown.Converter.js"), - compatible_staticpath("pagedown/Markdown.Sanitizer.js"), - compatible_staticpath("pagedown/Markdown.Editor.js"), - compatible_staticpath("pagedown-extra/Markdown.Extra.js"), - compatible_staticpath("pagedown_init.js"), - )) + class Media: + js = ('pagedown/Markdown.Converter.js', + 'pagedown-extra/pagedown/Markdown.Converter.js', + 'pagedown/Markdown.Sanitizer.js', + 'pagedown/Markdown.Editor.js', + 'pagedown-extra/Markdown.Extra.js', + 'pagedown_init.js') - media = property(_media) - def render(self, name, value, attrs=None, renderer=None): - if value is None: - value = "" +class AdminPagedownWidget(PagedownWidget, widgets.AdminTextareaWidget): - extra_attrs = { - 'name': name + class Media: + css = { + 'all': ('pagedown/demo/browser/demo.css', + 'admin/css/pagedown.css',) } - - extra_attrs.update(self.attrs) - - # Signature for build_attrs changed in 1.11 - # https://code.djangoproject.com/ticket/28095 - if VERSION < (1, 11): - final_attrs = self.build_attrs(attrs, **extra_attrs) - else: - final_attrs = self.build_attrs(attrs, extra_attrs=extra_attrs) - - if "class" not in final_attrs: - final_attrs["class"] = "" - final_attrs["class"] += " wmd-input" - - template = loader.get_template(self.template) - - # Compatibility fix: - # see https://github.com/timmyomahony/django-pagedown/issues/42 - context = { - "attrs": flatatt(final_attrs), - "body": conditional_escape(force_unicode(value)), - "id": final_attrs["id"], - "show_preview": self.show_preview, - } - context = Context(context) if VERSION < (1, 9) else context - return template.render(context) - - -class AdminPagedownWidget(PagedownWidget, admin_widgets.AdminTextareaWidget): - def _media(self): - return super(AdminPagedownWidget, self).media + forms.Media( - css={ - "all": (compatible_staticpath("admin/css/pagedown.css"),) - }, - js=( - compatible_staticpath("admin/js/pagedown.js"), - )) - - media = property(_media) + js = ('admin/js/jquery.init.js', + 'admin/js/pagedown.js') diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..d5cf04f Binary files /dev/null and b/screenshot.png differ diff --git a/setup.py b/setup.py index 737a5a5..4d92d8d 100644 --- a/setup.py +++ b/setup.py @@ -6,6 +6,7 @@ from pagedown import VERSION + def get_submodules(): if path.exists('.git'): check_call(['rm', '-rf', 'pagedown/static/pagedown']) @@ -32,13 +33,13 @@ def run(self): author="Timmy O'Mahony", author_email="hey@timmyomahony.com", url="https://github.com/timmyomahony/django-pagedown", - description=("A django app that allows the easy addition of Stack Overflow's 'PageDown' markdown editor to a django form field"), + description=("A Django app that allows the easy addition of Stack Overflow's 'PageDown' markdown editor to a django form field"), long_description=open('README.md').read(), packages=['pagedown'], include_package_data=True, - # _Should_ work back to 1.10 but untested + # _Should_ work back to 1.1 but untested install_requires=[ - "Django >= 1.8", + "Django >= 2.1", ], license='LICENSE.txt', cmdclass={"build": build_with_submodules, "develop": develop_with_submodules}, @@ -46,14 +47,14 @@ def run(self): classifiers=[ 'Intended Audience :: Developers', 'Topic :: Text Editors', - 'Framework :: Django :: 1.8', - 'Framework :: Django :: 1.9', - 'Framework :: Django :: 2.0', - 'Programming Language :: Python :: 2.6', - 'Programming Language :: Python :: 2.7', + 'Framework :: Django :: 2.1', + 'Framework :: Django :: 2.2', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4' + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7' ] )