<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Paul Pepper</title>
	<atom:link href="https://www.paulpepper.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.paulpepper.com</link>
	<description>Software Developer</description>
	<lastBuildDate>Sun, 11 Feb 2018 00:18:54 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://www.paulpepper.com/wp-content/uploads/2017/04/paulpepper.com-logo-32x32.png</url>
	<title>Paul Pepper</title>
	<link>https://www.paulpepper.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>HTTPS with Letsencrypt on nginx</title>
		<link>https://www.paulpepper.com/blog/2017/03/https-with-letsencrypt-on-nginx/</link>
		
		<dc:creator><![CDATA[Paul]]></dc:creator>
		<pubDate>Wed, 15 Mar 2017 23:01:24 +0000</pubDate>
				<category><![CDATA[Uncategorised]]></category>
		<category><![CDATA[HTTPS]]></category>
		<category><![CDATA[nginx]]></category>
		<guid isPermaLink="false">https://www.paulpepper.com/?p=165</guid>

					<description><![CDATA[Open port 443 on Shorewall firewall using the ACCEPT rule /etc/shorewall/rules # Allow access to web server default ports (secure and unsecured HTTP) ACCEPT net $FW tcp 80 ACCEPT net $FW tcp 443 Download the certbot-auto program: $ cd /etc/nginx $ sudo mkdir letsencrypt $ cd letsencrypt $ sudo wget https://dl.eff.org/certbot-auto $ sudo chmod a+x ... [<a class="read-more" href="https://www.paulpepper.com/blog/2017/03/https-with-letsencrypt-on-nginx/">read more</a>]]]></description>
										<content:encoded><![CDATA[<p>Open port 443 on Shorewall firewall using the ACCEPT rule <code>/etc/shorewall/rules</code></p>
<pre># Allow access to web server default ports (secure and unsecured HTTP)
ACCEPT net $FW tcp 80
ACCEPT net $FW tcp 443</pre>
<p>Download the certbot-auto program:</p>
<pre>$ cd /etc/nginx
$ sudo mkdir letsencrypt
$ cd letsencrypt
$ sudo wget https://dl.eff.org/certbot-auto
$ sudo chmod a+x certbot-auto</pre>
<p>To obtain certificates for a domain, certbot will verify you have ownership by creating files in a hidden directory under the domain&#8217;s web root and issuing a HTTP request for them. To ensure nginx will serve the files, without denying access (403 Forbidden), then add the following to your nginx config. For instance, you may add the rule to the file <code>/etc/nginx/custom-conf/restrictions.con</code> before any rules restricting access to dot files:</p>
<pre># Allow the letsencrypt ACME Challenge.
location ~ /\.well-known\/acme-challenge {
    allow all;
}</pre>
<p>Some of the web applications that I maintain serve static files from /static/. For those domains I place the following location block inside the application&#8217;s server block:</p>
<pre># Allow the letsencrypt ACME Challenge.
location '/\.well-known/acme-challenge' {
    root /var/www/www.paulpepper.com/html/letsencrypt;
    default_type 'text/plain';
    allow all;
}</pre>
<p>Run certbot-auto, specifying the root from which files for domains are served from and the name of those domains that the requested certificate applies to:</p>
<pre>$ sudo ./certbot-auto certonly --agree-tos --webroot -w /var/www/www.paulpepper.com/html -d paulpepper.com -d www.paulpepper.com</pre>
<p>Some of the web applications that I maintain serve static files from /static/. For those domains I place the following location block inside the application&#8217;s server block, changing the location from which the letsencrypt verification files are served:</p>
<pre># Allow the letsencrypt ACME Challenge.
location '/\.well-known/acme-challenge' {
    root /var/www/www.paulpepper.com/html/letsencrypt;
    default_type 'text/plain';
    allow all;
}</pre>
<p>Certbot is then executed as follows, with a change in the</p>
<pre>$ sudo ./certbot-auto certonly --agree-tos --webroot -w /var/www/www.paulpepper.com/html/letsencrypt -d paulpepper.com -d www.paulpepper.com</pre>
<p>If successful, then new key and certificate files should have been created under <code>/etc/letsencrypt/live/paulpepper.com</code> after running the above command.</p>
<p>Next configure nginx to service HTTPS requests for the domains using the new certificate. In the virtual server config:</p>
<pre>server {
    listen 443 ssl;
    server_name paulpepper.com;

    root /var/www/www.paulpepper.com/html;

    include custom-conf/restrictions.conf;

    ssl_certificate /etc/letsencrypt/live/paulpepper.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/paulpepper.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/paulpepper.com/chain.pem;
}</pre>
<p>You might also wish to redirect HTTP requests to HTTPS too:</p>
<pre>server {
    listen 443 ssl;
    server_name www.paulpepper.com;

    root /var/www/www.paulpepper.com/html;

    include custom-conf/restrictions.conf;

    ssl_certificate /etc/letsencrypt/live/paulpepper.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/paulpepper.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/paulpepper.com/chain.pem;
}

server {
    listen 80;
    server_name paulpepper.com www.paulpepper.com;
    return 301 https://www.paulpepper.com$request_uri;
}

server {
    listen 443 ssl;
    server_name paulpepper.com;
    return 301 https://www.paulpepper.com$request_uri;
}</pre>
<p>Check your config and restart nginx</p>
<pre>$ sudo nginx -t
...
$ sudo systemctl restart nginx.service</pre>
<p>If your distribution is still using the Upstart init system then restart nginx as follows:</p>
<pre>$ sudo service nginx restart</pre>
<p>The certbot documentation recommends running a cron job twice per day to renew certificates. Let&#8217;s Encrypt will only renew certificates if they are due to expire, so it&#8217;s safe and good practice to run the renewal frequently.</p>
<p>Create a new file under <code>/etc/cron.d/</code> called <code>letsencrypt</code> with the following content:</p>
<pre> # Run the letsencrypt renewal service using certbot-auto.
 8 06 * * * root /etc/nginx/letsencrypt/certbot-auto renew --no-self-upgrade --post-hook '/bin/systemctl reload nginx.service'

</pre>
<p>The above crontab entry will attempt to run the certbot-auto renewal once per day at 6:08am and then reload nginx if ssl certificates were due for renewal (the <code>--post-hook</code> only executes its argument if certificates were due for renewal).</p>
<p>WordPress content that uses the old http protocol prefix can be replaced using an SQL UPDATE. Here&#8217;s how you might do that:</p>
<pre>$ mysqldump -u root -p paulpepper &gt; paulpepper-db-backup.sql
$ mysql -u root -p
...
mysql&gt; CONNECT paulpepper;
mysql&gt; UPDATE `wp_posts` SET `post_content` = REPLACE(`post_content`, 'http://www.paulpepper.com', 'https://www.paulpepper.com');
mysql&gt; UPDATE `wp_posts` SET `post_content` = REPLACE(`post_content`, 'http://paulpepper.com', 'https://paulpepper.com');</pre>
<p>The table <code>wp_posts</code> is the one containing content for pages and post, but may be named slightly depending upon the value of the <code>$table_prefix</code> variable found in your wp-config.php file. The standard value given to this variable is &#8216;wp_&#8217; and so wp_posts is normally table that should be targeted by the above SQL.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>WordPress Development Environment</title>
		<link>https://www.paulpepper.com/blog/2017/02/wordpress-development-environment/</link>
		
		<dc:creator><![CDATA[Paul]]></dc:creator>
		<pubDate>Tue, 28 Feb 2017 11:27:17 +0000</pubDate>
				<category><![CDATA[Uncategorised]]></category>
		<category><![CDATA[WordPress]]></category>
		<guid isPermaLink="false">https://www.paulpepper.com/?p=175</guid>

					<description><![CDATA[Here&#8217;s a little detail about the development environment setup that I use whenever I need to develop a WordPress theme or plugin. My aim is to somewhat isolate each project (sadly, not quite as effectively as Python&#8217;s VirtualEnv) and keep it from impacting more general workstation setup such as configs under /etc/. Run PHP in Isolation No need to [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Here&#8217;s a little detail about the development environment setup that I use whenever I need to develop a WordPress theme or plugin. My aim is to somewhat isolate each project (sadly, not quite as effectively as Python&#8217;s VirtualEnv) and keep it from impacting more general workstation setup such as configs under <code>/etc/</code>.</p>
<h1>Run PHP in Isolation</h1>
<p>No need to set up a new virtual web server for each site. Use PHP&#8217;s CLI and a router&#8230;</p>
<p>Get and unzip the latest version of WordPress and rename as necessary for your new project:</p>
<pre>$ wget https://wordpress.org/latest.tar.gz
$ tar xzvf latest.tar.gz
$ mv wordpress newproject
$ cd newproject
$ mv wp-config-sample.php wp-config.php</pre>
<p>Ensure the PHP command-line is installed:</p>
<pre>$ sudo apt-get install php7.0-cli</pre>
<p>Create a <code>router.php</code> file in the root directory (we named ours &#8216;newproject&#8217;, above) of the WordPress install:</p>
<pre>&lt;?php
$root = $_SERVER['DOCUMENT_ROOT'];
chdir($root);

$path = '/'.ltrim(parse_url($_SERVER['REQUEST_URI'] )['path'],'/');

if (file_exists($root.$path)) {
    if (is_dir($root.$path) &amp;&amp; substr($path,strlen($path) - 1, 1) !== '/') {
        header('Location: '.rtrim( $path,'/' ).'/');
        exit;
    }

    if (strpos($path,'.php') === false) {
        return false;
    } else {
        chdir(dirname($root.$path));
        require_once $root.$path;
    } 
} else {
    include_once 'index.php';
}</pre>
<p>From within the root directory of the WordPress installation run PHP&#8217;s built-in server from the command-line, passing the name of our <code>router.php</code> file as an argument:</p>
<pre>$ php -S localhost:8080 router.php</pre>
<p>PHP will then pass all HTTP requests through the router, which either, redirects, passes control to WordPress or serves up static assets as necessary.</p>
<p>From a browser access the newly created project (http://localhost:8080) and the familiar WordPress installation routine should be presented.</p>
<p>If you&#8217;d additionally like to avoid setting up a database instance for your new dev project, then read on&#8230;</p>
<h1>Disposable Database Setup</h1>
<p>Get latest version of WordPress SQLite plugin and extract inside the plugins directory:</p>
<pre>$ cd wp-content/plugins
$ wget https://downloads.wordpress.org/plugin/sqlite-integration.zip
$ unzip sqlite-integration.zip</pre>
<p>Copy <code>db.php</code> from the sqlite-integration plugin directory into the <code>wp-content</code> directory:</p>
<pre>$ cp sqlite-integration/db.php ../.</pre>
<p>Ensure <code>php7.0-sqlite3</code> and <code>sqlite3</code> are both installed:</p>
<pre>$ sudo apt-get install sqlite3 php7.0-sqlite3
</pre>
<p>You should now be able to run the PHP built-in webserver, as described above, but now your WordPress data will be persisted to an SQLite file, <code>wp-content/database/.ht.sqlite</code>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>AngularJS Providers, Factories and Services</title>
		<link>https://www.paulpepper.com/blog/2014/10/angularjs-providers-factories-and-services/</link>
		
		<dc:creator><![CDATA[Paul]]></dc:creator>
		<pubDate>Tue, 07 Oct 2014 23:47:51 +0000</pubDate>
				<category><![CDATA[Uncategorised]]></category>
		<category><![CDATA[AngularJS]]></category>
		<category><![CDATA[JavaScript]]></category>
		<guid isPermaLink="false">http://localhost:8080/?p=65</guid>

					<description><![CDATA[Angular Services Angular relies heavily upon objects that it refers to as Services. An Angular Service is a singleton object that can be injected into other Angular components (other Services, Controllers, Directives, Filters, etc). Services may be defined and registered in a number of ways using Angular&#8217;s Module interface. The Module interface offers the functions, provider, factory [&#8230;]]]></description>
										<content:encoded><![CDATA[<h2>Angular Services</h2>
<p><a href="http://angularjs.org/">Angular </a>relies heavily upon objects that it refers to as <em>Services</em>. An Angular Service is a singleton object that can be injected into other Angular components (other Services, Controllers, Directives, Filters, etc).</p>
<p>Services may be defined and registered in a number of ways using Angular&#8217;s <a href="https://docs.angularjs.org/api/ng/type/angular.Module">Module</a> interface. The <code>Module </code>interface offers the functions, <code>provider</code>, <code>factory</code> and <code>service</code> for service definition. (There are other service definition functions on this interface, but they&#8217;re of less interest here.) Each of these functions is used in a manner that the Angular documentation refers to as a <em>recipe</em>, with each offering varying degrees of sophistication and configurability.</p>
<p>The <code>$provider</code> Service can be used to register Services after Angular&#8217;s configuration phase, however we normally only require the <code>Module</code> interface.</p>
<h2>Provider</h2>
<p><code>Module.provider()</code> offers the greatest flexibility in defining and registering a Service. <code>Module.factory()</code> and <code>Module.service()</code> wrap and simplify <code>Module.provider()</code> to achieve their results.</p>
<p>Here&#8217;s an example of a <code>Pony</code> Service defined and registered using <code>Module.provider()</code>.</p>
<pre>var Pony = function(ponyColour, FoodService) {
    // ...
};

var ponyModule = angular.module('ponyModule');

ponyModule.provider('Pony', function() {
    var colour = 'pink';

    this.setColour = function(value) {
        colour = value;
    };

    this.$get = ['Cheese', function(Cheese) {
        return new Pony(colour, Cheese);
    }];
};</pre>
<p>After creating an Angular module named <code>ponyModule</code>, the above code defines and registers an Angular Service Provider which is used to create the <code>Pony</code> Service. The Service Provider is responsible for constructing and returning the Angular Service instance of <code>Pony</code> from its <code>$get()</code> factory function. Notice that the <code>Pony</code> service is also injected with the <code>Cheese</code> service (defined elsewhere).</p>
<p>The Provider method of creating a Service is overkill in most circumstances, but it is useful to understand Service creation in it&#8217;s more fundamental form. Only use this technique when a Service is used by more than one application and requires application-specific configuration during Angular&#8217;s module configuration phase.</p>
<p>In the above code, a pony&#8217;s colour may be overridden by calling the <code>setColour()</code> function (defined on the Provider object instance) during Angular&#8217;s configuration phase. Angular makes the Provider instance available to us by appending the word <code>Provider</code> to the Service name &#8211; <code>PonyProvider</code> in our case. The Provider instance can then be injected into the application&#8217;s configuration function:</p>
<pre>var app = angular.module('app', ['Pony']);

app.config(['PonyProvider', function(PonyProvider) {
  PonyProvider.setColour('blue');
  // ...
});</pre>
<p>Incidentally, the Provider instance itself can be injected with other Service Provider instances during Angular&#8217;s configuration phase, in much the same manner that dependency injection is performed on a Service.</p>
<h2>Factory</h2>
<p><code>Module.factory()</code> offers the next most sophisticated way to define and register a Service. Using this technique, only the Provider&#8217;s <code>$get()</code> factory function is defined and registered:</p>
<pre>ponyModule.factory('Pony', ['Cheese', function(Cheese) {
  return new Pony('blue', Cheese);
}]);</pre>
<p>Here Service configurability (during Angular&#8217;s configuration phase) has been sacrifised in order to simplify Service registration.</p>
<h2>Service</h2>
<p><code>Module.service</code> is the simplest technique for defining and registering a Service:</p>
<pre>ponyModule.service('Pony', ['Cheese', function(Cheese) {
  this.colour: 'blue',
  // ...
}]);</pre>
<p>Angular will call <code>new</code> on the Service constructor function for us. Slightly more configurability has been lost again, though the Service definition and registration is simpler. In the above use of <code>Module.service()</code> we have set a <code>Pony</code>&#8216;s colour within the <code>Module.service()</code> function.</p>
<p>How flexible you require Service configuration will likely drive which of the above three techniques you employ. <code>Module.service</code> will likely be sufficient for defining and registering the Services used by most applications.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Preserve the Shell Environment Using sudo</title>
		<link>https://www.paulpepper.com/blog/2014/02/preserve-shell-environment-using-sudo/</link>
		
		<dc:creator><![CDATA[Paul]]></dc:creator>
		<pubDate>Wed, 19 Feb 2014 18:56:56 +0000</pubDate>
				<category><![CDATA[Uncategorised]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[security]]></category>
		<guid isPermaLink="false">https://www.paulpepper.com/?p=81</guid>

					<description><![CDATA[When executing a command or script as another user, it may be necessary to preserve the current shell&#8217;s environment. sudo provides the -E flag for this. However, on Ubuntu systems the PATH environment variable is not preserved by the -E flag. Work around this by passing the current shell&#8217;s PATH environment variable on the command-line [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>When executing a command or script as another user, it may be necessary to preserve the current shell&#8217;s environment. <code>sudo</code> provides the <code>-E</code> flag for this.</p>
<p>However, on Ubuntu systems the <code>PATH</code> environment variable is not preserved by the <code>-E</code> flag. Work around this by passing the current shell&#8217;s <code>PATH</code> environment variable on the command-line in the form of <code>PATH=$PATH</code>.</p>
<pre>$ sudo PATH=$PATH -E -u anotheruser ./some-command.sh</pre>
<p>There are other ways to manage environment variables with sudo by editing <code>/etc/sudoers</code> (use <code>sudoedit</code>, don&#8217;t edit it directly!), but the above can be useful to quickly get the job done.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Locating Django Applications in Their Own Sub-Directory</title>
		<link>https://www.paulpepper.com/blog/2014/02/locating-django-applications-their-own-sub-directory/</link>
		
		<dc:creator><![CDATA[Paul]]></dc:creator>
		<pubDate>Mon, 17 Feb 2014 22:50:49 +0000</pubDate>
				<category><![CDATA[Uncategorised]]></category>
		<category><![CDATA[Django]]></category>
		<guid isPermaLink="false">https://www.paulpepper.com/?p=83</guid>

					<description><![CDATA[It&#8217;s straightforward to keep Django applications in their own directory if necessary &#8211; though the convention is to place them in the project&#8217;s root directory. Instructions below assume pwd/$PWD is the project&#8217;s root directory. Create the directory in which Django applications will be located: $ mkdir apps Create a new application named _newapp_ under apps/newapp [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>It&#8217;s straightforward to keep Django applications in their own directory if necessary &#8211; though the convention is to place them in the project&#8217;s root directory.</p>
<p>Instructions below assume <code>pwd</code>/<code>$PWD</code> is the project&#8217;s root directory.</p>
<ul>
<li>Create the directory in which Django applications will be located:</li>
</ul>
<pre>$ mkdir apps</pre>
<ul>
<li>Create a new application named _newapp_ under <code>apps/newapp</code> using <code>startapp</code>&#8216;s optional directory parameter (this requires that the target directory exist, so create it first):</li>
</ul>
<pre>$ mkdir apps/newapp
 $ ./manage.py startapp newapp apps/newapp</pre>
<ul>
<li>Modify the project&#8217;s <code>manage.py</code> file to ensure the development server can find the applications. It should end up looking something like the following:</li>
</ul>
<pre>#!/usr/bin/env python
 import os
 import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")

    # Add the apps directoriy to Python's path. In production it will
    # be necessary to add the apps directory to the path, too.
    from os.path import abspath, dirname, join
    PROJECT_ROOT = abspath(dirname(__file__))
    sys.path.append(join(PROJECT_ROOT, "apps"))

    from django.core.management import execute_from_command_line

    execute_from_command_line(sys.argv)</pre>
<ul>
<li>For a development environment, that&#8217;s it. For production, ensure the <code>apps</code> directory is included on the project&#8217;s <code>PYTHONPATH</code>. If using Apache and <code>mod_wsgi</code> then ensure that the <code>WSGIDaemonProcess</code> directive in the project&#8217;s <code>&lt;virtualhost/&gt;</code> includes the path to the newly added <code>apps</code> directory, as well as the path to the (virtualenv&#8217;s) python site packages directory and the project&#8217;s root directory:</li>
</ul>
<pre>WSGIDaemonProcess myapp python-path=/myapp/apps:/myapp/python-packages:/myapp</pre>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Django, X-SendFile and Apache</title>
		<link>https://www.paulpepper.com/blog/2014/02/django-x-sendfile-and-apache/</link>
		
		<dc:creator><![CDATA[Paul]]></dc:creator>
		<pubDate>Wed, 05 Feb 2014 12:37:40 +0000</pubDate>
				<category><![CDATA[Uncategorised]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[python]]></category>
		<guid isPermaLink="false">https://www.paulpepper.com/?p=85</guid>

					<description><![CDATA[Static File Access Management Django documentation advises against using an application to directly serve static files when in a production environment. A dedicated HTTP server, optimised for serving static files, should be used for this purpose. That&#8217;s fine for serving publicly accessible files. But serving static files which require user access permission checking necessarily involves [&#8230;]]]></description>
										<content:encoded><![CDATA[<h2>Static File Access Management</h2>
<p><a href="https://docs.djangoproject.com/en/dev/howto/static-files/deployment/">Django documentation advises against using an application to directly serve static files when in a production environment</a>. A dedicated HTTP server, optimised for serving static files, should be used for this purpose. That&#8217;s fine for serving publicly accessible files. But serving static files which require user access permission checking necessarily involves routing the download request via application code.</p>
<p>It&#8217;s possible to get the best of both Django view processing, where access permissions can be managed and dedicated HTTP server download handling, by using a HTTP server extension that processes response headers applied by the web application. The <a href="https://tn123.org/mod_xsendfile/">mod_xsendfile Apache module</a> is one such module. Used with <a href="https://pypi.python.org/pypi/django-sendfile">django-sendfile</a>, application management of file downloads can be cleanly separated into interface and implementation.</p>
<h2>django-xsendfile and Apache mod_xsendfile Installation</h2>
<p>Install the mod_xsendfile module:</p>
<pre>$ sudo apt-get install libapache2-mod-xsendfile</pre>
<p>The mod_xsendfile module is usually enabled by the install scripts. Use <code>a2enmod</code> to further manage its enabled status.</p>
<p>Enable X-SENDFILE header processing within the application&#8217;s Apache virtual host. For instance, a virtual host for for a Django application to serve example.com, <code>/etc/apache2/sites-available/example.com</code>:</p>
<pre>&lt;VirtualHost *:80&gt;
    ServerName example.com

    # mod_wsgi settings
    WSGIDaemonProcess example python-path=/var/www/example.com/app
    WSGIProcessGroup example
    WSGIScriptAlias / /var/www/example.com/app/example/wsgi.py

    # Publicly available static files directly available via Apache.
    Alias /static /var/www/example.com/app/static
    Alias /pub-uploads /var/www/example.com/pub-uploads

    # Restricted access files via Apache mod_xsendfile.
    XSendFile On
    XSendFilePath /var/www/example.com/priv-uploads

    # Apache &lt;directory/&gt; directives here...
&lt;/VirtualHost&gt;</pre>
<p>From the Django application:</p>
<pre>$ pip install django-xsendfile</pre>
<p>Within the Django application&#8217;s <code>settings.py</code>:</p>
<pre>SENDFILE_BACKEND = 'sendfile.backends.xsendfile'</pre>
<p>Conventionally this can be overridden in a <code>local_settings.py</code> and imported by <code>settings.py</code> for a development environments:</p>
<pre>SENDFILE_BACKEND = 'sendfile.backends.development'</pre>
<p>The Django view used to manage download access permissions may then look something like the following:</p>
<pre>from django.views.generic.base import View
from sendfile import sendfile

class DownloadFile(View):
    def get(self, request):
    # Get user access rights and the file's file-system path.
    # ...
    # If access denied return HttpResponseForbidden(), else:
    return sendfile(request, file_path)</pre>
<p>The sendfile API also provides response header management, such as <code>Content-Disposition</code> header content:</p>
<pre>def sendfile(request, filename,
    attachment=False, attachment_filename=None,
    mimetype=None, encoding=None)</pre>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Set up git and gitosis on Ubuntu</title>
		<link>https://www.paulpepper.com/blog/2012/08/set-git-and-gitosis-ubuntu/</link>
		
		<dc:creator><![CDATA[Paul]]></dc:creator>
		<pubDate>Thu, 02 Aug 2012 17:03:06 +0000</pubDate>
				<category><![CDATA[Uncategorised]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[version control]]></category>
		<guid isPermaLink="false">https://www.paulpepper.com/?p=88</guid>

					<description><![CDATA[Introduction Gitosis is used to help centrally manage git repositories. Gitosis will allow: SSH access to repositories (with the help of openssh-server). User management without the need to add server shell accounts for each person accessing repositories. While gitosis manages user repository access, gitosis is accessed through a single shell account (its use is limited [&#8230;]]]></description>
										<content:encoded><![CDATA[<h2>Introduction</h2>
<p>Gitosis is used to help centrally manage git repositories. Gitosis will allow:</p>
<ul>
<li>SSH access to repositories (with the help of openssh-server).</li>
<li>User management without the need to add server shell accounts for each person accessing repositories.</li>
<li>While gitosis manages user repository access, gitosis is accessed through a single shell account (its use is limited to a specific gitosis command in ssh config).</li>
</ul>
<h2>Central Repository Server</h2>
<p>Install gitosis (apt-get should install all dependencies):</p>
<pre>paul@server$ sudo apt-get install gitosis</pre>
<p>As the first administrator of the gitosis installation, grant access to gitosys for yourself by passing in your SSH public key (the one you currently use to securely access the server via ssh can be used, but better practice is to use one specially created for gitosis access &#8211; see section below on creating and managing ssh keys) to the gitosis-init command:</p>
<pre>paul@server$ sudo -H -u gitosis gitosis-init &lt; ~/.ssh/id_rsa.pub</pre>
<p>After executing the above command you should notice that the gitosis authorized_keys file (~gitosis/.ssh/authorized_keys) has been populated with your public key. gitosis will add new entries to this file when new users are granted access to the gitosis system.</p>
<h2>Cloning the gitosis-admin Project</h2>
<p>You should now be able to clone the gitosis admin repository to your workstation:</p>
<pre>paul@workstation$ git clone gitosis@server:gitosis-admin.git</pre>
<p>Be sure that your workstation is correctly configured to use the ssh private key counterpart to the public key that you used when initialising gitosis (see above).</p>
<p>As the admin you can now manage gitosis system access by adding and removing user public keys in the keydir directory of the gitosis admin project directory (shown cloned above as gitosis-admin). Projects and user access to those projects is managed by editing the gitosis.conf file found in the gitosis admin project directory.</p>
<h2>SSH Key Management</h2>
<p>This isn&#8217;t a tutorial on ssh; just a little assistance with commonly required ssh config when adding access to new gitosis users on your system.</p>
<p>Each user should should create a public/private key pair for exclusive use in accessing your gitosis service. The key pair can be created using ssh-keygen as follows:</p>
<pre>$ ssh-keygen -t rsa</pre>
<p>When ssh-keygen requests a filename, provide something that will help you, the workstation user, associate the key file names with their intended use, e.g. gitosis@server-name.id_rsa</p>
<p>In your workstation&#8217;s ~/.ssh/config you should instruct ssh to use those keys against your server for the gitosis user:</p>
<pre>Host gitosis.server-name.com
User gitosis
Hostname server-name.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/gitosis@server-name.id_rsa</pre>
<p>The ssh public key file (the one ending .pub) can then be added to the keydir directory of the gitosis admin project. You may wish to rename the public key files to something like paul@workstation when copying them into the keydir directory.</p>
<h2>Adding New Projects</h2>
<p>As an administrator of a gitosis system, it is possible to add new projects. Within the gitosis-admin project, add a new project entry, adding the names of the public key files (less the .pub extension) for the members you wish to grant access:</p>
<pre>[group project-team]
writable = new_project
members = paul@workstation fred@anotherworkstation
 
[group gitosis-admin]
...</pre>
<p>Commit and push the changes to the gitosis server:</p>
<pre> $ git commit -a -m "Added new_project as a new project."
 $ git push</pre>
<p>It should now be possible to push the project files up to your gitosis server:</p>
<pre>$ mkdir myproject
$ cd myproject
$ :&gt; hello.py
$ git init
$ git add .
$ git commit -a -m "Initial commit."
$ remote add origin gitosis@server.com:new_project.git
$ git push origin master</pre>
<h2>Gitosis Username and Project Directory</h2>
<p>Warning: you probably shouldn&#8217;t do this&#8230; The Apt scripts will assume the original username and home directory, so the following changes may break future Apt updates.</p>
<p>The Ubuntu Apt system creates the user gitosis to access the server. If a different username and/or home directory are required then it&#8217;s necessary to apply changes to the gitosis user account. To change the home directory (from the default /srv/gitosis to /home/git):</p>
<pre> paul@server$ sudo usermod --home /home/git gitosis</pre>
<p>To change the username used to access gitosis (from gitosis to git):</p>
<pre>paul@server$ sudo usermod --login git gitosis</pre>
<p>&nbsp;</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Overusing Software Reuse</title>
		<link>https://www.paulpepper.com/blog/2012/05/software-reuse-overuse/</link>
		
		<dc:creator><![CDATA[Paul]]></dc:creator>
		<pubDate>Wed, 23 May 2012 12:54:51 +0000</pubDate>
				<category><![CDATA[Uncategorised]]></category>
		<category><![CDATA[software design]]></category>
		<guid isPermaLink="false">https://www.paulpepper.com/?p=91</guid>

					<description><![CDATA[Well intentioned, but poorly considered efforts at code reuse can cause allsorts of maintenance pain &#8211; engineering best practice shouldn&#8217;t be applied unthinkingly. Although software reuse can be good (see Dry Principle, for example) it should only be applied where there isn&#8217;t too much variation from the common case. Taking code reuse as an example, [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Well intentioned, but poorly considered efforts at code reuse can cause allsorts of maintenance pain &#8211; engineering best practice shouldn&#8217;t be applied unthinkingly. Although software reuse can be good (see <a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself">Dry Principle</a>, for example) it should only be applied where there isn&#8217;t too much variation from the common case.</p>
<p>Taking code reuse as an example, a good software engineer will judge what should be considered the common case and how much variation from it would result in poor application of reuse. That type of judgement often comes with the experience of having attempted reuse where it isn&#8217;t best suited!</p>
<p>Poor results often come where a good case for reuse hasn&#8217;t yet been established &#8211; i.e. where it is only anticipated there&#8217;s such a case for reuse. Before embarking on an effort to create generic, reusable code, the rule-of-thumb, &#8216;use before reuse&#8217;, normally applies. A corollary to this is that good quality reusable code is often factored from existing (well designed) code, or at least evolves.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Setting date and time on Debian and Ubuntu</title>
		<link>https://www.paulpepper.com/blog/2012/05/setting-date-and-time-debian-ubuntu/</link>
		
		<dc:creator><![CDATA[Paul]]></dc:creator>
		<pubDate>Tue, 22 May 2012 10:09:44 +0000</pubDate>
				<category><![CDATA[Uncategorised]]></category>
		<category><![CDATA[Linux]]></category>
		<guid isPermaLink="false">https://www.paulpepper.com/?p=94</guid>

					<description><![CDATA[The Linux date command can take several formats to set the system date andtime. Here&#8217;s the format that I use when setting the date and the time: mmddHHiiYYYY.ss where, mm = month dd = day of month HH = hour in 24 hour format ii = minutes ss = seconds The following is an example [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>The Linux <code>date</code> command can take several formats to set the system date andtime.</p>
<p>Here&#8217;s the format that I use when setting the date and the time:</p>
<pre>mmddHHiiYYYY.ss</pre>
<p>where,</p>
<pre>mm = month
 dd = day of month
 HH = hour in 24 hour format
 ii = minutes
 ss = seconds</pre>
<p>The following is an example usage using the date and time, 22nd May 2012, 11:57am, respectively:</p>
<pre>$ sudo date&amp;nbsp_place_holder;052211572012.00</pre>
<p>If only the time needs to be set then use the -s flag as follows:</p>
<pre>$ sudo date&amp;nbsp_place_holder;-s&amp;nbsp_place_holder;"11:57:00"</pre>
<p>If the system&#8217;s time zone requires changing, then on Debian-based systems use:</p>
<pre>$ sudo dpkg-reconfigure tzdata</pre>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Installing pip, virtualenv and virtualenvwrapper</title>
		<link>https://www.paulpepper.com/blog/2012/05/installing-pip-virtualenv-and-virtualenvwrapper/</link>
		
		<dc:creator><![CDATA[Paul]]></dc:creator>
		<pubDate>Thu, 17 May 2012 08:31:28 +0000</pubDate>
				<category><![CDATA[Uncategorised]]></category>
		<category><![CDATA[python]]></category>
		<guid isPermaLink="false">https://www.paulpepper.com/?p=100</guid>

					<description><![CDATA[As recommended in a public announcement cd into a directory where distribute_setup.py can be downloaded and executed. $ wget http://python-distribute.org/distribute_setup.py $ sudo python distribute_setup.py $ sudo easy_install pip $ sudo pip install virtualenv $ sudo pip install virtualenvwrapper]]></description>
										<content:encoded><![CDATA[<p>As recommended in a <a href="http://s3.pixane.com/pip_distribute.png">public</a> announcement</p>
<p>cd into a directory where distribute_setup.py can be downloaded and executed.</p>
<pre>$ wget http://python-distribute.org/distribute_setup.py
$ sudo python distribute_setup.py
$ sudo easy_install pip
$ sudo pip install virtualenv
$ sudo pip install virtualenvwrapper</pre>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
