Package: django-simple-notes
django_simple_notesAdd private (available only to logged in users) notes to different pages of your Django-CMS site, through generic foreign keys (using content types).Requirementsdjango-filer: manage media files using folders/permissions & a dedicated interfacedjango-ckeditor: install version <6 if you're still on django<3django-ckeditor-filebrowser-filer: allow using django-filer filer system for image/file uploaddjango: work with django 2.2+python: work with python >= 3.10 (may work with older versions of python 3)Installrunpython3 -m pip install django_simple_notesoptional: adddjango-ckeditor<6if you're still using django < 3add those apps to yourINSTALLED_APPS:"filer","ckeditor","ckeditor_uploader",# for hosting images in your ckeditor view, see below for a ready-to-use config"ckeditor_filebrowser_filer","django_simple_notes",config this (see the doc here):CKEDITOR_UPLOAD_PATH=""add those urls in yoururls.py:# Upload images using ckeditor & django-filerpath('ckeditor/',include('ckeditor_uploader.urls')),path('filer/',include('filer.urls')),path('filebrowser_filer/',include('ckeditor_filebrowser_filer.urls')),runpython manage.py migrate django_simple_notesthat's all folks!FeaturesAdd private notes on nearly each page of your sitesupports djangocms pages & djangocms-blog posts out of the box!youcanadd your own function to support even more type of pages (in apphooks)!privates notes contain 1 ckeditor field (you can add images inside it), and many file fields (if you need to store a particular pdf file for a page for example)Supports adding different notes for same page in different languagesSingle-site & multisite support!Templatetags, yay!There's a templatetag you can use to display a text if a private note exists on the current page and is not empty. The templatetag will only returnTrueif the current user is logged in and have the permission to view the private note.Here's how to use it:{%loaddjango_simple_notes%}{%have_non_empty_noteascheck_have_non_empty_note%}{%ifcheck_have_non_empty_note%}<p><i>This page have a private note.</i></p>{%endif%}How it works?The main logic is incms_toolbars.py, in theget_content_type_and_idfunction.This function will try to retrieve the content type and the id of the object (CMS Page, DjangoCMS-blog Post...). If it find the object, another function will create an empty SimpleNote for this object, and will save it. This way, we don't need to differentiate the create and the edit link of the note in the toolbar (I don't know if it's possible to refresh only the toolbar to return the right link (create/edit)).Another cool function is theget_querysetinadmin.py. It will return the full queryset if we're on achangepage (edit the object in the admin). It will however only returns the "non-empty" objects in thelistview (in order to not clutter the list view too much).Customize it!You can define your own function that findobject_ctandobject_idin your settings!Django simple notes will try to get a function from your site settings, namedSIMPLE_NOTES_CUSTOM_CONTENT_TYPE_AND_ID. It will then execute it, and if it returnsNone, Noneit will launch its ownget_content_type_and_idfunction (in order to retrieve cms/djangocms-blog ct/id).Here's an example to get you started:Just copy/paste this file in your site settings, and update it to suits your needs.defSIMPLE_NOTES_CUSTOM_CONTENT_TYPE_AND_ID(request):# init parametersobject_ct=Noneobject_id=None# condition from request (use this to find the object/page type)ifrequest.resolver_match.view_namein("my_app_view",):# necessary imports, be sure to include them in your function or you might stumble accross an import loopfrommy_app.modelsimportMyModelslug=(request.resolver_match.kwargs["slug"]if"slug"inrequest.resolver_match.kwargselse"")# a condition (if we have a slug, then retrieve the object using its slug)ifslug:my_object=MyModel.objects.get(slug=slug)object_ct=my_objectobject_id=my_object.idelse:# another condition (for if we have its id)id=(request.resolver_match.kwargs["pk"]if"pk"inrequest.resolver_match.kwargselse"")ifid:my_object=MyModel.objects.get(id=id)object_ct=MyModelobject_id=my_object.id# return found ct/id or None/None# None/None will allow the regular `get_content_type_and_id` to proceed, searching for cms or blog pagesreturnobject_ct,object_idIn order to get the object url, you will need to add another functionThis function is namedSIMPLE_NOTES_ADMIN_URLS, and is called inadmin.pyif it exists.It's goal is to return an url for the current object, in order to add a direct link to the page in the admin list view.If the returned url isNone, then simple notes will try to get the url of a CMS page or a djangocms-blog Post.If no link is found, the object name will be displayed without a link.Here's an example to get you started:defSIMPLE_NOTES_ADMIN_URLS(obj):ifobj.content_type.model=="mymodel":# an object from a model with get_absolute_url()frommy_app.modelsimportMyModelreturnMyModel.objects.get(id=obj.object_id).get_absolute_url()ifobj.content_type.model=="myothermodel":# another model, which don't have get_absolute_url()fromdjango.urlsimportreversereturnreverse("view-name",kwargs={"pk":obj.object_id})returnNoneOh, and you can customize this too:SIMPLE_NOTES_TOOLBAR_MENU_TEXT(Default to_("Simple notes")): The toolbar menu text.SIMPLE_NOTES_TOOLBAR_EDIT_TEXT(Default to_("Edit note for current page")): The toolbar edit button text.SIMPLE_NOTES_TOOLBAR_LIST_TEXT(Default to_("Notes list")): The toolbar "show list notes" text.SIMPLE_NOTES_APP_NAME(Default toSIMPLE_NOTES_TOOLBAR_MENU_TEXTcontent): App name in django admin.SIMPLE_NOTES_NOTE_ADMIN_NAME(Default to_("note")): Name of single object in django admin (used in title: "Editingnote").Toolbar screenshot with default values.Ckeditor configYou will need to configuredjango-ckeditorin order to make it work in the notes. Here's a ready-to-use config snippet that you can paste on your project's settings:CKEDITOR_UPLOAD_PATH="uploads/"CKEDITOR_IMAGE_BACKEND="pillow"CKEDITOR_THUMBNAIL_SIZE=(150,150)CKEDITOR_ALLOW_NONIMAGE_FILES=FalseCKEDITOR_CONFIGS={"default":{"language":"{{ language }}","toolbar":"Simple","toolbar_Simple":[["Undo","Redo"],["Styles","Format"],["TextColor","BGColor"],["Subscript","Superscript","-","RemoveFormat","PasteText","PasteFromWord","FilerImage"],["Link","Unlink"],["Source"],],"autoParagraph":False,"colorButton_colors":"01b6ad,00b6ef,a0cd49,ffc01c,9d1a75,fff,000","skin":"moono-lisa","height":"600px","extraPlugins":"filerimage","removePlugins":"image"# do not use the classic image plugin, use the one from django-ckeditor-filebrowser-filer}}You can learn more about thoses config values and customize them values by having a look at thedjango-ckeditor documentation.Todo?Here's a sneak peak of the (maybe) future features:supports simple django apps!