What Branch am I on Anyway?

Often during web development the code is switching between different branches and it can be difficult to let everyone on a small team know which branch is in use.

Adding this line to a PHP script and using CSS to make it easy to see can save time.

<div class="git-branch">'.`git name-rev --name-only HEAD`.'</div>

Be sure to wrap it in some code to prevent display on the production site.

One Approach to Complying with a "script-src 'self'" Content Security Policy

I recently encountered this error when working with plugin code on an application:

Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' chrome-extension-resource:".

The cause of the error was inline script code I was using to pass values from the server to the client.

After a bit of research (see the link above), the best solution looked like a little bit of PHP code to create the JavaScript required to pass the values to the client.

The overhead of checking the timestamp and creating the file is minimal, so this code recreates the JavaScript once each day.

<?php

Class CSP {
	const JSFILENAME = 'csp.js';

	static public function cspFilename($dir = __DIR__) {
		return $dir.'/'.self::JSFILENAME;
	}

	static public function cspFileNeedsRebuild($filename) {
		if (!is_file($filename)) {
			return true;
		}
		$fileLastModified = date('z',filemtime($filename));
		$today = date('z');
		return $fileLastModified !== $today;
	}
}

$someValue = 'Some value';
$jsFilename = CSP::cspFilename();
if (CSP::cspFileNeedsRebuild($jsFilename)) {
	$js = 'var someValue = "'.$someValue.'";'.PHP_EOL;
	file_put_contents($jsFilename,$js);
}
echo '<script src="'.$jsFilename.'"></script>'; 

Other solutions I could have used would have been to disable the Content Security Policy, but that's really a stupid approach. There is also nonce and one may code the policy with more complex values.

Presenting KnpMenus with Dojo

This Dojo module will traverse a menu delivered by the KnpMenuBundle and present it as a Dijitized menu bar.

There may be a few extraneous modules included, but this code will likely be extended.

define([
    "dojo/_base/declare",
    "dojo/dom",
    "dojo/dom-attr",
    "dojo/dom-construct",
    "dojo/on",
    "dojo/query",
    "dijit/registry",
    "dijit/MenuBar",
    "dijit/MenuBarItem",
    "dijit/PopupMenuItem",
    "dijit/PopupMenuBarItem",
    "dijit/MenuItem",
    "dijit/DropDownMenu",
    "dijit/form/Button",
    "dijit/Dialog",
    "app/lib/common",
    "dojo/i18n!app/nls/core",
    "dojo/NodeList-traverse",
    "dojo/domReady!"
], function (declare, dom, domAttr, domConstruct, on, query, registry,
        MenuBar, MenuBarItem, PopupMenuItem, PopupMenuBarItem, MenuItem, DropDownMenu, Dialog,
        lib, libGrid, core) {

    function run() {
        var menuBar = new MenuBar({}, "admin-top-menu");

        function createMenuItem(widget, parent, depth) {
            var children, node, item, i, label, nextNode;
            var popupMenuObj, labelObj, link;
            children = query(parent).children();
            for( i = 0; i < children.length; i++ ) {
                node = children[i];
                nextNode = (typeof children[i + 1] !== "undefined") ? children[i + 1] : null;
                switch( node.tagName ) {
                    case "SPAN":
                    case "A":
                        label = node.textContent.trim();
                        if( typeof node.href !== "undefined") {
                            link = node.href;
                        } else {
                            link = null;
                        }
                        if( nextNode !== null && nextNode.tagName === "UL" ) {
                            popup = new DropDownMenu();
                            popupMenuObj = {label: label, popup: popup};
                            if( depth <= 1 ) {
                                item = new PopupMenuBarItem(popupMenuObj);
                            } else {
                                item = new PopupMenuItem(popupMenuObj);
                            }
                            createMenuItem(popup, nextNode);
                        } else {
                            labelObj = {label: label};
                            if( depth <= 1 ) {
                                item = new MenuBarItem(labelObj);
                            } else {
                                item = new MenuItem(labelObj);
                            }
                        }
                        if( link !== null ) {
                            item.on("click", function () {
                                location.href = link
                            });
                        }
                        widget.addChild(item);
                        break;
                    case "LI":
                        createMenuItem(widget, node, depth + 1);
                        break;
                }
            }
        }

        var menuElements = query("#admin-top-menu ul");
        if( menuElements.length > 0 ) {
            createMenuItem(menuBar, menuElements[0], 0);
            domConstruct.destroy(menuElements[0]);
        }
        menuBar.startup();
    }
    return {
        run: run
    };
});

Symfony / Dojo - Prod and Dev environment management

Dojo has a great build process which allows you to create a optimized and minimized files for the client side (and more!).

In a production environment, this greatly improves performance.

However, building the code after every change will slow development significantly.

Since Dojo can load the required modules dynamically, you can load the source files and work with them directly, and maintain your profile file as you work. Running a build at the end of each development session will help to ensure the code and profile stay in sync.

admin.base.html.twig

This template provides the foundation page layout for all admin pages. If the application is running in a dev environment, it includes a page_footer_script, but in production, it includes dojo.js

{% if app.environment == 'dev' %}
    {% include 'admin/parts/page_footer_script.html.twig' %}
{% else %}
    <script data-dojo-config="async:1" src="/release/dojo/dojo.js"></script>
{% endif %}

{% block javascripts %}
{% endblock %}

{% if omit_menu is not defined %}
    <script>
        require([
            "app/admin/menu",
            "dojo/domReady!"
        ], function (menu) {
            menu.run();
        });
    </script>
{% endif %}

page_footer_script.html.twig

Used only in the dev environment


<script>
    var dojoConfig = {
        async: true,
        baseUrl: '/release',
        paths: {"lib": "/app/lib",
            "nls": "/app/nls"},
        packages: [
            {"name": 'app', "location": '/app'},
            'dojo',
            'dijit',
            'dojox',
            'dgrid',
            'dstore',
            'put-selector',
            'xstyle'
        ],
        selectorEngine: 'lite',
        tlmSiblingOfDojo: false,
        has: {
            "dojo-trace-api": false
        }
    };
</script>
<script data-dojo-config="async:1" src="/vendor/dojo/dojo/dojo.js"></script>

example-page.html.twig

Each page template has a javascripts block which includes the require call to bring in the client side code.


{% block javascripts %}
    <script>
        require(["app/admin/asset/brand"], function (brand) {
            brand.run();
        });
    </script>
{% endblock %}

Symfony / Dojo - Prod and Dev environment management

Ansible SSH issues under CentOS

I needed a CentOS 6.5 guest under a CentOS 6.6 host for development and to prepare for deployments.

I could not get Ansible to SSH into the box.

I took the provisioning out of the Vagrantfile and began running the playbook on the command line with:

ansible-playbook main.yml -i inventories/vagrant -u vagrant -vvvv -k

Issues:

  • Offending key in /home/user - removed offending key
  • sshpass not installed - installed sshpass
  • SSHed in directly using the user (vagrant) that will be provisioning
  • Set the .gui flag to true in the Vagrantfile - http://docs.vagrantup.com/v2/virtualbox/configuration.html. This made it easier to see what was happening.