When I brought the first version of this site up a I wrote an, ah...difficult to read article on how the site was built using Mezzanine. Oh sure, you could copy/paste diff files and generate the changes for each step in the process. But as a tutorial, almost unreadable and hardly a worthy guardian of learning.
To go with this new and slightly improved revision of the site is a new and (hopefully) much improved Mezzanine tutorial.
If you're already setup for Django dev you can probably skip some or all of this setup section. If you're new to Python or Django development, or have a freshly minted Ubuntu site and need to get your bearings to work with Mezaanine, this section can help.
I use virtualenv and virtualenvwrapper. The former is essential for python dev, the latter recommended. I also install the python fabric package in system dirs, as it's used by Mezzanine and many other apps for deploy. Note that these can also be installed from apt-get, but those packages won't track as quickly as the pypi repo.
sudo easy_install pip # no pip in ubuntu < 10, make sure sudo pip install --upgrade virtualenv virtualenvwrapper sudo pip install --upgrade 'fabric>=1.0'
Compiler and python dev packages are needed. On Ubuntu that's
sudo apt-get --yes install build-essential python-setuptools python-dev \
python-software-properties
Before the Mezzanine install, make sure dev packages are installed to give us jpeg, png and other formats in the PIL module. Actually, Mezzanine uses Pillow, which is a "easy on the installee" fork of PIL. Check the lates Pillow docs for requirements. Currently on Ubuntu they are:
sudo apt-get install --yes libtiff4-dev libjpeg8-dev zlib1g-dev libfreetype6-dev \
liblcms1-dev libwebp-dev
I do almost all my python in a virtualenv. With virtualenvwrapper it's
mkvirtualenv rodmtech_net # use your project name as you like
With the Mezzanine install, include south and django-compressor. Mezzanine uses django-compressor automatically in its templates, and sets up for south, which can be used, or not.
pip install mezzanine south django-compressor
My current practice is to build the site from the top virtualenv dir, under
project
directory. That conforms to how the Mezzanine fabric script
deploys. The mezzanine-project
script installs the initial files for the site in that directory.
cdvirtualenv # a virtualenvwrapper function mezzanine-project project cd project pwd >../.project # virtualenvwrapper has a cdproject function that uses this path
OPTIONAL: If you're starting a "real" project, now is a good time to initialize and make a first commit.
# optional scm hg init . hg add . hg commit -m 'initial project setup from mezzanine-project'
Let's see what mezzanine-project command produced:
./deploy/ # templates used by fabfile.py ./fabfile.py # python fabric commands to setup and deploy a server ./.gitignore # initial ignore file for git... ./.hgignore # and mercurial ./__init__.py ./local_settings.py # local overrides to settings.py, put install specific stuff here ./manage.py # a *modified* django command wrapper ./requirements/project.txt # pip requirements to install the site ./settings.py # django settings ./urls.py # top level url routing ./wsgi.py # wsgi wrapper for deploy to wsgi servers
At this point, we can populate a dev database and run a vanilla Mezzanine site. The initial mezzanine produced DATABASES var in local_settings.py will use a sqlite database, dev.db. I'm not covering postgres or mysql setups, they're well documented. and standard django stuff.
Mezzanine adds a number of commands to manage.py, including "createdb", a Mezzanine specific alternative to django's "syndb". Createdb runs Django's syncdb and South's migrate command. Createdb also installs sample data, unless you tell it not to with --nodata.
Create the dev db with sample data. With the --noinput option createdb will create one django superuser account, admin/default.
cdproject python manage.py createdb --noinput python manage.py runserver
You can browse to http://localhost:8000/ login as admin (password: default) create some pages, blog entries, add photos to the gallery, etc.
Now, let's add pagedown, my preferred markdown integration for Mezzanine. Install Pygments also, for spiffy code syntax highlighting. While you at it, add those two packages to your pip requirements file.
pip install mezzanine-pagedown Pygments pip freeze | grep mezzanine-pagedown >>requirements/project.txt pip freeze | grep Pygments >>requirements/project.txt
I generaly only add the top level projects I directly install to requirements, and let those pacakges call out their dependencies. However, for production, I like to have exactly what I'm developing with. I'll run the following whenever I touch the packages, for use in deploys.
pip freeze >requirements/production.txt
See the pip docs for more info:
Note: if you want to stick with the Tinymce markup editor, you can skip the rest of this section about configurin pagedown.
The pagedown setup is per these instructions. Summarizing, in
settings.py, add "mezzanine_pagedown"
to the settings.py
INSTALLED_APPS list...
INSTALLED_APPS = ( "mezzanine_pagedown", "django.contrib.admin", "django.contrib.auth",
and the following somewhere in settings.py (I put it just above DEPLOY SETTINGS). [Why both RICHTEXT_FILTER and RICHTEXT_FILTERS? At the time of this writing, this ticket was not fixed, but I expect it is now.)
##################### # PAGEDOWN SETTINGS # ##################### RICHTEXT_WIDGET_CLASS = 'mezzanine_pagedown.widgets.PageDownWidget' RICHTEXT_FILTER = 'mezzanine_pagedown.filters.custom' RICHTEXT_FILTERS = (RICHTEXT_FILTER,) PAGEDOWN_MARKDOWN_EXTENSIONS = ('extra','codehilite','toc') RICHTEXT_FILTER_LEVEL = 3 PAGEDOWN_SERVER_SIDE_PREVIEW = True
Server side previews are selected, so near the top of urls.py add
import mezzanine_pagedown.urls
Also in urls.py, the pagedown uri MUST go above the mezzanine "catch all" (which is near the bottom anyway). I'll put it near the top, just under "urlpatterns =".
urlpatterns = patterns("", ("^pagedown/", include(mezzanine_pagedown.urls)),
It's a good time for a sanity check: check a page with markdown syntax.
python manage.py runserver
Create a new RichTextPage from http://localhost:8000/admin/pages/page/ (createdb --noinput created an admin account with password 'default'). Give a new page a title and some content like
## hi :::python def foo(): return "foo" 1. one 1. two
While you're entering the page content in the admin, you should see the processed markdown displayed below. Save and view the page on the site.
Optional: If you get, and want to get rid of, the following
deprecation warning: You haven't defined the ALLOWED_HOSTS settings, which Django 1.5
requires. Will fall back to the domains configured as sites.
add
ALLOWED_HOSTS = ('localhost', '.local')
to your settings.py. That'll do for dev, but you'll want to deal with this properly in production. See the Django docs on ALLOWED_HOSTS for more information on this important production setting. Btw, Django won't complain about this in debug mode...it's Mezzanine with a failsafe warning.
If you wish, you can review the changeset from Part 1 on bitbucket.
Ok, let's start customizing using a Pygments style
for the markdown codehilter extension. Pygments
and mezzanine-pagedown were installed and in the previous section,
and there is a new manage.py command, pygments_styles
.
python manage.py pygments_styles
which should give you a list like
Usage: ./manage.py pygments_styles <scheme_name> Available color schemes: monokai manni rrt perldoc borland colorful
...and so on. The same utility will generate customized css for codehilite
python manage.py pygments_styles colorful
Great, so what's the Mezzanine-esque place to put it? Well, there's no one answer. One place you do not want to put it is in the top level static dir. The manage.py collectstatic feature (very handy) dumps all static known to the static-files app there. Then, a capable static delivery system, e.g., Apache, Nginx or a CDN, serve static outside of Django. (The Django development server has been handling that so far, but very inefficient for production.)
Two ways to go here, Django options, not specific to Mezzanine:
add a STATICFILES_DIRS list to settings, with a path to the top level of user supplied static files, or
add an app to the django project via INSTALLED_APPS with a subdir named 'static'.
I prefer the latter because it avoids littering the top level with subdirs. Plus, there's already that top level ./static that will be used by collectstatic, and seperate top level dir for static files I don't like. IMHO, using an app (python module dir, really) is more in tune with current Django practice: Django 1.5, changed from 1.3, creates a subdir of the same name as the top level project, and puts settings and urls there.
Note: mezzanine-project generates and "old" 1.3 layout. It's not difficult to massage that into the current Django scheme. Mezzanine will likely catch up in due time.
It's worth mentioning that in the current Django docs on static files, in a sidebar called "Static file namespacing" somewhat discourages serving static files from app subdirs. I'm not sure why this is, perhaps it's legacy documentation, or maybe there's reasoning behind it. In any case, I'll create a "main" app, rodmtech, at the top of the INSTALLED_APPS list, insuring it's first in line amongst all the apps.
mkdir -p rodmtech/static/css # replacing rodmtech as needed
touch rodmtech/__init__.py
python manage.py pygments_styles colorful >rodmtech/static/css/codehilite.css
Add the new rodmtech module to settings.INSTALLED_APPS, and take due notice of the Mezzanine notes on app ordering. Put the app in the list before any other mezzanine apps......it's first seen, first served for statics and templates.
INSTALLED_APPS = ( "rodmtech", "mezzanine_pagedown", "django.contrib.admin",
In case you're curious, Django looking in app static dirs isn't entirely automatic either. This feature is controlled by the STATICFILES_FINDERS setting. It's a good read along with Managing static files.
Great, so we've got a codehilite.css, but there's nothing automatic about having a new stylesheet pulled down. Delivered page content is all being produced by templates buried in Mezzanine apps. How do we get at them and start customizating?
What we need to do is
You can do that by fishing them out from
under site-packages manually, or the preferred way, use the Mezzanine
provided collecttemplates
function.
The collecttemplates function lets you be selective, but I'm going to pull them all. (Certainly for figuring out Mezzanine, grabbing all the templates is helpful.) Your choice here really. Alternately, you could just delete templates you don't touch and fall back to those found in the Mezzanine apps.
One argument for keeping most most of the templates in your space: in case a Mezzanine software upgrade changes Mezzanine templates that would break your customizations. The Mezz maintainers are pretty careful about not breaking things, but it can happen depending on how you customize.
python manage.py collecttemplates
That puts all of Mezzanine's templates in the templates dir under you project root dir. Be sure to read about how Mezzanine finds templates. In fact, read that whole page carefully if you plan on going deep with Mezzanine.
Django is finding those templates via settings.TEMPLATE_DIRS
. We
could also have put them in a templates dir under the
rodmrodmtechtech app and the app_directories template loader would
find them there. As is, collecttemplates insists on putting them at
the top level templates dir. Btw, mezzanine-project setup that
TEMPLATE_DIRS variable in settings.py for us. So be it.
Finally, finally, finally: add codehilite.css to the header section in the base template. While doing that, also add a custom.css to override bootstrap, mezzanine and pygments defaults. On top of all that, let's also add a custom font, from the google maintained collection.
In templates/base.html
, find the stylesheet link tags, which should
be just under a {% compress css %}
line. Add the custom.css file
below the bootstrap and mezzanine entries. The font callout must
be above/outside the compress tag; what's inside the "compress css"
will get concatanted into a single download file for production, i.e., with
DEBUG turned off. A link with an href will not work inside that compress block.
The result should look like
<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Ubuntu"> {% compress css %} <link rel="stylesheet" href="{% static "css/bootstrap.css" %}"> <link rel="stylesheet" href="{% static "css/mezzanine.css" %}"> <link rel="stylesheet" href="{% static "css/bootstrap-responsive.css" %}"> <link rel="stylesheet" href="{% static "css/codehilite.css" %}"> <link rel="stylesheet" href="{% static "css/custom.css" %}">
Then create rodmtech/static/css/custom.css and fill with
body { background-color: #ddd; font-family : 'Ubuntu', sans-serif; }
to match the font you imported.
Make sure your dev server is running, and create or refresh a page with markdown code rendered. You should get colored code highlighting, a grey background and a new font.
Again, the changeset for this section.
Let's get further into Mezzanine customization and really tweak some templates, i.e., specialize the homepage and blog listing page. Let's also get rid of bottom and left menus.
A small change first though. So the user doesn't have to choose
what they want to search for, remove the choice of Blog Posts, Pages,
and Everything; search will always be Everything. Mezzanine has
the [SEARCH_MODEL_CHOICES][30]
setting to control this feature.
SEARCH_MODEL_CHOICES = []
Next, remove bottom and left menus. In settings.py, add
PAGE_MENU_TEMPLATES = ( (1, "Top navigation bar", "pages/menus/dropdown.html"), )
underneath the SEARCH_MODEL_CHOICES
just added. However, doing this change only
removes menus from admin, not templates. Before we move on to those templates, it's worth
taking a look a the discussion near the commented PAGE_MENU_TEMPLATES
, approximately
line 44 of settings.py in the current release.
To remove from layout go to templates/base.html and remove
{% page_menu "pages/menus/tree.html" %}
and from down in the footer section remove
{% page_menu "pages/menus/footer.html" %}
With no left menu I like to give wider main content
space. The middle of my templates/body.html
change
<div class="span2 left">
to
<div class="span1 left">
and
<div class="span7 middle">
to
<div class="span8 middle">
Now for the home page. In urls.py look for HOMEPAGE AS AN EDITABLE
PAGE IN THE PAGE TREE
. Uncomment the line under the lengthy comment
url("^$", "mezzanine.pages.views.page", {"slug": "/"}, name="home"),
Then, under the HOMEPAGE AS STATIC TEMPLATE
docu-comment, # out the
url line.
#url("^$", direct_to_template, {"template": "index.html"}, name="home"),
This change hands the home page over to the mezzanine.pages package views.py
file, to the page
function. (You can look it up if you're interested,
site-packages/mezzanine/pages/views.py
line 42, atm.)
The key point is the home page will now come from the CMS, i.e.,
the Pages table in the database, specifically, the record with a /
as its slug value.
As soon as we save the urls.py change out, /
won't load, as that page is
not yet in the Pages table. Go to admin, add a page
and give it a /
url (in the Meta section), save it, and then try
loading http://localhost:8000
While we're mucking with pages in the admin, clean out pages you
don't want. For my site, I delete the Team, History, Blog, Gallery
and Legals pages from Pages, assuming you started with the
Mezzanine sample data. Remember, you can also start without sample data
using createdb --nodata
for a full custom build.
After you create that new home page, go to the page admin again to reorder, so the new home page is first in the list. Drag that page to first position in the tree of pages...the menu templates will use this order. (Click on the little triangles and drag...took me a minute to figure that out.)
While you're in admin, you might bring up the Site->Settings page and set some things, e.g., Tagline, Site Title, Twitter tokens and query, etc.
If you do setup twitter creds in the admin Settings section, run
python manage.py poll_twitter
to populate the tweet table. You'll want that in a cronjob in
production; see deploy/crontab
for an example.
On to that home page customization. What I want on the home page is the CMS content followed by a listing of recent blog posts. Also, I want the twitter feed on this page, but not every page on the site. Note also that the blog listing page has a right hand panel that will stay.
Mezzanine's CMS template rules will use
templates/pages/index.html
for the /
page from the Pages table. This template
is already in our tree from collecttemplates
.
From here on, the work is standard Django template hacking, not much
Mezzanine specific about it, except that Mezzanine gave us the
starting templates. Presently, in templates/base.html
there's a
<div class="span3 right">
that contains an optional login button and the twitter div. I'm going to move the contents
of that div block to
templates/pages/index.html
and in `templates/base.html replace it with a block
tag that will make the right panel optional.
In base.html, replace
<div class="span3 right">
with
<div class="{% block right_panel_span %}span1{% endblock %} right"> {% block right_panel %}{% endblock %}
Then, cut the remaining interior contents of that div, specifically
{% nevercache %} {% include "includes/user_panel.html" %} {% endnevercache %} <div class="panel"> {% block right_panel %} {% ifinstalled mezzanine.twitter %} {% include "twitter/tweets.html" %} {% endifinstalled %} {% endblock %} </div>
from base.html and paste it into the bottom of templates/pages/index.html
.
Above the just pasted div add
{% block right_panel_span %}span3{% endblock %}
Inside that same div find
{% block right_panel %}
and its matching
{% endblock %}
and pull them outside the former div contents. The bottom of
templates/pages/index.html
should look like
{% block right_panel_span %}span3{% endblock %} {% block right_panel %} {% nevercache %} {% include "includes/user_panel.html" %} {% endnevercache %} <div class="panel"> {% ifinstalled mezzanine.twitter %} {% include "twitter/tweets.html" %} {% endifinstalled %} </div> {% endblock %}
The right_panel_span
in base.html
lets an extending template specify the width of it's right panel in the base template.
Similarly, right_panel
allows the template to specify contents. If you're not familiar with html templates
and that all sounds mysterious, time to read up on Django template blocks and inheritance.
The net of all this is, if there's no right panel
block, base.html will
make that an empty span1 div, for spacing.
If the extending template does have a right panel, it can
adjust the width via right_panel_span and specify content with
right_panel, as previously mentioned.
One more thing: there are template tags new to templates/pages/index.html. At the top, under the "extends", add
{% load mezzanine_tags blog_tags i18n future %}
If you browse the site now, only the home page should have a right panel. Other content pages should not. But one of the reasons I wanted the right panel gone on non-home pages is more room for content. The base template still has a span8 div (was span7 before we took out the left menu) around the main block.
Make that center div configurable with a span10 by default, similar to right_panel_span. In base.html,
<div class="span8 middle">
changes to
<div class="{% block main_span %}span10{% endblock %} middle">
and in templates/pages/index.html, just above {% block main %} add
{% block main_span %}span8{% endblock %}
One thing left to do: if you try the top level blog page, it's likely
wacked. It's right panel needs fixing.
In templates/blog/blog_post_list.html
add
{% block main_span %}span8{% endblock %} {% block right_panel_span %}span3{% endblock %}
above {% block main %}
Good time to save, check visuals in a browser, and commit, if you're using scm.
Finally, let's add a recent post listing to the home page.
In templates/pages/index.html
right underneath the
block.super line
<hr/> {% block blog_recent_posts %} {% blog_recent_posts 5 as recent_posts %} {% if recent_posts %} <h3>{% trans "Recent Posts" %}</h3> <ul class="unstyled recent-posts"> {% for recent_post in recent_posts %} <li><a href="{{ recent_post.get_absolute_url }}">{{ recent_post.title }}</a></li> {% endfor %} </ul> {% endif %} {% endblock %}
Be sure to create a blog post to test.
The changeset for all that template hacking.
Next is one of the most powerful ways to customize Mezzanine: a new content page type. The new page will be for my documentation section, which is a tree of articles and how-tos on various subjects. I plan to have a top level "Docs" page, with a few subject area pages underneath it, say "Mezzanine", "Django", "Python", etc. Underneath these pages, I'll have "leaf node" articles of content. I want the Docs and subject pages to have a clickable table of contents, after the page content.
Note: I got the hang of extending page schemas by looking at the Mezzanine Gallery model and interface. Good reading.
Start by creating a new model for the page. I'm going to use
the rodmtech app created previously. (You could also create and use
a new app, with python manage.py createapp newapp
; if you do,
be sure to add it to INSTALLED_APPS
in settings.py.)
Create rodmtech/models.py and fill it with
from django.db import models from django.utils.translation import ugettext_lazy as _ from mezzanine.pages.models import Page, RichText class DocPage(Page, RichText): """ A doc tree page """ add_toc = models.BooleanField(_("Add TOC"), default=False, help_text=_("Include a list of child links")) class Meta: verbose_name = _("Doc Page") verbose_name_plural = _("Doc Pages")
As soon as this file is saved, the Django ORM will be out of sync with our
database. The site should work, but the admin won't, at least not
the Pages section of the admin, until we get things back in
sync. Would could use python manage.py syncdb
, but instead get
used to using South.
I highly recommend mastering South if you plan any Django projects that will evolve over time. Or, figure out a better way to do what South does. I'm not being facetious here, there are other apps, including some home grown solutions that work quite well. Here's a good article on schema change management.
The django-south incantation is
python manage.py schemamigration rodmtech --initial python manage.py migrate rodmtech
Now the admin doesn't know about our new DocToc page type. Create rodmtech/admin.py and fill with
from django.contrib import admin from mezzanine.pages.admin import PageAdmin from .models import DocPage admin.site.register(DocPage, PageAdmin)
For a good example of extending the admin for a richer page type,
see the galleries app in Mezzanine, i.e.,
site-packages/mezzanine/galleries/admin.py
.
After adding a new admin.py, you'll need to exit the dev server, ^C
out of manage.py runserver, and python manage.py runserver
again. (Now why does the
new rodmtech/admin.py require a restart? Afaict, Django looks for
app/admin.py files at startup, to build it's admin site
dynamically. If you change one that was already there, ok,
"runserver" sees that and reloads. But a whole new admin.py file?
Django doesn't catch that.)
Go back to the Pages section of the admin, refresh it if you're already there, and you should see "Doc Page" in the Add dropdown.
Time for the DocPage template. Remember the Mezzanine template
finding rules for models inherited from Page:
http://mezzanine.jupo.org/docs/content-architecture.html#page-templates. We'll
want a templates/pages/docpage.html
. I'm going to use a hierarchical
list template already in Mezzanine for my markup, namely
templates/pages/menus/footer_tree.html. My docpage.html looks like
{% extends "pages/page.html" %} {% load pages_tags mezzanine_tags %} {% block main %}{{ block.super }} {% editable page.docpage.content %} {{ page.docpage.content|richtext_filters|safe }} {% endeditable %} {% if page.docpage.add_toc %} <hr/> <div class="container"> {% page_menu "pages/menus/footer_tree.html" page %} </div> {% endif %} {% endblock %}
Now, the only thing missing are some DocPage pages. In the admin, create a new top level DocPage with the "Add TOC" checkbox checked. Add some additional pages underneath, and test your work.
And, the final changeset for this tutorial.
It this point, you should have built a site awfully close to the one you're browsing...at least as it was when this was written. All of the work described here is also in a Bitbucket Mercurial repo, rmorison/rodmtech_net.
Thanks for following along. Questions, comments, complaints are welcome.
Comments
This looks to be spot on and is super thorough, great work!
Link / ReplyI really like and appreciate your blog.Really looking forward to read more. Will read on...
Link / ReplyThanks a lot, you took me through a nice journey!
Link / ReplyBTW, I cannot get rid of the deprecation warning "You haven't defined the ALLOWED_HOSTS settings..."
Link / ReplyCheck settings.py carefully. Works for me, fwiw:
Link / ReplyALLOWED_HOSTS = ('localhost', '.local')
OMG this is fantastic. Thanks!
Link / ReplyHi Rod,
Link / ReplyThanks for such an exhaustive guide on customizing Mezzanine. I've been trying to get my own blog/site up and running in Mezzanine with my limited knowledge of Django/Python. Currently following your tutorial. However, I'm stuck.
First off, I cannot figure out how to get any links such as "Blog", "Docs", etc. on the top navigation bar of my page. It only has the default "Home" and "Search".
Also, I cannot see any recent blog posts on my Home page.I followed everything as written here up to creating the listing on the index.html. Then I created two blog posts but they failed to show up.
I seem to have hit a brick wall with these issues. Any help is greatly appreciated.
Great tutorial. Many thanks. Hope to see more Mez tuts from you.
Link / ReplyHello, I have am making a mezzanine website using pythonanywhere.com and am having trouble with changing the theme.
Link / ReplyI seem to have followed every single step of every single guide I can find and nothing is working, I don't understand why it will not work. I have a new app called themeapp which has directory templates/ which has a duplicate base.html and index.html and another directory templates/includes/ which has a duplicate footer_scripts.html file, the other directory it has is static/css/ which has a file bootstrap-theme-cosmo.css which is a css file from http://bootswatch.com/cosmo/bootstrap.css and the file codehilite.css which I got from running "python manage.py pygments_styles colorful >themeapp/static/css/codehilite.css". In settings.py for my actual project I have added "themeapp", and "mezzanine_pagedown", to my installed apps list. In base.html in both copies I have added <link rel="stylesheet" href="{% static "css/codehilite.css" %}"> and <link rel="stylesheet" href="{% static "css/bootstrap-theme-cosmo.css" %}"> after the 3 other lines which link to css/bootstrap.css, css/mezzanine.css, css/bootstrap-theme.css. I installed Pygments using "pip install mezzanine-pagedown Pygments" and added the necessary stuff to settings.py and urls.py which you can see in this guide http://rodmtech.net/docs/mezzanine/a-mezzanine-tutorial-take-2/#part-1-install-through-pagedown-setup, I've also tried other two other guides http://rosslaird.com/blog/customizing-mezzanine/ and http://bitofpixels.com/blog/mezzatheming-creating-mezzanine-themes-part-1-basehtml/. Any ideas at all why on earth nothing will work?
From http://teamjjsl.pythonanywhere.com/ it looks like you got the basics working. Still having trouble? I'd need more details to help.
Link / ReplyThis tutorial covers a lot of ground. I am using Mezzanine too. Keep the good work !!
Link / ReplyGreat work! Thanks a lot!
Link / ReplyI don't think that RICHTEXT_FILTER_LEVEL = 3 it's a good practices.
Link / Replyhave you an other way ?
Hi, I've followed your tutorial and everything works great!
Link / ReplyI wanted to ask your advice on rendering math on your mezzanine site and how to do it.
For example, if I wanted to display $\frac{\gamma}{2}$, are there some resources I can attend to?
I don't know, is my honest answer. But, I bet there's a way...in fact, I'm sure there is, if you're will to write some code and integrate an equation renderer.
Link / ReplyI got ImproperlyConfigured
Link / ReplyCould not import the value of settings.RICHTEXT_WIDGET_CLASS: mezzanine_pagedown.widgets.PageDownWidget.
This is when I added PAGEDOWN SETTINGS in my settings.py file
How can I fix it?
Thanks
Comment awaiting approval 7 years, 11 months ago
New Comment