--- /dev/null
+# combo - content management system
+# Copyright (C) 2018 Entr'ouvert
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from django.core.urlresolvers import reverse
+from django.forms.utils import flatatt
+from django.template.loader import render_to_string
+from django.utils.encoding import force_text
+from django.utils.html import conditional_escape
+from django.utils.safestring import mark_safe
+from django.utils.translation import get_language
+
+import ckeditor.views
+import ckeditor.widgets
+from ckeditor.image import pillow_backend
+
+
+def ckeditor_render(self, name, value, attrs=None):
+ if value is None:
+ value = ''
+ final_attrs = {'name': name}
+ if getattr(self, 'attrs', None):
+ final_attrs.update(self.attrs)
+ if attrs:
+ final_attrs.update(attrs)
+ if 'filebrowserUploadUrl' not in self.config:
+ self.config.setdefault('filebrowserUploadUrl', reverse('ckeditor_upload'))
+ if 'filebrowserBrowseUrl' not in self.config:
+ self.config.setdefault('filebrowserBrowseUrl', reverse('ckeditor_browse'))
+ if not self.config.get('language'):
+ self.config['language'] = get_language()
+
+ # Force to text to evaluate possible lazy objects
+ external_plugin_resources = [
+ [force_text(a), force_text(b), force_text(c)] for a, b, c in self.external_plugin_resources
+ ]
+
+ return mark_safe(
+ render_to_string(
+ 'ckeditor/widget.html',
+ {
+ 'final_attrs': flatatt(final_attrs),
+ 'value': conditional_escape(force_text(value)),
+ 'id': final_attrs['id'],
+ 'config': ckeditor.widgets.json_encode(self.config),
+ 'external_plugin_resources': ckeditor.widgets.json_encode(external_plugin_resources),
+ },
+ )
+ )
+
+
+ckeditor.widgets.CKEditorWidget.render = ckeditor_render
+
+orig_should_create_thumbnail = pillow_backend.should_create_thumbnail
+
+
+def should_create_thumbnail(file_path):
+ if file_path.endswith('.gif'):
+ # disable thumbnails for gif to workaround
+ # https://github.com/python-pillow/Pillow/issues/2803
+ return False
+ return orig_should_create_thumbnail(file_path)
+
+
+def get_backend():
+ backend = pillow_backend
+ backend.should_create_thumbnail = should_create_thumbnail
+ return backend
+
+
+ckeditor.views.image_processing.get_backend = get_backend
+# chloro - personal space
+# Copyright (C) 2019 Frederic Peters
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+default_app_config = 'chloro.phyll.apps.AppConfig'
--- /dev/null
+# chloro - personal space
+# Copyright (C) 2019 Frederic Peters
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from django.apps import AppConfig
+
+
+class AppConfig(AppConfig):
+ name = 'chloro.phyll'
+
+ def ready(self):
+ import chloro.monkeypatch
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from django.conf import settings
import ckeditor.fields
tags = TaggableManager(_('Tags'), blank=True)
creation_timestamp = models.DateTimeField(auto_now_add=True)
last_update_timestamp = models.DateTimeField(auto_now=True)
+
+ def get_absolute_url(self):
+ return '/%s/' % self.slug
-<!DOCTYPE html>
+{% load i18n %}<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/> <!-- 🌱 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Coin web de Frédéric Péters</title>
<link rel="stylesheet" type="text/css" href="/static/css/style.css">
+ {% block bottom-head %}
+ {% endblock %}
</head>
<body>
{% block body %}
{% endblock %}
+ {% if request.user.is_staff %}
+ <div class="actions">
+ <a href="/new-note/">{% trans "New Note" %}</a>
+ {% block bottom-actions %}
+ {% endblock %}
+ </div>
+ {% endif %}
</body>
</html>
--- /dev/null
+{% extends "phyll/base.html" %}
+{% load i18n %}
+
+{% block body %}
+<form method="post">
+{% csrf_token %}
+{% trans "Delete?" %}
+<button>{% trans "Delete" %}</button>
+</form>
+{% endblock %}
{% extends "phyll/base.html" %}
+{% load i18n %}
{% block body %}
<h1>{{ object.title }}</h1>
-
<div>{{ object.text|safe }}</div>
+{% endblock %}
+{% block bottom-actions %}
+<a href="edit/">{% trans "Edit" %}</a>
+<a href="delete/">{% trans "Delete" %}</a>
{% endblock %}
--- /dev/null
+{% extends "phyll/base.html" %}
+{% load gadjo i18n static %}
+
+{% block bottom-head %}
+<script src="{% xstatic 'jquery' 'jquery.min.js' %}"></script>
+<script src="{% static "ckeditor/ckeditor/ckeditor.js" %}"></script>
+<script type="text/javascript" src="{% static "ckeditor/ckeditor-init.js" %}"></script>
+{% endblock %}
+
+{% block body %}
+<form method="post">
+{% csrf_token %}
+{{ form.as_p }}
+<button>{% trans "Submit" %}</button>
+</form>
+{% endblock %}
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.conf.urls import url
+from django.contrib.admin.views.decorators import staff_member_required
+from django.views.decorators.cache import never_cache
+
+import ckeditor.views as ckeditor_views
from . import views
urlpatterns = [
+ url(
+ r'^ckeditor/upload/',
+ staff_member_required(ckeditor_views.upload, login_url='login'),
+ name='ckeditor_upload',
+ ),
+ url(
+ r'^ckeditor/browse/',
+ never_cache(staff_member_required(ckeditor_views.browse, login_url='login')),
+ name='ckeditor_browse',
+ ),
+ url(r'^(?P<slug>[\w:-]+)/edit/$', staff_member_required(views.NoteEditView.as_view(), login_url='login')),
+ url(
+ r'^(?P<slug>[\w:-]+)/delete/$',
+ staff_member_required(views.NoteDeleteView.as_view(), login_url='login'),
+ ),
+ url(r'^new-note/$', staff_member_required(views.NoteAddView.as_view(), login_url='login')),
url(r'^(?P<slug>[\w:-]+)/$', views.NoteView.as_view()),
]
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from django.views.generic import DetailView
+from django.views.generic import CreateView, DeleteView, DetailView, UpdateView
from .models import Note
class NoteView(DetailView):
model = Note
+
+
+class NoteEditView(UpdateView):
+ model = Note
+ fields = ['title', 'slug', 'text', 'tags']
+
+
+class NoteAddView(CreateView):
+ model = Note
+ fields = ['title', 'slug', 'text', 'tags']
+
+
+class NoteDeleteView(DeleteView):
+ model = Note
+
+ def get_success_url(self):
+ return '/'
https://docs.djangoproject.com/en/1.11/ref/settings/
"""
+import copy
import os
+from django.conf import global_settings
+
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
+ 'ckeditor',
+ 'gadjo',
'taggit',
'chloro.phyll',
]
# https://docs.djangoproject.com/en/1.11/howto/static-files/
STATIC_ROOT = os.path.join(BASE_DIR, 'collected-static')
STATIC_URL = '/static/'
-
+STATICFILES_FINDERS = list(global_settings.STATICFILES_FINDERS) + ['gadjo.finders.XStaticFinder']
# Media files
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
+# ckeditor
+CKEDITOR_UPLOAD_PATH = 'uploads/'
+CKEDITOR_IMAGE_BACKEND = 'pillow'
+
+CKEDITOR_CONFIGS = {
+ 'default': {
+ 'allowedContent': True,
+ 'removePlugins': 'stylesheetparser',
+ 'toolbar_Own': [
+ ['Source', 'Format', '-', 'Bold', 'Italic'],
+ ['NumberedList', 'BulletedList'],
+ ['JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock'],
+ ['Link', 'Unlink'],
+ ['Image', '-', 'HorizontalRule'],
+ ['RemoveFormat',],
+ ['Maximize'],
+ ],
+ 'toolbar': 'Own',
+ 'resize_enabled': False,
+ 'height': 500,
+ },
+}
local_settings_file = os.environ.get(
'CHLORO_SETTINGS_FILE', os.path.join(os.path.dirname(__file__), 'local_settings.py')
'Programming Language :: Python',
'Programming Language :: Python :: 3',
],
- install_requires=['django>=1.11, <1.12', 'django-ckeditor<=4.5.3', 'django-taggit',],
+ install_requires=['django>=1.11, <1.12', 'django-ckeditor<=4.5.3', 'django-taggit', 'gadjo'],
zip_safe=False,
cmdclass={
'build': build,