In the first and second Mezzanine tutorials we explored ways of getting and started and building a project with Mezzanine. In this tutorial we’ll examine how to personalize the administration interface, how to tweak a few underlying settings, and how to build a customized theme. But before we travel too far down that track, it’s important to reinforce that these tutorials are intended to be exercises in intentional, self-reflective web development. These days most of us spend a great deal of time participating in online distractions and digital meandering. We have become less focused and mindful than we might be, and we have surrendered much of our attentional autonomy to the persistent keening of technology. Compare, for example, our online habits to the activities for which 200,000 years of evolution have prepared us. The online wanderer sits, shifts focus every few seconds, constantly responds to new stimuli, rides a constant wave of subliminal anxiety rooted in the dread of being left behind. The ancestral nomad of our evolutionary past stands, holds a fierce and singular focus (on an animal, or a stretch of landscape, or a work of craft), remains in the present moment of engagement and experience, and rides a wave of mindful calm rooted in a sense of belonging to place and to people. We tend to look upon those ancient peoples as more primitive than ourselves. But is the modern, distracted, disconnected self more advanced than the ancients? Is the complexity of our technology a sufficient justification for our belief in our superiority? Or, instead, have we lost something fundamental, and do ancient approaches offer important wisdom?
I believe that they do, and that the central challenge of our age involves the integration of practices both ancient and modern. And so, as I approach a subject such as web development, my intent is to do so with a clear and singular focus, to choose tools and approaches with purpose and mindfulness, to create my digital landscape as an ancient nomad traversed a range of mountains: thoughtful, careful, attentive. The heart of web development lies not in the code but rather in the way that the process of development helps us understand ourselves, our connections to one another, and our purposes in the world. Without these considerations, web development is empty, devoid of meaning. It’s easy to forget this, as the relentlessness of machine culture drives us onward. But onward is not always the same as forward. And that’s the key question: how do we carry forward our humanity in a world increasingly mediated by mindless data and hidden code?
One of the ways we can do this is by taking control, as much as we can, of our data and our code. This is one of the aspects of Mezzanine that I like very much: I can see the code, and (most of the time) I can understand what the code will do. That’s not an accident: readability is a central principle in the programming language (Python) and web framework (Django) upon which Mezzanine is built. As a result, more than any of the other content management systems I’ve used (Movable Type, Wordpress, Drupal, Radiant), the Mezzanine code base is easy to read and to use. It’s also easy to modify, test, and deploy. And, when I break things, which is often, I can usually find my errors in the code and fix them without too much fuss. However, before we start breaking things, let’s first setup the Mezzanine interface so that we can start mucking around. (If you have not yet consulted the first and second Mezzanine tutorials, now would be a good time to do so.)

The Administration Area

Once you login to Mezzanine you will see the administration interface, which offers quite a number of tools and settings. Most of these are intuitive and straightforward. For example, you probably don’t need me to tell you how to add a page (well, if you do, just click + Add, beside Pages). The administration interface is simple enough that you can figure out most of what you need to know just by clicking around. This is one indicator of a well-designed interface: it facilitates exploration. As you click around, pay particular attention to the Settings area, which you can access from the dashboard in two ways: within the Site drop-down menu at the top, or through theSettings link in the list of Site pages, beneath the Content pages. (In Mezzanine, there are usually two ways to access a given link. The top navigation is the definitive path and will always provide quick access to any section of your site regardless of where you are.) On the Settings page, complete as many of the entries as apply to you. If you haven’t already setup accounts at bit.lyDisqusTwitter, or Google Analytics, you may wish to think about doing so at least for Disqus, which provides the commenting function for Mezzanine’s blogging platform.
Also be sure to review the contact page settings (available from the Pages area, in the dashboard). Note that the contact page is not special in any way; it’s a regular Mezzanine page (a form page) that has been customized for a specific purpose. You might wish to create several contact forms for different purposes or sections of your site, or — if your needs are straightforward — the default form may be all that you need. Either way, it’s useful to remember that this page (as with all the other pages Mezzanine provides by default) can be customized, adapted, or removed as you prefer. To see how pages are implemented and organized, look at the Pages area, which shows all of Mezzanine’s page types in a hierarchical tree that represents the navigational structure of your site.
The default contact page settings should be straightforward and the descriptive text should provide all the guidance you need. However, if you are developing Mezzanine from a local setup (as you probably are, if you followed the first and second tutorials), your local machine may not be configured to send emails — in which case you will not be able to actually send (or receive) email messages from Mezzanine using the basic setup. But there are also several good reasons not to use your local machine for sending and receiving email messages (those reasons include security, configuration, and maintenance, among others). Besides, when you deploy Mezzanine from your local machine to a server online, your local mail settings will no longer apply. So, as a result of these considerations, Mezzanine and Django provide a means of using various online services — such as Gmail and Amazon Simple Email Service — to handle email. I will show the setup for both Gmail and Amazon Simple Email Service (SES); but I use SES for my email configuration, so I’ll show that method first.

Customizing Email Settings for Amazon SES

Open up a terminal, activate your virtual environment (using the workon command), and navigate to the root directory of your project. Then install the Django-SESpackage:
pip install django-ses
Next, open up your settings.py file. We looked briefly at this file in the secondtutorial; but now we are going to do a bit more. We’re going to add a new section to the end of the file. If you prefer, you can add the new section to local_settings.pyinstead of settings.py. Both will work, and there is no requirement to use one or the other for the kinds of things we are about to do. Different developers use different methodologies, depending on their needs and their situations. Some developers prefer using local_settings.py as a more modular and portable approach; others usesettings.py as a more global strategy. Whichever approach you choose, add the relevant code:
##########
# EMAIL # 
##########

EMAIL_BACKEND = 'django_ses.SESBackend' 
AWS_ACCESS_KEY_ID = 'LONGSTRINGHERE' 
AWS_SECRET_ACCESS_KEY = 'EVENLONGERSTRINGHERE' 
EMAIL_HOST_USER = 'MYAMAZONSESUSER' 
EMAIL_USE_TLS = True 
EMAIL_HOST = 'email-smtp.us-east-1.amazonaws.com' # The next two lines may not be required; try without them first. 
DEFAULT_FROM_EMAIL = SERVER_EMAIL = 'you@yourdomain.com' 
FORMS_DISABLE_SEND_FROM_EMAIL_FIELD = True # The next line is not required, but may be useful. 
SEND_BROKEN_LINK_EMAILS = True
The last line causes a notification to be sent (to administrative users) whenever an internal link results in a 404 (page not found) error. I found this to be highly useful when I first started with Mezzanine, as I migrated postings from various other content management systems and needed to update quite a few internal links. But only the first six lines of the above snippet should be required. However, depending on your server setup (which, as I indicated above, may change as you deploy your project online), lines eight and nine may help.

Customizing Email Settings for Gmail or Other Services

One advantage of using Gmail (or any other service for which you know the IMAP and SMTP settings) is that no extra packages are required. Configuration simply involves adding the relevant settings in settings.py (or local_settings.py, as noted in the previous section), like so:
EMAIL_HOST_USER = 'YOURGMAILADDRESS'
 EMAIL_USE_TLS = True 
 EMAIL_HOST = 'smtp.gmail.com'
 EMAIL_HOST_PASSWORD = 'yourpassword'
 EMAIL_PORT = 587
 
Now that you have personalized the administrative interface and have customized your email settings, you’re almost done with Mezzanine’s default infrastructure. However, while you’re in the settings.py file you may also wish to explore the following settings (details are in the file itself):
BLOG_USE_FEATURED_IMAGE = True 
ACCOUNTS_ENABLED = True
One final setting, which is not in the settings.py file by default, is this:
PAGES_MENU_SHOW_ALL = False
This setting controls the context for menu items. By default, Mezzanine’s main menu (on the left, in a default setup) shows all the menu items. Personally, I prefer the menu to show entries that are relevant to the current context. For example, when readers visit my courses pages, I want the menu to show courses only. This provides a way for me to create (navigationally self-contained) sections on the site simply by modifying the menu. To activate this type of functionality, you can add the above snippet to the end of the settings.py file.
And, with that, you’ve already begun to customize Mezzanine.

Customizing the Theme

Modifying the look and feel of a site is typically the most sought-after and time-intensive aspect of web development. More than any other single factor, the aesthetic of a site embodies who we are and what we want to share. In some ways, the look and feel of a site is as important as the content. We recognize this corollary in speech —how you say something is as important as what you say — but we tend to overlook the importance of tone and mood on our websites. Or, we become frustrated by the difficulties involved in adapting web frameworks to our own design principles and preferences (if you’ve ever tried theme development in Drupal, you know what I’m talking about). We give up, or make a few small changes, or accept the defaults provided by a given system created by someone else. We don’t do this with our clothing, or our hairstyles, or our food. Why should we accept the defaults in our web presence, which is just as much a reflection of our inner nature as anything else we share?
Thankfully, Mezzanine provides a simple and elegant way to adapt and customize the look and feel of your site. Mezzanine utilizes a system of templates, which are built upon the Django template engine and which provide a robust methodology for tweaking just about everything you might want to include on your site. These templates are installed, by default, in the /core and associated directories of Mezzanine's source code: in other words, the templates do not appear in your project (they used to, in previous versions of Mezzanine). The default templates are created automatically by Mezzanine whenever you start a project, and these templates evolve along with Mezzanine. New versions of Mezzanine typically require adjustments to various templates. So, although you can modify the default templates, doing so might create problems with upgrading. Instead, the recommended method is to create a self-contained theme directory — what Django calls an application (or app).
Here’s how to get started: from the root directory of your project, issue the following command (replace yourapp with whatever name you choose for your theme):
django-admin.py startapp yourapp
This command will create a directory, with your app name, and will populate the directory with several files. For theme development in Mezzanine, you only need one of those files: __init__.py. You can delete the others or leave them as they are. Next, you will need to create directories to hold your templates, CSS files, images, and javascripts. Strictly speaking, only the template directory is required; but let’s assume that you want to experiment with extensive theme modification, and that you want to make the theme portable (i.e. adaptable to other Mezzanine projects). In that case, the other directories will be required. Here are the relevant commands (shown in the simplest manner for those who are not expert on the command line: if you are a command-line junkie, you will know how to enter these commands more efficiently):
cd yourapp #enter the app directory 
mkdir static 
cd static 
mkdir css img js 
cd .. 
mkdir templates 
cd templates 
mkdir includes
The above layout mirrors (much of) the default directory structure of Mezzanine. The structure you create should have the same layout and use the same file names as what Mezzanine provides through its own source code. How do you make sure you do this correctly? Create a test project. Here's what I do:
cd #to return to my home directory
mezzanine-project test-project #call it whatever you like
cd test-project
python manage.py collecttemplates
The final command above will collect all of the default templates (44, as of this writing) and place them in the /templates directory of your test project. Sweet. Now you can browse through the templates, copy those you wish to modify into the/templates directory of your theme app, and hack away. This mirroring of files and directories is the basis of theme development in Mezzanine: our custom theme files will mirror Mezzanine’s defaults but will be loaded first (more on that later), and by virtue of being loaded first will cause the default files to be ignored. This bears repeating: once the setup of an app is complete, any custom files in that app will take precedence over default files. In turn, this suggests a basic working rule: only change the files that you need to change. Do not, for example, copy all of the default templates into your newly-created app. Instead, copy only the templates you need. And, for most people, that amounts to three template files:
/templates/base.html #The base layout 
/templates/index.html #The home page layout 
/templates/includes/footerscripts.html #Javascripts
Copy these files from your Mezzanine test project into the app you have just created, taking care to place the files in locations that precisely match where they are in the directory structure of the test project. These four files control the basic layout of the site, the layout of the home page (consult the file urls.py, in the root directory of your project, for more home page options), and the inclusion of scripts at the bottom of each page (typically, this is where Javascripts are placed).
These files are easy to read and to adjust. For example, you can tell — even without knowing anything about programming — that the following code checks to see if there is a site tagline (set in the administration interface), then outputs that tagline at the specified location on the page:
{% if settings.SITE_TAGLINE %}<p class="tagline">{{ settings.SITE_TAGLINE }}</p>{% endif %}
Most of the template entries are like this: you may not know how to write this type of code, but it’s straightforward to read and easy to copy (or remove). As you progress in your theme development, you may find that you wish to modify more templates than the ones shown above. If so, just copy them over.

Modifying the CSS

If you want to modify the CSS (and you probably will), there are four basic approaches. Each offers distinct advantages and challenges.

Method One: Use Bootswatch

You can download and use themes from Bootswatch. All you need to do is download the files and link to them in your template files (typically, these links will be inbase.html and index.html). This is a simple, effective, no-fuss approach. On the other hand, you will be limited to the themes that Bootswatch provides (unless you add one or more of the strategies below).

Method Two: Customize Bootstrap

If you enjoy working with CSS files (and with less files), you can modify the Bootstrap theme directly. Simply copy the bootstrap.css file from your project’s /static/cssdirectory (placing it inside the /static/css folder of your app), or download Bootstrapfrom Github. If you work with Git (the subject of a future tutorial), you can clone the Bootstrap directory into your app. This way, you can stay current with Bootstrap development but also hack away as much as you like. Bootstrap is extensivelydocumented, so if you want to adapt the basic scaffolding of your site, or the core elements of your pages, or customize your implementation in any way, you can do so. Because the default Mezzanine theme is built with Bootstrap, you will see various Bootstrap classes already embedded in the default page templates. Simply adapt, remove, or augment these as needed to create your own aesthetic.
(This is the approach that I use, and for which I maintain a public repository on Github).

Method Three: Provide a Custom CSS file

One interesting feature of CSS is that the last rule is applied. In other words, if you have two entries which both apply to the same element, the entry which appears last (in the file, or in a file loaded subsequently) will be used. So, one method of customizing Bootstrap is to create a custom CSS file that lists only the elements you want to change. Then, in the template files which call the CSS (base.html andindex.html, usually), list the custom file after the Bootstrap CSS file. Like this:
 rel="stylesheet" href="{{ STATIC_URL }}css/bootstrap.css">  rel="stylesheet" href="{{ STATIC_URL }}css/custom.css">
With the above setup, styles listed in bootstrap.css that have a matching entry incustom.css will be ignored. Your customized styles will be applied. This method is perhaps a bit inelegant, and it violates the basic Python principle of Don’t Repeat Yourself; but it works, and for those who do not wish to wade too deeply into CSS, it might be a good compromise.

Method Four: Craft Your Own CSS

You can remove Mezzanine’s default CSS altogether and replace it with your own. All that’s required is to make sure that the links in base.html and index.html point to the relevant CSS files. Obviously this approach is more involved and requires a good working knowledge of CSS. However, it is also the approach most likely to deliver deep learning. If you choose this approach, make sure that you stay current with CSS trends and developments (A List Apart is an excellent place to start).

Activating the Theme

You’re going to want to look at your theme in a browser as you tweak and adapt things. And, of course, you’re going to want Mezzanine to use your templates instead of the defaults. This requires that you activate your theme, which is accomplished by placing the name of the app in your settings.py file, at the top of the list ofINSTALLED_APPS. Like this:
INSTALLED_APPS = ( 
 "yourapp", 
 "django.contrib.admin", 
 "django.contrib.auth", 
 ... # more apps listed
That’s all there is to it (don’t forget the comma). Your templates will be loaded and the results shown in your browser. Once your theme (or app) is installed and recognized by the server, any changes that you make to your templates will be noticed and rendered by the server. There is no need to restart a running server when you are modifying templates; you just need to save the file and refresh the page to see new results. Note that this approach will work provided you deploy the built-in development server (activated by the command python manage.py runserver). Once you launch your site into a live environment on the web (to be covered in a later tutorial), you will use Apache or another web server to serve the files. In that scenario, the rules will change; the reloading of files will be handled differently by various web servers (and, usually, will not be automatic).