The DjangoCMS render_model_block

In short: We have the situation where we need to load rows of data from a django app model into a Django plugin template.

Example: We enter job openings in Django admin in an app such as aldryn-jobs. Then we want to display these job openings in a DjangoCMS plugin, such as latest job openings. Now, we would like to have Frontend Editing not only on the latest job openings plugin where we can configure stuff like the number of job openings to show but we would also like to have Frontend Editing for each of the job openings which are listed by the plugin.

Enters render_model_block:

<div class="job-box row">
    {% if jobs %}
        {% for job in jobs %}
          {% render_model_block job %}
              {% include 'job_plugin/plugins/include/job.html' %}
          {% endrender_model_block %}
        {% endfor %}
    {% else %}
        <!-- No jobs found -->
    {% endif %}
</div>

and the job.html:

<div class="job-wrapper">
    <a href="{{ instance.link }}">
        <div class="job">
            <h1 class="title">{{ instance.title }}</h1>
            <h2 class="subtitle">{{ instance.subtitle }}</h2>
            <p class="text">{{ instance.job_text }}</p>
        </div>
    </a>
</div>

Inside the render_model_block job is now instance.

DjangoCMS – CkEditor Configuration

CKEDITOR_SETTINGS = {
    'language': '{{ language }}',
    'toolbar': 'CUSTOM',
    # http://ckeditor.com/apps/ckeditor/4.4.0/samples/plugins/toolbar/toolbar.html
    'toolbar_CUSTOM': [
        ['Undo', 'Redo'],
        ['cmsplugins', '-', 'ShowBlocks'],
        ['Format', 'Styles', 'FontSize'],
        ['TextColor', 'BGColor', '-', 'PasteText', 'PasteFromWord', 'RemoveFormat'],
        ['Maximize', ''],
        '/',
        ['Bold', 'Italic', 'Underline', '-', 'Subscript', 'Superscript', '-', ],
        ['JustifyLeft', 'JustifyCenter', 'JustifyRight'],
        ['Link', 'Unlink'],
        ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Table'],
        ['Source']
    ],
    'skin': 'moono',
    'toolbarCanCollapse': False,
    'stylesSet': [
        {'name': 'Intro', 'element': 'span', 'attributes': {'class': 'intro-text'}}
    ],
    'fontSize_sizes': '0.5em;0.6em;0.7em;0.8em;0.9em;1.1em;1em;1.2em;1.3em;1.4em;1.5em;1.6em;2em;2.3em;2.5em;3em;',
    # 'extraPlugins': 'cmsplugins' // auto-included in 'TextPlugin', doesn' work in 'HtmlField'
    # see https://github.com/divio/djangocms-text-ckeditor/issues/317
}

Aldryn – Adapting Add-On to a project’s requirements

How often does it happen: There is a perfect pre-made add-on / plugin available for your CMS however the web project has a specific requirement that this add-on doesnt quite meet.

The patching begins.

On Aldryn, currently there are some drawbacks with Add-On development:

  • Adding an add-on requires rebuilding the docker container and this is slow.
  • There is no way to “install” an existing add-on and then patch it, and include the changed code in the source code repository.
  • Forks of existing add-ons need to be public on github so that the deploy process can install them (with pip). That doesn’t fly for project-specific patched versions.

I have tested a way to make this patching process cleaner. Below I try to sketch it out.

It requires a virtualenv setup and the command line aldryn client.

  1. Now, a public add-on repo (like aldryn-events) can be duplicated (git clone & git rm remote origin, then add your own repo’s remote)
  2. This private ‘fork’ can now be cloned into Aldryn’s project /addons-dev.
  3. Install it by adding -e /absolute/path/to/Projects/project-name/addons-dev/project-name-aldryn-events to requirements.in and pip install -r requirements.in
  4. Add the app name to settings.py and maybe run ./manage.py migrate
  5. Now you can patch, test and commit to your own private repo.
  6. Once you are happy with the patched version you can go into /addons-dev/project-aldryn-events and aldryn addons register "Project Name Aldryn Events" project-aldryn-events and aldryn addons upload. From the Aldryn web console you can then install the new add-on into the system (in the virtualenv, do git pull and pip install -r requirements.in)
  7. For the installed addons to work the addons in addons-dev have to be renamed (I rename them to addon-name-dev).

Note: For easier upgrading it is even possible to diff the patched version with the original open source version:

  • add the original version as remote: `git remote add upstream git-url`
  • Then `git checkout -b original upstream/master`
  • Now a pull request between your patched branch and `original` can be made on github

Git Branch Management with Aldryn

For some reason on the Aldryn git, it’s not so easy to share branches between different collaborators. One doesn’t see the other’s branches.

Here is how to load the branch of a collaborator:

# See all branches
git fetch origin
git branch -r
# 'activate' a branch
git branch --track  origin/

Here is how to sync branches with rebase:

# in a feature branch:
# Do as if work in the feature branch had started on top of the changes that have been made in the meantime to the develop branch:
git checkout develop
git pull
git checkout feature-branch
git rebase develop
# If there are conflicts you can either resolve them manually or in order to fix quickly (for example the updates to config files by Aldryn) you can use:
git checkout --ours foo/bar.js
# to take the feature-branch version use --theirs, for the develop branch version use --ours

Having your Aldryn project in Github allows for easy diffs between branches and a structured workflow with multiple collaborators:

# add a github repository as second remote
git remote add github git@github.com:what-digital/repo-name.git
# now collaborators can push with
git push github

Beyond async & defer: Javascript Dependency Handling with system.js

Today, after another depressing look at Google Pagespeed results, I went looking for a hands-down dead simple approach to javascript dependency management in the browser.

I looked at require.js, webpack and browserify. All three of them seemed to be heavily focussed on nodejs and it looked like it wouldn’t work out of the box in three simple steps.

Somehow I came across system.js and decided to give it a try. It looked clean and simple. Three hours later, here is what I found out about it.

Getting system.js is super easy (if you have bower):

bower install system.js --save

Loading (here into a Django template):

<script src="{% static 'vendor/system.js/dist/system.js' %}"></script>

Configuration

    <script>
        System.config({
                        defaultJSExtensions: "js",
            // set our baseURL reference path
            baseURL: '{% static '' %}',
            map: {
                "jquery": 'js/libs/jquery.min.js',
                "bootstrap": 'js/libs/bootstrap.min.js',
                "scrollmagic": 'vendor/scrollmagic/scrollmagic/minified/ScrollMagic.min.js'
            },
                      meta: {
                bootstrap: {
                                        format: 'global',
                    deps: ['jquery']
                }
            }
        });
    </script>

Inline Javascript:

Promise.all([
   // Asynchronously loads the javascript files if not already loaded on the current page
   // alternatively maybe use the bundle option: https://github.com/systemjs/systemjs/blob/master/docs/config-api.md#bundle
     // or see the dependency solution below 
   System.import('jquery'),
   System.import('scrollmagic'),
]).then(function (modules) {
   var jQuery = modules[0];
   var ScrollMagic = modules[1];

   jQuery(document).ready(function () {

      var controller = new ScrollMagic.Controller();
   });

The deps part in the meta configuration makes sure that bootstrap is working correctly (first, jQuery needs to be loaded, then the bootstrap jquery plugins need to be loaded by jQuery).

This allows for a one-import statement even for complex dependencies:

 // load jquery & bootstrap & execute them
Promise.all([
        System.import('bootstrap')
]).then(function () {
        jQuery(document).ready(function () {
                jQuery('#accordion').collapse();
});

Sometimes one wants to include javascript files that are written to be compatible with different formats (amd, es6, …). In that case system.js sometimes can’t figure out the right format and tries to import assumed dependencies it ‘finds’ inside those files. To prevent that, define the file’s format in the meta configuration like this:

meta: {
    bootstrap: {
            deps: ['jquery']
    },
    isotope: {
            format: 'global', // or if supported: format: 'amd',
            deps: ['jquery']
    },
}

Local Development with Aldryn/Docker

So far, local development with Docker for Aldryn projects has been a pain.

  • Docker features a very slow build (5 to 10 minutes)
  • super slow page load / server response time. This might be acceptable for software development, but not for web development where much is try & refresh.
  • Docker also brought my 2015 8GB RAM 2.2GHz Intel i7 MacBook Air to its knees. It’s just not cool, wenn the fan is constantly on. No pun intended.
  • Also I found debugging through Docker with Pycharm to be a major pain, it was slow and eventually completely stopped working. Pycharm kept adding Docker containers and they ate up my system.
  • Last but not least not all of the syntax / symbol checks work in Pycharm through Docker

I wanted to be able to develop locally just like it works great for a non-Aldryn DjangoCMS project. Here is how I made Aldryn work for me:

Prerequisite: Fully working Aldryn Local / Docker (Docker Toolbox on Mac OS X) setup.

In docker-compose.yml make the db container expose its postgres port to the host system:

db:
 ports:
  - "5432:5432"
 image: postgres:9.4
 volumes:
  - ".:/app:rw"

Create a virtualenv

cd ~/Projects/projectname
mkvirtualenv projectname --python /usr/bin/python2.7

In postactivate of your new virtualenv add the following env vars. They are needed by the aldryn-django package.

export DJANGO_SETTINGS_MODULE=settings
export NGINX_CONF_PATH=/dummy/path/
export BASE_DIR=/your-path/Projects/projectname
# if your apps live in a separate folder add the path to PYTHONPATH
# In Pycharm, mark the folder that contains the apps as Source so that Django finds them.
export PYTHONPATH="{PYTHONPATH}:${BASE_DIR}/src"
export DEBUG=true
# make sure the IP is the one of your docker-machine (look it up with: docker-machine ip)
export DATABASE_URL="postgres://postgres@192.168.99.100:5432/db"
export CACHE_URL="locmem://"

then source postactivate or deactivate and workon projectname

Also there are a couple of packages that are required by Aldryn in a version that is not on pypi. Any of the following stuff might help

--extra-index-url https://devpi.divio.ch/divio/all/+simple/
# unresolved dependency if pip install manually
boto
easy-thumbnails

The requirements can now be installed:

pip install -r requirements.in

now, manage.py should already work.

Before the website works locally, you have to add the local static and media urls to urls.py:

if settings.DEBUG:
   from django.conf.urls.static import static
   urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
   urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

On the command line a ./manage.py runserver will now be able to start the Aldryn project.

In Pycharm a normal Django Server interpreter can now be set up (I serve env vars through a .env file).

A Pycharm Note: If your apps live in a separate folder (such as src) you need to flag this folder as additional source in pycharm (have a look at the project preferences).

Let me know whether you like this approach or if there is potential for optimisation.

Typical WordPress ACF Project Setup

This is a typical WordPress / Advanced Custom Fields Setup that we use for our WordPress projects. It’s a clean, simple but low-level approach, and I am very likely to automate most of it using ansible anytime soon.

WordPress Project

Access to Stage and Production Servers

  • Server names are www. and stage. plus the main domain
  • All servers are accessible through SSH keys. Ask Mario at mario@what.digital if you need one.
  • Access to servers is SSH / SFTP (key-based) only: sftp|ssh -i ./example.pem admin@stage.example.com
  • Username is always admin unless stated otherwise

Initial Deployment

  1. Set up the LAMPW stack according to this blog post
  2. Log into the system with SSH
  3. Generate a Github deploy key with ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
  4. cat the public key in ~/.ssh and copy & paste it into the github’s repository’s settings -> deploy keys as read-only key. Give a speaking name (i.e. domain.com stage)
  5. Create a directory outside web root (preferably /var/git/) and git clone ssh/https repo url into it.
  6. Go to the theme folder and do a git submodule update --init --recursive
  7. in the theme folder do php composer.phar install
  8. Rename the example-.env file to .env
  9. Create a symlink (ln -s /absolute/path/to/origin /absolute/path/to/dest) for each theme / plugin in the repository to the according WP folders (themes / plugins)
  10. if the server doesn’t have npm: https://gist.github.com/isaacs/579814
  11. go to the tools folder in the wp theme folder and run npm install and then ./npm_modules/gulp/bin/gulp.js styles to translate SASS into CSS

Deployment

  1. Go to /var/git
  2. git pull
  3. If you changed ACF fields, then you have to go to WP Admin web inteface at /wp-admin then Custom Fields then Syncable Fields and sync all the syncable field groups.
  4. go to the tools folder and run ./npm_modules/gulp/bin/gulp.js styles to translate SASS into CSS

Notes:
* Never change the branch on stage or production servers.
* Never change code on the stage or production servers directly
* Never change the ACF field setup on the stage or production servers directly
* The master branch is read-only – You cannot deploy to production. Please create a Pull Request into master and follow the workflow as described below.

Deployment Workflow

  1. Client submits change request
  2. what.digital creates a technical brief including ETA
  3. Developer creates a new feature / change request branch (set speaking branch name!)
  4. Developer creates a PR from his feature / change request branch into stage branch
  5. what.digital accepts the PR
  6. Developer deploys to stage
  7. what.digital tests on stage and creates a PR from stage into master
  8. Mario at mario@what.digital accepts the PR and deploys to master

Database and Media Import / Export

Export

sudo mysqldump -p db-name > db-name.sql
sudo tar -cvzf uploads.tar.gz /var/www/wordpress/wp-content/uploads

Move these files to another system using SFTP

sftp -i cert-name.pem admin@domain.com
get db-name.sql uploads.tar.gz

Import this data on the target system

sudo mysql -u root -p db-name < db-name.sql
sudo tar -zxvf uploads.tar.gz
sudo mv uploads /var/www/wordpress/wp-content/
sudo chown www-data:www-data -R /var/www/wordpress/

Then go to domain.com/phpmyadmin and change the entries siteurl and home in the table wp-options to the domain name.

Configuration

  1. Check whether a page named Frontpage got created
  2. If necessary set it as Front Page in the WP settings
  3. Start adding content to the page.

Local Development

Initial Setup for Linux / Mac OS X

  • On Mac OS X I recommend using MAMP which simplifies the setup significantly
  • Then just wget https://wordpress.org/latest.tar.gz, unzip into the web root of MAMP
  • Follow the steps in ‘Initial Deployment’

Note: Windows as a development platform is not supported.

Hints

  • Set PRODUCTION to false in the .env so you can see the ACF sidebar menu item in WP admin
  • spin up compass watch in the theme directory to create css from sass
  • in your ~/.profile set up an alias like this for easy SSH: alias ssh-example-domain-stage="ssh -i /absolute/path/to/your/.ssh/example.pem admin@stage.exammple.com"

HTML5 index.html Template

There are lots of html5 boilerplates out there, http://www.initializr.com/ being one that shows up on Google Search on top.

However often, I only need to copy and paste the index.html to start a new test / experiment. Here it is. Adapted version from initializr.

Initialise a package.json with npm init and then

npm install jquery
npm install normalize.css
npm install bootstrap
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">

<link rel="stylesheet" href="node_modules/normalize-css/normalize.css">
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css" />
<link href="/css/base.css" media="screen, projection" rel="stylesheet" type="text/css" />

</head>
<body>
<!--[if lt IE 8]>
You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.
<![endif]-->

<script src="node_modules/jquery/dist/jquery.min.js"></script>
<script src="node_modules/bootstrap/dist/js/bootstrap.min.js"></script>

<script src="js/main.js"></script>
</body>
</html>

Quick LAMPW (WordPress) server setup on Debian

Installation

sudo apt-get update
sudo apt-get install apache2 mysql-server php5 libapache2-mod-php5 php5-mysql phpmyadmin vim git sendmail curl php5-curl
sudo a2enmod rewrite
cd /var/www/
sudo wget https://wordpress.org/latest.tar.gz
sudo tar -zxvf latest.tar.gz
sudo chown -R www-data:www-data wordpress/

VirtualHost Setup

<VirtualHost *:80>
 # The ServerName directive sets the request scheme, hostname and port that
 # the server uses to identify itself. This is used when creating
 # redirection URLs. In the context of virtual hosts, the ServerName
 # specifies what hostname must appear in the request's Host: header to
 # match this virtual host. For the default virtual host (this file) this
 # value is not decisive as it is used as a last resort host regardless.
 # However, you must set it for any further virtual host explicitly.

 ServerName www.founderstories.ch

 ServerAdmin webmaster@localhost
 DocumentRoot /var/www/wordpress

<Directory /var/www/wordpress >
 Options Indexes FollowSymLinks MultiViews
 AllowOverride All
 Order allow,deny
 allow from all
</Directory>

 ErrorLog ${APACHE_LOG_DIR}/error.log
 CustomLog ${APACHE_LOG_DIR}/access.log combined

</VirtualHost>

Restart Apache

sudo /etc/init.d/apache2 restart

Set up the database:

mysql -uroot -pyourpassword -e "create database wordpress"
mysql -uroot -pyourpassword -e "GRANT ALL PRIVILEGES ON wordpress.* To 'wordpress'@'localhost' IDENTIFIED BY 'password';"

Then finish the setup by going through the WordPress installation web wizard.

django CMS on Divio / Docker

This is for developers at what. that are new to the Divio world of django and django CMS. Welcome!

Below you will find a couple of hints that will hopefully make your life simpler in regards to handling django and django CMS projects with Divio hosting.

Divio Intro

First of all, get familiar with the Divio platform and download their tools:

Handle Deployment Failures on Divio

  • Grep for error in the Docker stdout (divio.com log)
  • When it works locally, but not on the cloud: put the local server into live configuration
  • How to provide cases to divio.com in case above doesn’t help: Push the state that causes problems to a separate branch and ping with a step by step reproduction instructions and the link to the branch.

Collaboration through Divio Control with your own git repository

Tip: Use gitlab or github or such and add your own repository to the project so you have full control over the repository.

How to run the frontend pipeline in Docker:

docker-compose run --rm web gulp watch --debug

But in general it’s better to run npm outside of the docker container in your host system, it’s more performant.

Migrations

docker-compose run --rm web python manage.py migrate

or if the container is already running:

docker-compose exec web python manage.py migrate

Performance

Are you on Mac OS X? First of all, open the Docker for Mac settings and release the docker machine from its pitiful state: Increase memory to 3GB and give it a second CPU (no need for additional GPU power though).

Adding django apps to your project

Rebuild the web container after adding a Django App:

docker-compose build web

Docker says no space left on device

docker system prune

Debug Aldryn Settings

docker-compose run --rm -e ALDRYN_ADDONS_DEBUG=True web python manage.py

This will give you a folder in

addons/.debug/

with the history of how settings are set by different aldryn modules.

Aldryn addons

Addons add some magic to every django CMS project. Especially to your settings. In order to debug the settings from addons you can add a print() statement for the settings after the following line to see what settings variables are already present from the addons:

aldryn_addons.settings.load(locals())

  • Settings are added in the order of how the addons are added to INSTALLED_ADDONS
  • Ssh into a docker container, run `env` to show all the env variables, many of these are picked up as settings.py variables.
  • You can copy and paste addons outside of the XML brackets in your settings.py and use them as normal pip packages, just be aware that every version has it’s own unique UUID in the package URL, so you cannot guess the URL of an addon package for a specific version you need to know it from Divio Control.

It’s best not to use aldryn addons anymore. Use pypi instead. It’s much simpler to develop using a minimum example django / django CMS setup in the package itself, so it can be installed in itself with the pip -e flag, also good for unit tests.

Access the docker postgres database

pycharm has a pretty ok database editor. You can connect to your docker database.

  1. Add this to your docker-compose.yml. This makes postgresql available on the docker machine’s IP address.
 ports:
  - "5432:5432"

Then reload the containers

docker-compose restart db