Feed aggregator

Vasudev Kamath: Loading Python modules/plug-ins at runtime

Planet Debian - Mon, 07/04/2014 - 18:25

Some times it is desired to load arbitrary python files or pre- installed python modules during application run time.I had encountered 2 such usecases, one is in SILPA application and other is dictionary-bot which I was refactoring recently.

Case 1: Loading installed python module

In case of SILPA I need to load pre-installed modules and here is the old code , that is a bit hacky code I copied from Active State Python recipies. I found a bit better way to do it using importlib module as shown below.

from __future__ import print_function import sys import importlib def load_module(modulename): mod = None try: mod = importlib.import_module(modulename) except ImportError: print("Failed to load {module}".format(module=modulename), file=sys.stderr) return mod

Here importlib itself takes care of checking if modulename is already loaded by checking sys.modules[modulename], if loaded it returns that value, otherwise it loads the module and sets it to sys.modules[modulename] before returning module itself.

Case 2: Loading python files from arbitrary location

In case of dictionary bot my requirement was bit different, I had some python files lying around in a directory, which I wanted to plug into the bot during run time and use them depending on some conditions. So basic structure which I was looking was as follows.

pluginloader.py plugins | |__ aplugin.py | |__ bplugin.py

pluginloader.py is the file which needs to load python files under plugins directory. This was again done using importlib module as shown below.

import os import sys import re import importlib def load_plugins(): pysearchre = re.compile('.py$', re.IGNORECASE) pluginfiles = filter(pysearchre.search, os.listdir(os.path.join(os.path.dirname(__file__), 'plugins'))) form_module = lambda fp: '.' + os.path.splitext(fp)[0] plugins = map(form_module, pluginfiles) # import parent module / namespace importlib.import_module('plugins') modules = [] for plugin in plugins: if not plugin.startswith('__'): modules.append(importlib.import_module(plugin, package="plugins")) return modules

Above code first searches for all python file under specified directory and creates a relative module name from it. For eg. file aplugin.py will become .aplugin. Before loading modules itself we will load the parent module in our case plugins, this is because relative imports in python expects parent module to be already loaded. Finally for relative imports to work with importlib.import_module we need to specify parent module name in package argument. Note that we ignore files begining with __, or specifically we don't want to import __init__.py, this will be done when we import parent module.

The above code was inspired from a answer on StackOverflow, which uses imp module, I avoided imp because its been deprecated from Python 3.4 in favor of importlib module.

Categories: Elsewhere

Olivier Berger: Tagged a first version of the TWiki to FusionForge’s MediaWiki converter

Planet Debian - Mon, 07/04/2014 - 18:23

As announced previously, I’ve been hacking on a migration tool allowing to import into the MediaWiki of a FusionForge project, a conversion of the contents of a TWiki wiki.

I’ve succesfully imported a first project (from PicoForge to FusionForge) using the tool, so I’ve decided to tag a first release and make the Git repo accessible.

More details at : https://fusionforge.int-evry.fr/projects/pytwiki2mediawi/

Feel free to ask here in the comments or by email, in case of need.

And, yes, my Python is most likely awful, but at least, this works, and much more featureful than existing tools I could test.

Categories: Elsewhere

Stanford Web Services Blog: Overriding Open Framework Styles: Block styles, Sidebar Menus, and Regions

Planet Drupal - Mon, 07/04/2014 - 18:06

In this post, I continue my series on how to override Open Framework's default styles to get a more custom look-and-feel on your site. Last time we looked at how to override our typography styles. Today, we'll look at a grab bag of other things, including block styles, sidebar menus, and region styles.

Categories: Elsewhere

Fred Parke | The Web Developer: Create your own tokens in Drupal 7

Planet Drupal - Mon, 07/04/2014 - 17:58

Tokens are a pretty powerful weapon to have in your arsenal, and they actually come in useful a lot if you remember that they're there.

If you haven't used them before, tokens are essentially text placeholders - they can be static text, variables, field values, whatever you want really.

The Token API is now part of Drupal 7 core and as it turns out, using it to create your own tokens is super easy - you just need a couple of hooks.

The first hook, hook_token_info(), is used to declare any custom tokens.

Categories: Elsewhere

Appnovation Technologies: Accessibility in the Real World: Top 6 Automation Misses

Planet Drupal - Mon, 07/04/2014 - 17:25
Making your work accessible shouldn’t be a optional deliverable; we have a duty of care to the real world of users to build this in as part of our work. No matter how good automated test tools are, they won't cover everything. Here are 6 points of consideration for accessibility. var switchTo5x = false;stLight.options({"publisher":"dr-75626d0b-d9b4-2fdb-6d29-1a20f61d683"});
Categories: Elsewhere

Bryan Braun: Making Targeted Drupal Cache Clears using Drush

Planet Drupal - Mon, 07/04/2014 - 16:21

The standard Drush command for clearing Drupal's cache looks like this:

drush cache-clear all

(You can also use the shortened alias cc like this: drush cc all)

These commands give you the same result as when you click that cache clear button in the UI — it clears all of Drupal's internal caches.

But clearing all of Drupal's caches at once can be overkill. You usually don't need to clear everything, and doing so can put heavy load on your servers (especially if your site is large or gets a lot of traffic). Beneath the surface, Drupal's caching is actually pretty granular, and tools like Drush give us the ability to target and clear caches on the subsystem level.

Using Drush, you can see your caching options with:

# Using the shortened alias from this point on. drush cc

Which returns something like this:

[0] : cancel [1] : all [2] : drush [3] : theme registry [4] : menu [5] : css+js [6] : block [7] : module list [8] : theme list [9] : registry ...

Let's look at what each of these does (as a quick note, I'm specifically looking at Drush 6, which is the major version at this time):

drush cc all
Remember how I said that this does the same thing as the cache clear button in the UI? Well, that's technically false. Yes, drush cc all will clear all your Drupal caches (as long as it can bootstrap Drupal), but it will also clear its own internal Drush cache. That's why when Drush cannot bootstrap Drupal, you will still see a success command saying 'drush' cache was cleared.

drush cc drush
This only clears Drush's internal cache (the same one I just mentioned). You don't need a Drupal site available to clear this cache.

drush cc theme registry
This command simply calls drupal_theme_rebuild() to rebuild the theming system. This is needed whenever new ".tpl.php" files or theme hooks are added to the system. This specific cache clear only applies for Drupal 7 and up.

drush cc menu
This runs a menu rebuild, which refreshes the database tables used by various menu functions. For example, any new router items (like those defined in hook_menu) are added to the menu_links table, and stale ones are removed. This also clears the page and block caches, to prevent the display of stale menu links.

drush cc css+js
If you have CSS or JS aggregation enabled, this will rebuild the aggregated files. It also increments the query string on CSS & JS links, forcing clients that have cached an old copy to download a fresh one.

drush cc block
Block caching exists so Drupal doesn't have to look up the locations and visibility of blocks with each page load. This command refreshes that cache.

drush cc module list
This re-scans the module directories in your codebase and refreshes Drupal's internal list of which modules are available.

drush cc theme list
This re-scans the theme directories in your codebase and refreshes Drupal's internal list of which themes are available.

drush cc registry
Drupal maintains an internal registry of all functions or classes in the system, allowing it to lazy-load code files as needed (reducing the amount of code that must be parsed on each request). The list of these files is known as the "code registry" and it is stored in the system table in your Drupal database. This cache clear will look at this list of files and update the contents of any files that have been changed. Note: it will not rebuild the registry from scratch. For more information, see registry_update.

drush cc ?????
You may see other options in this list, because contributed modules (like views and advanced aggregation) can add their own kinds of cache clears. In each case, you'll see a file in the contrib module named something like mymodule.drush.inc that contains the code which defines what the cache clear does.


If you want to see what each of these options does on a code level, you can download Drush and inspect the file found at Drush/commands/core/cache.drush.inc.

Categories: Elsewhere

Wunderkraut blog: Drupal 8 and the slow death of IE8

Planet Drupal - Mon, 07/04/2014 - 14:02

IE8 is resisting to die. Internet Explorer 8 world-wide usage is more popular than IE9 and even IE10

First, a small story.Once upon a time, in 2012, when Drupal 8 was going to support IE8, we added HTML5Shiv to Drupal core to support HTML5 elements in IE8 and old browsers. But during 2013 things happened. jQuery decided to split their library into: 
  • jQuery 1.x (IE8, IE7, IE6 support)
  • jQuery 2.x (IE9 support and newer)
Both branches have the same jQuery API. This situation, clearly pushed the Drupal core maintainers into a big decision: Should Drupal 8 core ship with jQuery 1.x or 2.x ?. Nat Catchpole ("catch") summarized this dilemma very well:  And the community and Dries, decided almost at the same time to embrace ECMAScript 5, jQuery 2 and drop IE8 support. The change record was modified (to include IE8 as not supported). And we all rejoiced, specially front-end developers. Yay! To end this small story, I will link it to the beginning: there is a task pending about removing HTML5Shiv from Drupal core. All the IE8 issues are spread in drupal.org, so it's nice that nod_ created a meta issue: Drop IE8 support.Present. 2014Looking back, it looks like dropping IE8 support was a good decision. This allowed core developers to write more efficient CSS3 and ECMAScript 5 code. And we avoid to waste the valuable time of core developers supporting old browsers. We jumped on the bandwagon of modern JavaScript libraries.  As I said sometimes, the biggest change in Drupal core front-end is not Twig, it's Drupal core dropping IE8 support. The only big problem is that IE8 is dying very slowly. During the discussions in 2012-2013 we thought that IE8 usage will drop fast (we wanted to believe that). But the reality hurts, This is a chart from StatCounter (IE8 has a 4.71% usage. IE11 is not available in the chart, but it has a 6.29% usage in March 2014):  From netmarketshare.com, the trend is even worse. IE8 has still 21.14% of the browser share on March 2014. 
I hid other browsers in the charts to highlight the situation with Internet Explorer.But one thing is clear: IE8 seems to be more popular than IE9, IE10 and even IE11. This is mainly due of Windows XP users. Why the difference between StatCounter and Netmarketshare?They have different methodologies.  As I understand, netmarketshare manipulates their data to make them more realistic.They are adding a country level weighting, based on how many internet-users the country has, even if their data samples are tiny. So that could distort a bit their data. But the good thing in netmarketshare methodology, is that they count users not traffic. (the same user is only counted once per day, no matter how many page loads she makes). In the other hand StatCounter counts page visits, not users. For example, for StatCounter, an internet-savy teenager  loading hundreds of pages per day in Chrome, counts the same as 100 hundred "grandpas" that are loading once a day their local newspaper in IE8. There is no winner. Both charts are correct, since they represent different things. But the truth is that there is a lot of people outside there using IE8 today. The hopeEveryone hopes that IE8 dies faster, including Microsoft. Two positive notes:
  • Tomorrow, 8th of April, Microsoft is announcing the drop of Windows XP support. No more updates. 
  • IE8 doesn't exist in mobile phones. The trend is that mobile browsers are eating desktop browsers usage, around the world. 
Non official FAQ. Drupal & IE8What if you try to load a Drupal 8 website with IE8? IE8 won't support many CSS3 stuff and EcmaScript 5 code. So a broken layout and lost JavaScript funcionality will be the normal thing to see. Drupal will load jQuery 2.x (that uses addEventListener method), IE8 will complain about it, stop parsing jQuery, and all your jQuery code won't work because "SCRIPT5009: jQuery is undefined". What if a customer asks for IE8 support?Stick to Drupal 7. The other option is to use a Drupal 8 and the work-in-progress-contrib-module IE8 Drupal module, that should downgrade jQuery to 1.x branch and include lots of polyfills to support ECMAScript 5 in IE8. Personally, it sounds to me too "magical",  that it could fix all CSS and broken JS, specially if your site has many contrib modules. But for sure, it will help. Also notice that Drupal helps the situation a bit,  including "X-UA-Compatible" http header to force IE use the most recent IE engine. What if I'm maintainer of Drupal module or theme? Drupal's core decision is pushing "gently" all the contrib modules and themes to follow the same path. When porting your modules, JavaScript code, etc make sure it works well with jQuery 2.x API. If you’re upgrading your Drupal 7 jQuery code from a version older than 1.9, jQuery team recommends to read the jQuery 1.9 Upgrade Guide since there have been a lot of changes, and help yourself using the jQuery Migrate plugin. Once that is migrated to 1.9, it will be compatible with jQuery 2.x, because they should have same API. Corrections welcomePlease, comment in this article, correct me in case I did wrong assumptions and I will update the post with the most up-to-date information.
Categories: Elsewhere

drupalsear.ch: Drupal Dev Days Szeged - Initial Sprint for Search API Drupal 8

Planet Drupal - Mon, 07/04/2014 - 12:02

I’ll tell you in a small prelude a story of a small place in the world named Szeged and why it holds a special place in my heart for me (Nick_vh). When I was job-hunting about 5 years and a half ago I found many companies willing to offer me a job after graduating but the one company I chose was willing to take a huge bet on me and offered me a job + an instant trip to Szeged, Hungary. It seemed that that was the location of the gathering of the Drupal Community in 2008. They said it would be amazing and I never looked back since then and I am still connected to the same and growing Drupal Community.

Going back to Szeged promised to be a lot of nostalgia and a lot of fun also.

Looking back

For those that just tune in on this story of the Search API Drupal 8 port, there is some reading to do.

In short: “Contrib Search maintainers are committed to make Drupal 8 kick ass with Search API.”

This is also exactly what we wanted to prove on this wonderful trip to Hungary.

What our goals were

Thomas and I already agreed this week in Szeged would be the first of a series of sprints to get Search API to a decent state in Drupal 8. As you can read in the Search API for Drupal 8 sprint entry for the Drupal Dev Days  the goals were the following: “The primary focus will be to get the Search API to a usable state in D8 and then decide on and implement framework improvements”

We discussed and talked about what needed to happen but in true community style, not many words were needed to get the first port started and so it happened that, even before all of this, freblasty started a sandbox and already ported a good chunk of the Drupal 7 code to well factored Drupal 8 code.

There was a sign up sheet for the sprints and to our surprise a LOT of people signed up for the sprint. I’ve masked the last names to protect their privacy, so they can opt-in on the exposure of their full name. Drunkenmonkey also prepared a nice list of meta issues that mark the Drupal 8 state before the sprint.

Plan of attack

Some of us already arrived early Monday, the other half arrived Monday afternoon. The team was already hacking away on the code when we decided to use a Google Doc that would semi-coordinate the progress. Process was simple:

  1. Raise your hand and say your name and that you wanted to help out
  2. You were given commit access to the sandbox
  3. Choose a task from the “To Do” in the collaborative document
  4. Solve it and commit it
  5. Say “I committed, please pull!” and do a little Ski-Dance (ask Aspilicious for specifics)
  6. Go to 3 and repeat until Sunday afternoon.

We purposefully supported this simple plan of attack because adding more process would block people from making progress and we figured it was better to break all the things than to block people from helping out.

The attack itself

II’m not going to waste a lot of empty words here but let the hard work/visuals speak for itself.

  • During the whole week we’ve had 322 commits.
  • Heaviest hours were from 09:00 till 19:00 and then it went up again from 22:00 till 24:00. Thanks to freblasty we even had commits during the middle of the night, 9 of them at 04:00.
  • Wednesday was our most productive day with 75 commits. Saturday was our low point with only 36 commits, I guess that has something to do with the party on Friday evening…!
  • 20834 lines were added, 11996 were removed

In total we had 15 contributors!!!! We’ve listed them by # of commits but by no means this means that the ones with more commits worked harder.

  1. drunkenmonkey            55    (15.62%)
  2. mollux_                51    (14.49%)
  3. Nick_vh            46     (13.07%)
  4. aspilicious            43     (12.22%)
  5. Andrew_l            38     (10.80%)
  6. freblasty            34     (9.66%)
  7. ekes                23     (6.53%)
  8. m1r1k                18     (5.11%)
  9. dpovshed            17     (4.83%)
  10. Andre-B            12     (3.41%)
  11. baldwinlouie            7     (1.99%)    
  12. sdecabooter            3     (0.85%)
  13. penyaskito            3     (0.85%)
  14. tstoeckler            1     (0.28%)
  15. pcambra             1     (0.28%)
Drupal 8 Codebase versus Drupal 7 codebase

A quick preview ofthe differences in terms of files of Drupal 8 vs Drupal 7.


Future plans

For now, work continues in the sandbox until the basic functionality is working. Also, mollux is already working on a port of the Database Search backend, which will allow us to test the module with a real search backend.

Once the functionality is stable and we get into improvement/feature-adding mode, the project will be transferred back to the 8.x branch of the proper search_api project and work will continue in the normal style of patches and issues. (Issues for the individual tasks from the Google document have already been converted to issues in the sandbox’s issue queue as of last meeting).

Weekly Meetings on Hangout & IRC

We also set up a weekly meeting in the form of a Google hangout, every Tuesday at 18:00 UTC. Please contact us if you want to be invited, everyone who wants to help with this project is welcome!

Other than this, there is also the possibility of discussing and coordinating via IRC. We are using a special channel, #drupal-search-api on Freenode <irc://freenode/drupal-search-api>, where you can also just join in and ask around if you want to get involved.


Many many many thanks to all those involved in the sprint. We understand this takes a big personal commitment and passion to focus so hard on complex problems to drive the next generation of Drupal Search. Also many thanks to all companies that sponsored the time and funds for allowing those people to be there. I hope this blogpost can help your company to convince them to send you to these events. It's educational, helpful and will lift you and your company to a higher level.


Proof of the team in Szeged, hard at work

Categories: Elsewhere

Web Omelette: Adding Facebook Open Graph tags to your Drupal site

Planet Drupal - Mon, 07/04/2014 - 09:10

In this article we are going to look at implementing the Facebook Open Graph (OG) tags on a Drupal site. It involves a bit of coding but nothing too major so bear with me. All the code we are going to write goes into the theme's template.php file so no need to to create a module for it. And if your theme does not yet have this file, go ahead and create it.

Why use Open Graph tags?

I'm sure by now you know that people share pages on Facebook and you want your site to be shared as much as possible. You also know that a little teaser information is made available on Facebook when you share these pages (title, image, short description).

But have you ever noticed that with some sites, when you share pages on Facebook, or like/recommend them, some random, unintented image gets used in this teaser. Or the description is not correct, or even the title is not right. Facebook is quite good at picking the right elements to show there but sometimes it doesn't manage by default. And you can't expect the random user who shares your page to make adjustments to the text or title. So what do you do? You use Open Graph meta tags.

What are the Open Graph meta tags?

These are simple <meta> tags that you put in the head of your site to specify which elements should be used by Facebook for various purposes. For instance, you specify a link to an image and then Facebook knows exactly which image to use when building the teasers. The same goes with the title, description and many others.

The tag is quite simply structured. It contains a property attribute in which you specify what this tag is for and a content attribute where you specify what should be used for this property. Let's see an example for the title:

<meta property="og:title" content="This is the article title" />

Simple. You'll also notice that the property attribute value is preceeded by og:. This stands for Open Graph and Facebook will recognize it as such. Here are some examples for other more common OG tags:

<meta property="og:url" content="http://webomelette.com/article-url" /> <meta property="og:site_name" content="Web Omelette" /> <meta property="og:type" content="blog" />

The first one is the canonical URL for the page, the second is the site title and the third is the type of site.

But how do we add them to Drupal?

I wrote a while back an article on this site in which I looked at how you can add your own custom tags into the <head> of the site. And there we learned that we use the drupal_add_html_head() function inside of a preprocess one to achieve this.

So let's say that our Article nodes don't show up properly in the Facebook teasers and we would like to specify OG tags for the title, image and description. We will do this in the theme's template.php file inside of the template_preprocess_node() function:

function theme_name_preprocess_node(&$vars) { }

Inside this function we get access to the node information being loaded and we can test to make sure we are adding our OG tags only to the pages that load these nodes:

// If the node is of type "article", add the OG tags if ($vars['type'] == 'article') { // Inside this conditional, we define and add our OG tags }

First, let's create the title tag, the simplest of them all. It will be represented by the node title:

$og_title = array( '#tag' => 'meta', '#attributes' => array( 'property' => 'og:title', 'content' => $vars['title'], ), ); drupal_add_html_head($og_title, 'og_title');

If you remember from the other article, this is how we create a new meta tag in the site header. We define a renderable array that describes the tag (type and attributes) and we use the drupal_add_html_head() function to set it. Simple. Now if you clear the cache and go to an article page you'll notice in its source a new tag:

<meta property="og:title" content="The title of the article" />

This was simple. Let's see how we can extract the image URL and specify it inside a new tag for the image Facebook will use:

$img = field_get_items('node', $vars['node'], 'field_image'); $img_url = file_create_url($img[0]['uri']); $og_image = array( '#tag' => 'meta', '#attributes' => array( 'property' => 'og:image', 'content' => $img_url, ), ); drupal_add_html_head($og_image, 'og_image');

So what happens here? First, we use the field_get_items() function to retrieve the field called field_image from the loaded node. This will return an array of one or more images (depending on how the field is set up and how many images are in it).

Next, we use the image file URI inside the file_create_url() function to turn it into an absolute URL. Then, just like above, we create the renderable array with the og:image property and the image URL as the content. The result will be:

<meta property="og:image" content="http://your-site.com/sites/all/files/your-image.jpg" />

Finally, let's see how we can get a small chunk of our body field and use that as a description for Facebook:

$body_field = field_view_field('node', $vars['node'], 'body', array('type' => 'text_plain')); $og_description = array( '#tag' => 'meta', '#attributes' => array( 'property' => 'og:description', 'content' => text_summary($body_field[0]['#markup']), ), ); drupal_add_html_head($og_description, 'og_description');

Instead of using the same function as when we retrieved the image field earlier, we use field_view_field() in this case. The reason is that it already prepares the body for output and we can specify a text format to use. We want to use plain text to avoid printing all the HTML markup that is usually found in the body field.

Next, like above, we create our renderable array. Only this time, we also use the text_summary() function to trim the text down to a reasonable default of 600 words (the defaul teaser length on the site). If you want to specify a different length, you could pass it as the third argument, like so:

text_summary($body_field[0]['#markup'], NULL, 100),

This will trim it to 100 words. One thing I noticed about this function however is that it doesn't trim nicely. It will end the chunk in the middle of the sentence even if its API says it will try to find a sensible place. For this purpose it doesn't really matter because Facebook will trim it down anyway to a shorter version.


So there you have it. Now you can use the Open Graph meta tags on your Drupal site to let Facebook know about your content. It's a handy social helper that can greatly improve your social presence by making your site more appealing on Facebook.

In Theming | Drupal var switchTo5x = true;stLight.options({"publisher":"dr-8de6c3c4-3462-9715-caaf-ce2c161a50c"});
Categories: Elsewhere

Andrew Pollock: [life] Day 69: Walk to King Island, a picnic at Wellington Point, the long slow acquisition of some linseed and a split lip

Planet Debian - Mon, 07/04/2014 - 07:15

Today was a really good day, right up until the end, when it wasn't so good, but could have been a whole lot worse, so I'm grateful for that.

I've been wanting to walk out to King Island at low tide with Zoe for a while, but it's taken about a month to get the right combination of availability, weather and low tide timing to make it possible.

Today, there was a low tide at about 10:27am, which I thought would work out pretty well. I wasn't sure if the tide needed to be dead low to get to King Island, so I thought we could get there a bit early and possibly follow the tide out. I invited Megan and Jason to join us for the day and make a picnic of it.

It turned out that we didn't need a really low tide, the sand bar connecting King Island to Wellington Point was well and truly accessible well before low tide was reached, so we headed out as soon as we arrived.

I'd brought Zoe's water shoes, but from looking at it, thought it would be walkable in bare feet. We got about 10 metres out on the sand and Zoe started freaking out about crabs. I think that incident with the mud crab on Coochiemudlo Island has left her slightly phobic of crabs.

So I went back to Jason's car and got her water shoes. I tried to allay her fears a bit by sticking my finger in some of the small holes in the sand, and even got her to do it too.

I'm actually glad that I did get her water shoes, because the shell grit got a bit sharp and spiky towards King Island, so I probably would have needed to carry her more than I did otherwise.

Along the way to the island we spotted a tiny baby mud crab, and Zoe was brave enough to hold it briefly, so that was good.

We walked all the way out and partially around the island and then across it before heading back. The walk back was much slower because where was a massive headwind. Zoe ran out of steam about half way back. She didn't like the sand getting whipped up and stinging her legs, and the wind was forcing the brim of her hat down, so I gave her a ride on my shoulders for the rest of the way back.

We had some lunch after we got back to Wellington Point, and Zoe found her second wind chasing seagulls around the picnic area.

After an ice cream, we went over to the playground and the girls had a great time playing. It was a pretty good park. There was this huge tree with a really big, thick, horizontal branch only about a metre or two off the ground. All the kids were climbing on it and then shimmying along the branch to the trunk. Zoe's had a few climbs in trees and seems not afraid of it, so she got up and had a go. She did really well and did a combination of scooting along, straddling the branch and doing a Brazilian Jiu-Jitsu-style "bear crawl" along the branch.

It was funny seeing different kids' limits. Zoe was totally unfazed by climbing the tree. Megan was totally freaking out. But when it came to walking in bare feet in an inch of sea water, Zoe wanted to climb up my leg like a rat up a rope, in case there were crabs. Each to their own.

Zoe wanted to have a swim in the ocean, so I put her into her swimsuit, but had left the water shoes back in the car. Once again, she freaked out about crabs as soon as we got ankle deep in the water, and was freaking out Megan as well, so the girls elected to go back to playing in the park.

After a good play in the park, we headed back home. We'd carpooled in Jason's truck, with both girls in the back. I'd half expected Zoe to fall asleep on the way back, but the girls were very hyped up and had a great time playing games and generally being silly in the back.

When we got back to our place, Jason was in need of a coffee, so we walked to the Hawthorne Garage and had coffee and babyccinos, before Megan and Jason went home.

It was about 3:30pm at this point, and I wanted to make a start on dinner. I was making a wholemeal pumpkin quiche, which I've made a few times before, and I discovered we were low on linseed. I thought I'd push things and see if Zoe was up for a scooter ride to the health food shop to get some more and kill some time.

She was up for it, but ran out of steam part way across Hawthorne Park. Fortunately she was okay with walking and didn't want me to carry her and the scooter. It took us about an hour to get to the health food shop.

Zoe immediately remembered the place from the previous week where we'd had to stop for a post-meltdown pit stop and declared she needed to go to the toilet again.

We finally made it out of the shop. I wasn't looking forward to the long walk back home, but there were a few people waiting for a bus at the bus stop near the health food shop, and on checking the timetable, the bus was due in a couple of minutes, so we just waited for the bus. That drastically shortened the trip back.

Zoe managed to drop the container of linseed on the way home from the bus stop, but miraculously the way it landed didn't result in the loss of too much of the contents, it just split the container. So I carefully carried the container home the rest of the way.

By this stage it was quite a bit later than I had really wanted to be starting dinner, but we got it made, and Zoe really liked the pumpkin quiche, and ate a pretty good dinner.

It was after dinner when things took a turn for the worse.

Zoe was eating an ice block for dessert, and for whatever reason, she'd decided to sit in the corner of the kitchen next to the dishwasher, while I was loading it. I was carrying over one of the plates, and the knife managed to fall off the plate, bounce off the open dishwasher door and hit her in the mouth, splitting her lip.

Zoe was understandably upset, and I was appalled that the whole thing had happened. She never sits on the kitchen floor, let alone in the corner where the dishwasher is. And this knife came so close to her eye.

Fortunately the lip didn't look too bad. It stopped bleeding quickly, and we kept some ice on it and the swelling went down.

I hate it when accidents happen on my watch. I feel like I'm fighting the stigma of the incompetent single Dad, or the abusive single Dad, so when Zoe sustains an injury to the face like a fat lip, which could be misinterpreted, I, well, really hate it. This was such a freak accident, and it could have gone so much worse. I'm just so glad she's okay.

Zoe recovered pretty well from it, and I was able to brush her teeth without aggravating her lip. She went to bed well, and I suspect she's going to sleep really well. It's a bit cooler tonight, so I'm half-expecting a sleep in in the morning with any luck.

Categories: Elsewhere

Russ Allbery: Review: Fantasy & Science Fiction, September/October 2011

Planet Debian - Mon, 07/04/2014 - 06:02

Review: Fantasy & Science Fiction, September/October 2011

Editor: Gordon van Gelder Issue: Volume 121, No. 3 & 4 ISSN: 1095-8258 Pages: 258

Another review of a magazine that I finished quite some time ago. Apologies for any inaccuracies or lack of depth in the reviews.

There wasn't much in Charles de Lint's reviews in this issue that interested me, but Michelle West covers a great selection of books. Two of them (The Wise Man's Fear and The Quantum Thief) are already on my to-read list; the third, The Postmortal, sounded interesting and would go on my list to purchase if I didn't already have so many good books I've not read. Otherwise, this issue is short on non-fiction. The only other essay entry is a film review from Kathi Maio, which is the typical whining about all things film that F&SF publishes.

"Rutger and Baby Do Jotenheim" by Esther M. Friesner: Baby is a former pole dancer with a toy poodle named Mister Snickers, which warns you right away that this story is going to involve a few over-the-top caricatures and more use of the word "piddle" than one might ideally want. Rutger is a mythology professor who tolerates her for the standard reasons in this sort of pairing. They're travelling across country to Baby's sister's wedding when their car breaks down in Minnesota, prompting an encounter with frost giants.

As you might expect, this is a sort of fractured fairy tale, except based on Norse mythology instead of the more typical Grimm fare. The fun is in watching these two apparent incompetents (but with enough knowledge of mythology to clue in the reader) reproduce the confrontation between Thor and Utgard-Loki. The fight with old age is particularly entertaining. If you've read any of Friesner's other stories, you know what to expect: not much in the way of deeper meaning, but lots of fun playing with stereotypes and an optimistic, funny outcome. Good stuff. (7)

"The Man Inside Black Betty" by Sarah Langan: This story comes with a mouthful of a subtitle: "Is Nicholas Wellington the World's Best Hope?" It's also a story that purports to be written by a fictional character, in this case one Saurub Ramesh (with Langan credited as having done "research"). It's told in the style of first-person journalism, relating the thoughts and impressions of Ramesh as he interviews Nicholas Wellington. The topic is Black Betty: a black hole above Long Island Sound. Wellington is a scientific genius and iconoclast with radical theories of black holes that contradict how the government has been attempting to deal with Black Betty, unsuccessfully.

The structure here was well-handled, reminding me a lot of a Michael Lewis article during the financial collapse. Langan has a good feel for how journalism of this type mixes personalities, politics, and facts. But it's all setup and no story. We get some world building, and then it's over, with no resolution except pessimism. Meh. (4)

"A Borrowed Heart" by Deborah J. Ross: Ross starts with the trappings of urban fantasy transplanted into a Victorian world: supernatural creatures about, a protagonist who is a high-class prostitute, and sex and a sucubus by the second page. It evolves from there into a family drama and an investigation, always giving the reader the impression that a vampire will jump out at any moment. But the ending caught me entirely by surprise and was far more effective due to its departure from the expected path. Well done. (7)

"Bright Moment" by Daniel Marcus: The conflict between terraforming and appreciation for the universe as we find it is an old story pattern in science fiction, and Marcus doesn't add much here. I think the story would have been stronger if he'd found a way to write the same plot with a pure appeal to environmental beauty without the typical stakes-raising. But he does sprinkle the story with a few interesting bits, including a pod marriage and a futuristic version of extreme sports as a way of communing with nature. (6)

"The Corpse Painter's Masterpiece" by M. Rickert: This is typical of my reaction to a Rickert story: shading a bit too much towards horror for me, a bit too cryptic, well-written but not really my thing. It's about a corpse painter who does the work of an informal mortician, improving the appearance of bodies for their funerals, and the sheriff who brings him all the dead bodies. It takes an odd macabre twist, and I have no idea what to make of the ending. (4)

"Aisle 1047" by Jon Armstrong: Armstrong is best known for a couple of novels, Grey and Yarn, which entangle their stories in the future of marketing and commerce. One may be unsurprised, then, that this short story is on similar themes, with the intensity turned up to the parody point. Tiffan3 is a department-store saleswoman, spouting corporate slogans and advertising copy while trying to push customers towards particular products. The story follows the escalation into an all-out brand war, fought with the bubbly short-cut propaganda of a thirty-second commercial. For me, it fell awkwardly between two stools: it's a little too over-the-top and in love with its own bizarre alternate world to be effective satire, but the world is more depressing than funny and the advertising copy is grating. More of a curiosity than a successful story, I think. (5)

"Anise" by Chris DeVito: Stories that undermine body integrity and focus on the fascinated horror of violation of physical boundaries aren't generally my thing, so take that into account in this review.

Anise's husband died, but that's not as much of a problem as it used to be. Medical science can resurrect people via a sort of permanent, full-body life support system, making them more cyborg than human. "Anise" is about the social consequences of this technology in a world where a growing number of people have a much different relationship with their body than the typical living person today. It's a disturbing story that is deeply concerned with the physical: sex, blood, physical intimacy in various different forms, and a twisted type of psychological abuse. I think fans of horror will like this more than I did, although it's not precisely horror. It looks at the way one's perception of self and others can change by passing through a profound physical transformation. (5)

"Spider Hill" by Donald Mead: I liked this story a lot better. It's about witchcraft and farm magic, about family secrets, and a sort of coming-of-age story (for a girl rather than a boy, for once). The main character is resourceful, determined, but also empathetic and aware of the impact of her actions, which made her more fun to read about. I doubt I'll remember this for too long, but when skimming through it again for a review, I had fond memories of it. (6)

"Where Have All the Young Men Gone?" by Albert E. Cowdrey: Cowdrey in his paranormal investigation mode, which I like better than his horror mode. For once, the protagonist isn't even a lower-class or backwoods character. Instead, he's a military historian travelling in Austria who runs across a local ghost story. This is a fairly straightforward ghost investigation that follows a familiar path (albeit to an unusual final destination), but Cowdrey is a good story-teller and I liked the protagonist. (7)

"Overtaken" by Karl Bunker: This is the sort of story that delivers its moral with the force of a hammer. It's not subtle. But if you're in the right mood for that, it's one of the better stories of its type. It's about a long-journey starship, crew in hibernation, that's overtaken by a far newer and faster mechanized ship from Earth that's attempting to re-establish contact with the old ships. The story is a conversation between the ship AIs. Save this one until you're in the mood for an old-fashioned defense of humanity. (8)

"Time and Tide" by Alan Peter Ryan: Another pseudo-horror story, although I think it's better classified as a haunting. A wardrobe recalls a traumatic drowning in the childhood of the protagonist. As these things tend to do in stories like this, reality and memory start blurring and the wardrobe takes on a malevolent role. Not my sort of thing. (3)

"What We Found" by Geoff Ryman: Any new Geoff Ryman story is something to celebrate. This is a haunting story on the boundaries between the scientific method and tribal superstition, deeply entangled with the question of how one recovers from national and familial trauma. How can we avoid passing the evils and madness of one generation down to the next? Much of the story is about family trauma, told with Ryman's exceptional grasp of character, but the science is entangled in an ingenious way that I won't spoil.

As with Air, this is in no way science fiction. The science here would have fascinating and rather scary implications for our world, but clearly is not how science actually works. But as an insight into politics, and into healing, I found it a startlingly effective metaphor. I loved every bit of this. By far the best story of the issue. (9)

Rating: 7 out of 10

Categories: Elsewhere

Paul Tagliamonte: Photo

Planet Debian - Mon, 07/04/2014 - 04:33

Categories: Elsewhere

Harry Slaughter: One revision control system to rule them all

Planet Drupal - Mon, 07/04/2014 - 04:18

The first revision control system I ever used was called RCS. It was the pre-cursor to CVS and stored all revision data locally. It was nifty but very limited and not suited for group development. CVS was the first shared revisioning system I used. It was rock solid, IMHO. But it had a few big problems, like the inability to rename or move files. Everything had to be deleted and re-added. 

Since those days, I've used several other revisioning systems: Perforce, Bitkeeper, Clearcase, Subversion and GIT.

read more

Categories: Elsewhere

High Rock Media: Drupal 8: Attaching Core Libraries and Other Scripts to Your Theme

Planet Drupal - Mon, 07/04/2014 - 01:54

For the past six months, I've been in the process of porting my contrib theme, Gratis, to Drupal 8. One of the challenges for contrib is Drupal 8 has been a constant moving target in terms of API changes. With every new Alpha of Drupal 8, I've had to adjust many bits of theme code to adhere to these new APIs.

One of the biggest challenges has been figuring out how to add jQuery and other core scripts into the theme. That's because off the shelf, Drupal 8 does not add these for anonymous users. This seems like an odd choice by core but my guess is that is was done for Drupal 8 to be more minimalistic and nimble on its feet.

At any rate, if you have a contrib theme, most likely the first thing you'll do is add all of these back in to bring it back to a point where Drupal 7 was. To add an additional layer of complexity to all this is the fact that the manner in which this is done has changed dramatically in recent weeks.

Exit nested arrays, enter YAML

At first one added core libraries using hook_library_info which was a mess of PHP nested arrays in your theme's .theme file. Now, in true Drupal 8 fashion, we do this with a .yml or "yaml" (rhymes with camel) file. YAML is an acronym for "Yet Another Markup Language" or "Ain't Markup Language" and has become a core method for streamlining code in Drupal 8.

Here is a basic example of how you can add jQuery to a theme and a few dependencies. You'd do this by creating a *libraries.yml file in the root of your theme. So in my case my theme name is foobar so the file name would be foobar.libraries.yml.

  version: VERSION
    js/scripts.js: {}
    - core/jquery
    - core/drupal.ajax
    - core/drupal
    - core/drupalSettings
    - core/jquery.once

With the above code, we'll be adding the core scripts jquery.js, drupal.js, ajax.js and jquery.once.js to our theme. This will make it available for anonymous users. With YAML, indentation spaces are relevant so you'll need to get that bit right. In addition, we are calling a theme relative script in the theme's js folder, scripts.js. The name -corescripts can be anything you want as long as it matches when you go to attach it in your .theme file as outlined below.

Attach the Library

The next step to actually attach the library we created with a theme preprocess function creating a variable in combination with the #attached method.

 * Override or insert variables into the page template.
function foobar_preprocess_page(&$vars, $hook) {
  // Render the library we defined in foobar.libraries.yml
  $libraries['#attached']['library'][] = 'foobar/foobar-corescripts';

Attaching the library above implements provider-namespaced strings i.e. 'gratis/foobar-corescripts' where previously it had an array associated with it. This is new in Drupal 8 Alpha 10 and it through me off for a while until I found a core issue that documented this change. Finally, we use function drupal_render to render the new library.

What's Next?

In a future Drupal 8 related articles, I'll get in to setting custom configuration for your Drupal 8 theme, this comes in real handy and we'll be using YAML with the added addition of using Drupal 8's CMI layer. I'll also talk more about API changes as they relate to themers, it's probably safe to do that now as Drupal 8 is most likely heading toward a beta within the next few months and it seems like many of the major core API changes have been done.

  • Drupal
  • YAML
  • Drupal 8
  • Drupal Planet
  • Theming
Categories: Elsewhere

Julien Danjou: Doing A/B testing with Apache httpd

Planet Debian - Mon, 07/04/2014 - 00:20

When I started to write the landing page for The Hacker's Guide to Python, I wanted to try new things at the same time. I read about A/B testing a while ago, and I figured it was a good opportunity to test it out.

A/B testing

If you do not know what A/B testing is about, take a quick look at the Wikipedia page on that subject. Long story short, the idea is to serve two different version of a page to your visitors and check which one is getting the most success. When you found which version is better, you can definitely switch to it.

In the case of my book, I used that technique on the pre-launch page where people were able to subscribe to the newsletter. I didn't have a lot of things I wanted to test out on that page, so I just used that approach on the subtitle, being either "Learn everything you need to build a successful Python project" or "It's time you make the most out of Python".

Statistically, each version would be served half of the time, so both would get the same number of view. I then would build statistics about which page was attracting the most subscribers. With the results I would be able to switch definitively to that version of the landing page.

Technical design

My Web site, this Web site, is entirely static and served by Apache httpd. I didn't want to use any dynamic page, language or whatever. Mainly because I didn't want to have something else to install and maintain just for that on my server.

It turns out that Apache httpd is powerful enough to implement such a feature. There are different ways to build it, and I'm going to describe my choices here.

The first thing to pick is a way to balance the display of the page. You need to find a way so that if you get 100 visitors, around 50 will see the version A of your page, and around 50 will see the version B of the page.

You could use a random number generator, pick a random number for each visitor, and decides which page he's going to see. But it turns out that I didn't find a way to do that with Apache httpd at first sight.

My second thought was to pick the client IP address. But it's not such a good idea, because if you got visitors from, for example, people behind a company firewall, they are all going to be served the same page, so that kind of kills the statistics.

Finally, I picked time based balancing: if you visit the page on a second that is even, you get version A of the page, and if you visit the page on a second that is odd, you get version B. Simple, and so far nothing proves there are more visitors on even than odd seconds, or vice-versa.

The next thing is to always serve the same page to a returning visitor. I mean that if the visitor comes back later and get a different version, that's cheating. I decided the system should always serve the same page once a visitor "picked" a version. To do that, it's simple enough, you just have to use cookies to store the page the visitor has been attributed, and then use that cookie if he comes back.


To do that in Apache httpd, I used the powerful mod_rewrite that is shipped with it. I put 2 files in the books directory, named either "the-hacker-guide-to-python-a.html" and "the-hacker-guide-to-python-b.html" that got served when you requested "/books/the-hacker-guide-to-python".

RewriteEngine On
RewriteBase /books
# If there's a cookie called thgtp-pre-version set,
# use its value and serve the page
RewriteCond %{HTTP_COOKIE} thgtp-pre-version=([^;])
RewriteRule ^the-hacker-guide-to-python$ %{REQUEST_FILENAME}-%1.html [L]
# No cookie yet and…
RewriteCond %{HTTP_COOKIE} !thgtp-pre-version=([^;]+)
# … the number of seconds of the time right now is even
RewriteCond %{TIME_SEC} [02468]$
# Then serve the page A and store "a" in a cookie
RewriteRule ^the-hacker-guide-to-python$ %{REQUEST_FILENAME}-a.html [cookie=thgtp-pre-version:a:julien.danjou.info,L]
# No cookie yet and…
RewriteCond %{HTTP_COOKIE} !thgtp-pre-version=([^;]+)
# … the number of seconds of the time right now is odd
RewriteCond %{TIME_SEC} [13579]$
# Then serve the page B and store "b" in a cookie
RewriteRule ^the-hacker-guide-to-python$ %{REQUEST_FILENAME}-b.html [cookie=thgtp-pre-version:b:julien.danjou.info,L]

With that few lines, it worked flawlessly.


The results were very good, as it worked perfectly. Combined with Google Analytics, I was able to follow the score of each page. It turns out that testing this particular little piece of content of the page was, as expected, really useless. The final score didn't allow to pick any winner. Which also kind of proves that the system worked perfectly.

But it still was an interesting challenge!

Categories: Elsewhere

Russ Allbery: control-archive 1.6.1

Planet Debian - Mon, 07/04/2014 - 00:04

control-archive is the software that maintains the archive of Usenet control messages and the "canonical" list of newsgroups maintained on ftp.isc.org. There's nothing particularly exciting in this release, but there were some accumulated metadata changes and it had been nearly a year since the previous release.

This is one of my few packages that uses a three-part version number, since I figured there would be metadata-only releases and wanted a way to designate those. And then, of course, I almost always made code changes. So this is only the second metadata-only release (which is part of why I did a release now, to be honest).

At some point, I still want to rewrite the underlying signature validation code and then redo all the code in this package to match my current coding style and be quite a bit cleaner. But the hours to do projects like that aren't particularly forthcoming at the moment.

You can get the latest release from the control-archive distribution page.

Categories: Elsewhere

Steve Kemp: So that distribution I'm not-building?

Planet Debian - Sun, 06/04/2014 - 16:35

The other week I was toying with using GNU stow to build an NFS-share, which would allow remote machines to boot from it.

It worked. It worked well. (Standard stuff, PXE booting with an NFS-root.)

Then I started wondering about distributions, since in one sense what I'd built was a minimal distribution.

On that basis yesterday I started hacking something more minimal:

  • I compiled a monolithic GNU/Linux kernel.
  • I created a minimal initrd image, using busybox.
  • I built a static version of the tcc compiler.
  • I got the thing booting, via KVM.

Unfortunately here is where I ran out of patience. Using tcc and the static C library I can compile code. But I can't link it.

$ cat > t.c <>EOF int main ( int argc, char *argv[] ) { printf("OK\n" ); return 1; } EOF $ /opt/tcc/bin/tcc t.c tcc: error: file 'crt1.o' not found tcc: error: file 'crti.o' not found ..

Attempting to fix this up resulted in nothing much better:

$ /opt/tcc/bin/tcc t.c -I/opt/musl/include -L/opt/musl/lib/

And because I don't have a full system I cannot compile t.c to t.o and use ld to link (because I have no ld.)

I had a brief flirt with the portable c-compiler, pcc, but didn't get any further with that.

I suspect the real solution here is to install gcc onto my host system, with something like --prefix=/opt/gcc, and then rsync that into my (suddenly huge) intramfs image. Then I have all the toys.

Categories: Elsewhere

Russell Coker: Finding Corrupt Files that cause a Kernel Error

Planet Debian - Sun, 06/04/2014 - 13:55

There is a BTRFS bug in kernel 3.13 which is triggered by Kmail and causes Kmail index files to become seriously corrupt. Another bug in BTRFS causes a kernel GPF when an application tries to read such a file, that results in a SEGV being sent to the application. After that the kernel ceases to operate correctly for any files on that filesystem and no command other than “reboot -nf” (hard reset without flushing write-back caches) can be relied on to work correctly. The second bug should be fixed in Linux 3.14, I’m not sure about the first one.

In the mean time I have several systems running Kmail on BTRFS which have this problem.

(strace tar cf – . |cat > /dev/null) 2>&1|tail

To discover which file is corrupt I run the above command after a reboot. Below is a sample of the typical output of that command which shows that the file named “.trash.index” is corrupt. After discovering the file name I run “reboot -nf” and then delete the file (the file can be deleted on a clean system but not after a kernel GPF). Of recent times I’ve been doing this about once every 5 days, so on average each Kmail/BTRFS system has been getting disk corruption every two weeks. Fortunately every time the corruption has been on an index file so I don’t need to restore from backups.

newfstatat(4, ".trash.index", {st_mode=S_IFREG|0600, st_size=33, …}, AT_SYMLINK_NOFOLLOW) = 0
openat(4, ".trash.index", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW|O_CLOEXEC) = 5
fstat(5, {st_mode=S_IFREG|0600, st_size=33, …}) = 0
read(5,  <unfinished …>
+++ killed by SIGSEGV +++

Related posts:

  1. Bizarre “No space left on device” error from Xen What should have been a routine “remove DIMMs and run...
  2. BTRFS Status March 2014 I’m currently using BTRFS on most systems that I can...
  3. Kernel Security vs Uptime For best system security you want to apply kernel security...
Categories: Elsewhere

Stefano Zacchiroli: historical overview of debian source code

Planet Debian - Sun, 06/04/2014 - 13:19
moar, and moar, and moar debsources stats

A while ago I've announced the availability of ?several stats about Debian source code on http://sources.debian.net. Since then the statistical basis of those stats has increased a lot, and now includes all Debian historical releases, from hamm (July 1998) onward. This allows to appreciate macro-level evolution trends in Free Software, over a period of more than 15 years, through the eyes of a distro that sits at the nice intersection of the eldest, largest, and most reputed distros.

To get there I've added support for sticky suites to the plumbing layer of debsources, and then injected historical releases from http://archive.debian.org. The injection process took about a week (without any sort of parallelism, pretty slow disks, and computing sha256 checksums, ctags, and sloccount on all source files) and has been an "interesting" experience.

When you go back decades in technology time, bit rot is just around the corner, and I've found my share while injecting archive.d.o into sources.d.n. In both cases the respective maintainers (Guillem and Ganneff, kudos) have been positive about and helpful in improving the situation, despite the low impact of the bugs I've found on the average user. That's quite important for the long-term preservation of digital information in general, and for the perennity of access to Free Software in the specific case of Debian.

While we are it, I'm now maintaining a list of bugs affecting sources.d.n but belonging to other packages, in case you fancy helping out but are not a Python hacker. Interestingly enough, quite a bit of those bugs are related to the fact that tools debsources uses (e.g. ctags, sloccount) are also starting to show their age.

You might wander why buzz, rex, and bo are still missing from sources.d.n. That's in fact for similar reasons. Before hamm Debian didn't have complete archive coverage in terms of Sources indexes and .dsc files. Given that debsources rely on both to extract source packages, it first needs to grow an additional abstraction layer that can cope with their absence. It's SMOP, and planned.

And now let's have fun with ctags bombs.

Yours truly,
Stefano “Indiana” Zacchiroli
(credits: KiBi, #debian-ftp)

Categories: Elsewhere

David Herron: Stopping server overload, cleaning up the site front page, disabling comments, and general goodness

Planet Drupal - Sun, 06/04/2014 - 08:09

The last few days the server hosting this site was overloaded, and I finally took a look at the access log, saw a continuous stream of requests that shouldn't be occurring, and realized the "links" row of teasers on the front page needed to go away. The default links row includes one reading "Log in to post comments" but this blog doesn't allow anybody else to register for an account, and in any case comments are handled by Disqus rather than Drupal's commenting system. The link didn't need to be there at all, and the more I looked at the links row the more useless it looked.

Categories: Elsewhere


Subscribe to jfhovinne aggregator