A Django-based CMS with a focus on extensibility and concise code

Related tags

CMS feincms
Overview

FeinCMS - An extensible Django-based CMS

https://travis-ci.org/feincms/feincms.svg?branch=next https://travis-ci.org/feincms/feincms.svg?branch=master

When was the last time, that a pre-built software package you wanted to use got many things right, but in the end, you still needed to modify the core parts of the code just because it wasn't (easily) possible to customize the way, a certain part of the system behaved?

Django came to rescue all of us, who were not happy with either doing everything on our own or customizing another software package until it was impossible to update.

The biggest strength of a framework-like design is, that it tries not to have a too strong view of what the user should do. It should make some things easy, but just GET OUT OF THE WAY most of the time.

Just after discovering the benefits of a framework-like approach to software design, we fall back into the rewrite everything all the time mindset and build a CMS which has very strong views how content should be structured. One rich text area, a media library and some templates, and we have a simple CMS which will be good enough for many pages. But what if we want more? If we want to be able to add custom content? What if the user can't be trusted to resize images before uploading them? What if you'd like to add a gallery somewhere in between other content? What if the user should be able to administer not only the main content, but also a sidebar, the footer?

With FeinCMS, this does not sound too good to be true anymore. And it's not even complicated.

FeinCMS is an extremely stupid content management system. It knows nothing about content -- just enough to create an admin interface for your own page content types. It lets you reorder page content blocks using a drag-drop interface, and you can add as many content blocks to a region (f.e. the sidebar, the main content region or something else which I haven't thought of yet). It provides helper functions, which provide ordered lists of page content blocks. That's all.

Adding your own content types is extremely easy. Do you like markdown that much, that you'd rather die than using a rich text editor? Then add the following code to your project, and you can go on using the CMS without being forced to use whatever the developers deemed best:

from markdown2 import markdown
from feincms.module.page.models import Page
from django.db import models

class MarkdownPageContent(models.Model):
    content = models.TextField()

    class Meta:
        abstract = True

    def render(self, **kwargs):
        return markdown(self.content)

Page.create_content_type(MarkdownPageContent)

That's it. Not even ten code lines for your own page content type.

Getting started

Visit these sites

Repository branches

The FeinCMS repository on github has several branches. Their purpose and rewinding policies are described below.

  • maint: Maintenance branch for the second-newest version of FeinCMS.
  • master: Stable version of FeinCMS.

master and maint are never rebased or rewound.

  • next: Upcoming version of FeinCMS. This branch is rarely rebased if ever, but this might happen. A note will be sent to the official mailing list whenever next has been rebased.
  • pu or feature branches are used for short-lived projects. These branches aren't guaranteed to stay around and are not meant to be deployed into production environments.
Comments
  • Unable to delete pages containing custom content types

    Unable to delete pages containing custom content types

    I have several custom page content types of the form:

    class MyWidget(models.Model):
    
        ...fields...
    
        class Meta:
            abstract = True
            verbose_name = _('my widget')
            verbose_name_plural = _('my widgets')
    
    Page.create_content_type(MyWidget)
    

    If I add these to a page, and later try and delete the page I get the IntegrityError exception:

    IntegrityError at /admin/page/page/82/delete/ (1451, 'Cannot delete or update a parent row: a foreign key constraint fails (mydatabase.page_page_mywidget, CONSTRAINT parent_id_refs_id_54aa763d FOREIGN KEY (parent_id) REFERENCES page_page (id))')

    I'm assuming this is due to an inappropriate Django ondelete handler on FK field for the page_page_mywidget table autogenerated by Page.create_content_type().

    Ideally, when a page is deleted, any rows in content type tables linked to the deleted page should also be deleted. I believe this corresponds to the CASCADE ondelete behavior.

    A slow and frustrating workaround is to manually delete all content types on the page, save the page, and then delete the page.

    opened by chrisspen 37
  • Page content is not updating

    Page content is not updating

    I'm having a constant problem. I've updated to the latest version (ab635ef2059b57b260547dc5bf8bdb3ade3247d3) and it's still not working. The site is running with PostgreSQL. When I edit text in a richtext item in my page, and I clicked save, it's not updating. The text just goes back to what it was before. This has been a problem on my WebFaction site, running PostgreSQL for a good few weeks and even updating to latest version still isn't fixing it!

    opened by moreofmorris 24
  • Show that create_content_type modifies current context

    Show that create_content_type modifies current context

    I am trying to register a content type on two models:

    ModelOne.register_regions(('main', 'Main'))
    ModelOne.create_content_type(MyContent)
    ModelTwo.register_regions(('main', 'Main'))
    ModelTwo.create_content_type(MyContent)
    

    But I am receiving an error from the final line:

    ImproperlyConfigured: Cannot create content type from non-abstract model (yet).
    

    This is because MyContent is getting redefined in my models.py on the second line.

    I must admit I am finding this one very hard to debug, but I suspect it is caused by one of the calls to type() in feincms.models.

    The attached diff has a failing test for the problem.

    Do you know what may be causing this? I would love to help if I can, but I am a little stumped as to what action to take.

    opened by meshy 20
  • Content type <content type>_set is already taken

    Content type _set is already taken

    I'm having a lot of trouble getting the blog module working. If i register a content type on Entry, ie.

    Entry.register_regions(
        ('main', 'Main region'),
    )
    Entry.create_content_type(RichTextContent)
    Entry.create_content_type(MediaFileContent, POSITION_CHOICES=(
        ('left', _(u'left')),
        ('right', _(u'right')),
        ('center', _(u'center')),
        ))
    Entry.create_content_type(ImageContent, POSITION_CHOICES=(
        ('left', 'Default position'),
        ('right', 'Default position'),
        ('default', 'Default position'),
        ))
    

    This throws me a lot of errors when starting the dev server.

    RuntimeWarning: Cannot create content type using feincms.content.richtext.models.RichTextContent for feincms.module.blog.models.Entry, because richtextcontent_set is already taken.
    RuntimeWarning: Cannot create content type using feincms.content.medialibrary.models.MediaFileContent for feincms.module.blog.models.Entry, because mediafilecontent_set is already taken.
    RuntimeWarning: Cannot create content type using feincms.content.image.models.ImageContent for feincms.module.blog.models.Entry, because imagecontent_set is already taken.
    

    How can i remedy this situation? I keep the page module stuff in an app called pages and the blog related stuff in app called blog.

    Any help will be greatly appreciated!

    opened by olafskaug 20
  • Page admin showing each extension twice, breaks admin page

    Page admin showing each extension twice, breaks admin page

    We've just started setting up a FeinCMS based blog, but have encountered a small problem.

    We set up the Page model in the normal way, and register a few extensions like this:

    Page.register_extensions(
        'feincms.module.extensions.datepublisher',
        'feincms.module.page.extensions.excerpt',
        'feincms.module.page.extensions.relatedpages',
    )
    

    The extensions appear to only be loaded once - that line of code is run once, no "AlreadyRegistered" exceptions, and the migrations all work fine which implies the model is being created properly.

    However, the admin shows two tabs for each extension. The tabs contain the same content, but only one (non-deterministic) tab will work at any time, which means you have to enter data twice in the admin to be sure that it will save. Also some widgets in the repeated tabs just don't work, an example of this is the multiple selection widget for the "relatedpages" extension.

    I don't know enough about FeinCMS to know where to even begin looking for what might be causing this. Does anyone have any ideas about what might be causing this, or even a good place to start looking?

    documentation 
    opened by danpalmer 16
  • Auto-generated

    Auto-generated "%sContent" classes potentially conflict with User-defined classes.

    If I have a FeinCMS Base subclass MyDocument, and in the same module a FeinCMS content_type (or indeed any class) called MyDocumentContent, this results in database weirdness, because FeinCMS generates a class with the pattern "%sContent".

    Assuming the "%sContent" can't be renamed to something less clashable, then I suggest clashes should be caught and warned against.

    opened by cogat 15
  • My rich text editor data is not being saved?

    My rich text editor data is not being saved?

    Using latest version from github. Django 1.2.1, with a sqlite3 database. It doesn't seem to happen all the time, but a lot of the time, the stuff I'm writing into the rich text editor isn't saving. It says it's saved, but the text reappears just as it was before. I have to delete the rich text editor from the region and then re-add it to then get it to save. ?

    opened by moreofmorris 14
  • Feincms and Django 3.1

    Feincms and Django 3.1

    Feincms (version 1.18.0) build-in page modules (probably not only them) dont work with Django 3.1.

    Duaring the doc (https://feincms-django-cms.readthedocs.io/en/latest/page.html#) "page" field in admin panel returns empty page.

    Temporary solution: uninstall django 3.1 and install django 3.0

    opened by andrey-zubov 13
  • Remove medialibrary dependency on local file system based storage.

    Remove medialibrary dependency on local file system based storage.

    We encountered a couple of problems when using the medialibrary app with a file system other than the default django.core.files.storage.FileSystemStorage.

    When using storages.backends.s3.S3Storage from django-storages MediaFileBase raised an error because it was trying to initialise S3Storage with the kwargs "location" and "base_url". S3Storage is a remote storage using Amazons S3 storage so has no concept of base_url.

    We fixed the issue by catching the exception and initialising the storage with no arguments. In addition the S3Storage does not implement the path property.

    opened by maxpeterson 13
  • threading.local is not that local on Apache wsgi

    threading.local is not that local on Apache wsgi

    It turns out that threading.local on Apache wsgi is persistent over multiple requests which it should not. This turns out to be an issue with the app_reverse function not working properly for web sites which have the same application content in several pages. Usually multi language sites.

    The issue has come up in this ticket: https://github.com/feincms/feincms/issues/306 but that one was about something different. So let's keep the discussion about this issue in this ticket here.

    I'm suggesting getting rid of threading.local and instead use the cache backend for app_reverse caching.

    major bug 
    opened by sbaechler 12
  • New Handler doesn't work if not at URLconf root

    New Handler doesn't work if not at URLconf root

    Just discovered that including 'feincms.urls' at a non-blank point the URLconf (e.g. '/content/') stopped working in an upgrade from 1.4.2 to 1.6.2.

    url(r'^content/', include('feincms.views.legacy.urls')), works correctly

    Losing this functionality seems like a backwards development?

    Also discussed here: https://groups.google.com/forum/?fromgroups#!topic/django-feincms/vFwfGkYdbQg but the suggested solution doesn't work (even after adding .as_view(), the handler doesn't seem to cope with not being at the URLconf root)

    major bug 
    opened by DrMeers 12
  • Deprecation of language storage in session

    Deprecation of language storage in session

    Django 3 docs say (and Django 4 actually removes the feature):

    To limit creation of sessions and hence favor some caching strategies, django.views.i18n.set_language() will stop setting the user’s language in the session in Django 4.0. Since Django 2.1, the language is always stored in the LANGUAGE_COOKIE_NAME cookie.

    We should adapt feincms/extensions/translation.py as well as the tests that rely on that behaviour?

    It's not super-urgent, as the fallback for django1.6 also somewhat covers the dj4 case, but that's more a happy accident than intentional.

    opened by mjl 1
  • Make register_templates plugin-friendly

    Make register_templates plugin-friendly

    Context: When creating a FeinCMS site using some kind of plugin mechanism, I ran into this problem.

    Each plugin wants to contribute some Template to the main project. To do that, it will do something like

    from feincms.module.page.models import Page
    
    regions_main = (('main', 'Content'), )
    Page.register_templates(
        {'key': 'mmmmh', 'title': 'Tasty template', 'path': 'munch/nomnom.html', 'regions': regions_main}
    )
    

    This will not work.

    Each region holds a list of content types that can be used in that region. Unfortunately, this list is built when a concrete content type is created, so calling register_templates again later will add regions that have no content types at all.

    I can paper over this by patching in all content types here

    for region in Page._feincms_all_regions:
        if not region._content_types:
            region._content_types = Page._feincms_content_types.copy()  # copy is important otherwise CTs will be duplicated
    

    but that is not always what is wanted, since I can't restrict CTs to a region that way.

    If we ever want to restrict a CT to a certain region, things will become more complicated (the region set for each CT is not stored anywhere, so that information is lost once the CT has been instanciated).

    Food for thought...

    enhancement 
    opened by mjl 2
  • Add redirect module and/or document work-around

    Add redirect module and/or document work-around

    Hi folks,

    This is between an issue and a feature request I guess. Here is the use case:

    • You set up a page to an url, delivering a customer request.
    • The customer has an internal discussion and the content needs to be moved/renamed
    • You rename/move the content to some other location
    • The old content is still indexed by some pages or search engines, but lead to a 404

    Ideally, there would be a module/plugin that would automatically add 301 redirects to the old urls. Optionally, there is some option to do so while renaming/moving.

    What do you think about it @matthiask ? Is there a work-around, or some way to do so currently? I couldn't find anything like this in the docs, neither browsing the code

    opened by MRigal 3
  • Is there an obvious way to override PageManager?

    Is there an obvious way to override PageManager?

    I would like to override a few methods in PageManager - what would be the most efficient way to achieve that?


    Here's some context to explain why I want to do that.

    I wanted to use Django's Sites framework, and configure FeinCMS in a way that would allow me to control on which sites any particular page can be shown.

    Yes, FeinCMS already has an extension for that: https://github.com/feincms/feincms/blob/e923d0466b74937d36eb45e2c46ea18cfef83d56/feincms/module/page/extensions/sites.py

    However:

    1. It creates 'site' ForeignKey (I want to have 'sites' ManyToMany instead, but I easily fixed that by writing a custom extension)
    2. It relies on Site.objects.get_current()

    Site.objects.get_current() works only if you have SITE_ID defined in your settings, otherwise it raises an error and asks to provide request in order to deduce the current Site. I omitted SITE_ID from my settings on purpose because I want to achieve behavior documented in Django docs: https://docs.djangoproject.com/en/dev/ref/contrib/sites/

    If the setting is omitted, the get_current_site() function will try to get the current site by comparing the domain with the host name from the request.get_host() method.

    There are a few problems, though. I noticed that feincms sites extension modifies which pages are treated as active by using the following approach:

    def current_site(queryset):
        return queryset.filter(site=Site.objects.get_current())
    
    PageManager.add_to_active_filters(current_site, key="current_site")
    

    However, without SITE_ID specified in settings, the only way to filter by current site is to somehow pass request to the filtering logic, and from my understanding, with add_to_active_filter there is no way to do so.

    Which brought me to conclusion that I need to research how Handler view works, because views have access to request.

    I've tried many different approaches, but the only one that worked was to copy FeinCMS Handler code and then manually redefine PageManager methods before Handler class is defined. https://github.com/feincms/feincms/blob/e923d0466b74937d36eb45e2c46ea18cfef83d56/feincms/views/init.py#L29

    from feincms.module.page.models import PageManager
    from .managers import for_request, page_for_path, best_match_for_path
    
    PageManager.for_request = for_request
    PageManager.page_for_path = page_for_path
    PageManager.best_match_for_path = best_match_for_path
    
    logger = logging.getLogger(__name__)
    
    class Handler(ContentView):
        # ...
    

    custom for_request simply passes request to custom page_for_path and best_match_for_path

    page_for_path and best_match_for_path are overridden in order to accept request as an argument and to additionally filter pages with .filter(sites__in=[request.site])

    Although it's very cumbersome, it actually works.

    I tried other, seemingly more elegant approaches, but they didn't work:

    1. If I create a custom manager and try to redefine objects in Page, then AttributeError: 'NoneType' object has no attribute '_mptt_meta' is thrown in django-mptt. It's probably because of all the MetaClass magic. I assume there must be some way to fix that?
    2. I can override FEINCMS_DEFAULT_PAGE_MODEL, but it creates many unwanted side-effects (i.e. I will have to manually recreate Page admin to import custom Page model, because current admin.py in FeinCMS does not use FENCMS_DEFAULT_PAGE_MODEL). Even when I did that, I had to rename db_table in the custom Page.Meta class, after which django was still complaining about two different models named Page. I ended up ditching this approach because too many things started falling apart.
    3. If I use add_to_class('objects', PageSitesManager()) in my custom extension, where PageSitesManager is the custom Manager will all the important methods overridden, it simply does nothing.

    Is there a more elegant/obvious way to override PageManager without causing errors in django-mptt?

    opened by paramono 1
  • The save buttons should be disabled after change template

    The save buttons should be disabled after change template

    If you add a new page and change the template, you can click on one of the save buttons (_addanother, _continue, _save) before the page if fully loaded, which results in 2 pages being created.

    This behavior could be prevented, if you disable all the save buttons right after the line: form_element.find('[type=submit][name=_save]').click(); in the on_template_key_changed function in item_editor.js or you could render a loading screen to prevent the user from working on the page.

    minor bug 
    opened by dakrulski 1
  • Article edit page makes many queries

    Article edit page makes many queries

    On my install, I find basic article pages take about 5500 queries to produce, and take around 200 seconds to load.

    Specifically, this query is repeated about 2000 times:

    SELECT "medialibrary_mediafiletranslation"."id", "medialibrary_mediafiletranslation"."parent_id", "medialibrary_mediafiletranslation"."language_code", "medialibrary_mediafiletranslation"."caption", "medialibrary_mediafiletranslation"."description" FROM "medialibrary_mediafiletranslation" WHERE ("medialibrary_mediafiletranslation"."parent_id" = 802 AND (UPPER("medialibrary_mediafiletranslation"."language_code"::text) LIKE UPPER('en-us%') OR UPPER("medialibrary_mediafiletranslation"."language_code"::text) LIKE UPPER('en%'))) ORDER BY "medialibrary_mediafiletranslation"."language_code" DESC LIMIT 1
    

    This one around 2000 times as well:

    SELECT "medialibrary_mediafiletranslation"."id", "medialibrary_mediafiletranslation"."parent_id", "medialibrary_mediafiletranslation"."language_code", "medialibrary_mediafiletranslation"."caption", "medialibrary_mediafiletranslation"."description" FROM "medialibrary_mediafiletranslation" WHERE ("medialibrary_mediafiletranslation"."parent_id" = 965 AND (UPPER("medialibrary_mediafiletranslation"."language_code"::text) LIKE UPPER('en-us%') OR UPPER("medialibrary_mediafiletranslation"."language_code"::text) LIKE UPPER('en%'))) ORDER BY "medialibrary_mediafiletranslation"."language_code" DESC LIMIT 1
    

    And this one around 1500 times:

    SELECT "medialibrary_mediafiletranslation"."id", "medialibrary_mediafiletranslation"."parent_id", "medialibrary_mediafiletranslation"."language_code", "medialibrary_mediafiletranslation"."caption", "medialibrary_mediafiletranslation"."description" FROM "medialibrary_mediafiletranslation" WHERE "medialibrary_mediafiletranslation"."parent_id" = 346 LIMIT 1
    
    opened by danpalmer 9
An open source CMS, in python and integrable in Django

Python CMS based on the Django Framework

Titouan Bénard 0 Sep 10, 2021
CMS framework for Django

Created by Stephen McDonald Overview Mezzanine is a powerful, consistent, and flexible content management platform. Built using the Django framework,

Stephen McDonald 4.6k Dec 29, 2022
Random tarot card generator + rudimentary Django CMS

TAROT JUICER This is a rudimentary Django-based CMS which dynamically presents tarot-related content placed onto unconventional but familiar contexts

Kyle Rafa Lazaro 7 Apr 26, 2022
Django CMS Project for quicksetup with minimal installation process.

Django CMS Project for quicksetup with minimal installation process.

Dipankar Chowdhury 3 Mar 24, 2022
Kotti is a high-level, Pythonic web application framework based on Pyramid and SQLAlchemy. It includes an extensible Content Management System called the Kotti CMS.

Kotti Kotti is a high-level, Pythonic web application framework based on Pyramid and SQLAlchemy. It includes an extensible Content Management System c

Kotti 394 Jan 7, 2023
The easy-to-use and developer-friendly CMS

django CMS Open source enterprise content management system based on the Django framework and backed by the non-profit django CMS Association. Get inv

django CMS Association 9.1k Jan 4, 2023
The easy-to-use and developer-friendly CMS

django CMS Open source enterprise content management system based on the Django framework and backed by the non-profit django CMS Association. Get inv

django CMS Association 9.1k Jan 8, 2023
The Plone CMS: root integration package

About Plone Plone is a mature, secure and user-friendly Content Management System (CMS). Plone - and the Open Source community behind it - aggregates

Plone Foundation 200 Jan 8, 2023
A python open source CMS scanner that automates the process of detecting security flaws of the most popular CMSs

CMSmap CMSmap is a python open source CMS scanner that automates the process of detecting security flaws of the most popular CMSs. The main purpose of

RazzorBack 1 Oct 31, 2021
A plugin for Wagtail CMS, to have Icon Blocks (Fontawesome support)

WAGTAIL ICONIFY Library developed for Wagtail CMS, its purpose is to provide icon blocks from various libraries Special thanks to Alex Gleason, as wel

null 2 Jun 7, 2022
Open Source CRM based on Django

Django-CRM Django CRM is opensource CRM developed on django framework. It has all the basic features of CRM to start with. We welcome code contributio

MicroPyramid 1.4k Dec 31, 2022
A Django content management system focused on flexibility and user experience

Wagtail is an open source content management system built on Django, with a strong community and commercial support. It's focused on user experience,

Wagtail 13.8k Jan 1, 2023
A modular, high performance, headless e-commerce platform built with Python, GraphQL, Django, and ReactJS.

Saleor Commerce Customer-centric e-commerce on a modern stack A headless, GraphQL-first e-commerce platform delivering ultra-fast, dynamic, personaliz

Mirumee Labs 17.8k Jan 7, 2023
Simple yet powerful and really extendable application for managing a blog within your Django Web site.

Django Blog Zinnia Simple yet powerful and really extendable application for managing a blog within your Django Web site. Zinnia has been made for pub

Julien Fache 2.1k Dec 24, 2022
django blog - complete customization and ready to use with one click installer

django-blog-it Simple blog package developed with Django. Features: Dynamic blog articles Blog pages Contact us page (configurable) google analytics S

MicroPyramid 220 Sep 18, 2022
A full stack e-learning application, this is the backend using django restframework and docker.

DevsPrime API API Service backing client interfaces Technologies Python 3.9 : Base programming language for development Bash Scripting : Create conven

Nnabue Favour Chukwuemeka 1 Oct 21, 2021
Django e-commerce website with Advanced Features and SEO Friendly

MyTech® - Your Technology Django e-commerce website with Advanced Features and SEO Friendly Images and Prices are only used for Demo purpose and does

null 28 Dec 21, 2022
A Django blog app implemented in Wagtail

Puput Puput is a powerful and simple Django app to manage a blog. It uses the awesome Wagtail CMS as content management system. Puput is the catalan n

APSL 535 Jan 8, 2023
An encylopedia that runs on Django as part of CS50w's coursework

Django Wiki As part of the CS50w course, this project aims to apply the use of Django together with HTML and CSS to replicate an encyclopedia. Require

Beckham 1 Oct 28, 2021