A Mezzanine Tutorial, Take 2

Contents


Introduction: Greetings Mezzaniners,

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.

Part 1: Install Through Pagedown Setup

Preliminaries: Get Ready for Dev

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

Get Mezzanine and Start the Project

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'

Initialize the DB and Test

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.

Pagedown, a Mezzanine Markdown Package

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.

Part 2: Add Pygments Styles, a Custom Font and Background

Generate Pygments css

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:

  1. add a STATICFILES_DIRS list to settings, with a path to the top level of user supplied static files, or

  2. 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

  1. make copies of the Mezaanine templates,
  2. customize then, and
  3. make sure Django finds our copies before the default versions.

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.

Part 3: Template Layout Customization

Design Plan

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 = []

Remove Left and Bottom Menus

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">

Make Home Page CMS Editable

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.

Customize Base and Page Templates

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.

Part 4: Creating New Page Types

Design Plan

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.

Creating the Model

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")

Migrating the Database

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

Extend the Admin Site

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.

A Custom Template for DocPages

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.

That's a Wrap

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

  • Josh Cartmell 9 years, 8 months ago

    This looks to be spot on and is super thorough, great work!

    Link / Reply
    • TenorePn 8 years, 6 months ago

      I really like and appreciate your blog.Really looking forward to read more. Will read on...

      Link / Reply
  • Mario Osorio 9 years, 8 months ago

    Thanks a lot, you took me through a nice journey!

    Link / Reply
    • Mario Osorio 9 years, 8 months ago

      BTW, I cannot get rid of the deprecation warning "You haven't defined the ALLOWED_HOSTS settings..."

      Link / Reply
      • Rod Morison 9 years, 8 months ago

        Check settings.py carefully. Works for me, fwiw:

        ALLOWED_HOSTS = ('localhost', '.local')

        Link / Reply
  • Matt 9 years, 8 months ago

    OMG this is fantastic. Thanks!

    Link / Reply
  • Ritwik Roy 9 years, 6 months ago

    Hi Rod,
    Thanks 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.

    Link / Reply
  • Markos 9 years, 4 months ago

    Great tutorial. Many thanks. Hope to see more Mez tuts from you.

    Link / Reply
  • Joe 9 years, 2 months ago

    Hello, I have am making a mezzanine website using pythonanywhere.com and am having trouble with changing the theme.

    I 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?

    Link / Reply
    • Rod Morison 9 years, 2 months ago

      From http://teamjjsl.pythonanywhere.com/ it looks like you got the basics working. Still having trouble? I'd need more details to help.

      Link / Reply
  • Mayur Rokade 9 years, 2 months ago

    This tutorial covers a lot of ground. I am using Mezzanine too. Keep the good work !!

    Link / Reply
  • Inti 9 years ago

    Great work! Thanks a lot!

    Link / Reply
  • herison 8 years, 11 months ago

    I don't think that RICHTEXT_FILTER_LEVEL = 3 it's a good practices.
    have you an other way ?

    Link / Reply
  • Andy Koh 8 years, 10 months ago

    Hi, I've followed your tutorial and everything works great!

    I 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?

    Link / Reply
    • Rod Morison 8 years, 9 months ago

      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 / Reply
  • Michael 8 years, 9 months ago

    I got ImproperlyConfigured
    Could 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

    Link / Reply
  • Comment awaiting approval 7 years, 11 months ago

New Comment

required
required (not published)
optional