Skip to Content


Mediacurrent: Understanding the Role of the Enterprise in Drupal

Planet Drupal - 22 July 2014 - 6:53pm

There is a trending topic I am seeing being discussed a lot more in the open-source software and Drupal community. The point of conversation focuses on what the role should be of enterprise organizations?  Especially, those that are or have already adopted Drupal as their web platform of choice.

Categories: Drupal

Gridforms Lib

New Drupal Modules - 22 July 2014 - 4:22pm

This is a module that is geared toward adding the Gridforms library via the libraries module. Gridforms is an open source jQuery extension with source available on github. Please post any bugs related to Gridforms.js on github.

The Gridforms lib is useful for giving a webform a more appealing look and feel

Categories: Drupal

Harmony Moderation

New Drupal Modules - 22 July 2014 - 1:35pm
Categories: Drupal

Greater Los Angeles Drupal (GLAD): Drupal Migrate using xml 0 to 35

Planet Drupal - 22 July 2014 - 11:55am

Using Drupal Migrate is a great way to move you content into Drupal. Unfortunately the documentation for xml import can be obscure. This comes about when those that developed the module try to communicate how they did what they did to someone that did not do the work. Things that seem obvious to them are not to someone else.

I have spent some time recently importing content using xml. In no way am I an expert that is speeding down the fast lane, something more in the cruising around town at a comfortable 35 mph.

To use Drupal Migrate you need to define your own class. A class is php code that is used in Object Oriented Programming that defines your data and defines how you can manipulate your data. Most of the actual migration work is done with the classes provide by the migrate module, you simply have to define the details of your migration.

Constructor - The constructor modifies the migration modules classes to define your specific data. I was able to follow the SourceList method, this provides one xml (file or feed) that contains the ID number for all the content you want to import, and a second (file or feed) that contains the content. The wine example migrate has this but understanding what it really wants is more difficult to understand.

Below is my class file explained:

* @file
* Vision Article migration.

* Vision Article migration class.
class VisionArticleMigration extends XMLMigration {
public function __construct() {
$this->description = t('XML feed of Ektron Articles.');

So far pretty easy. You need to name your class, extend from the proper migration. and give it an extension.


// There isn't a consistent way to automatically identify appropriate
// "fields" from an XML feed, so we pass an explicit list of source fields.
$fields = array(
'id' => t('ID'),
'lang_type' => t('Language'),
'type' => t('Type'),
'image' => t('Image'),
'authors' => t('Authors'),
'article_category' => t('Article Category'),
'article_series_title' => t('Article Series Title'),
'article_part_no' => t('Article Series Part Number'),
'article_title' => t('Article Title'),
'article_date' => t('Article Date'),
'article_display_date' => t('Article Display Date'),
'article_dropheader' => t('Article Dropheader'),
'article_body' => t('Article Body'),
'article_author_name' => t('Article Author Name'),
'article_author_url' => t('Article Author Email Address'),
'article_authors' => t('Article Additional Authors'),
'article_postscript' => t('Article Postscript'),
'article_link_text' => t('Article Link text'),
'article_link' => t('Article Link'),
'article_image' => t('Article Image'),
'article_image_folder' => t('Article Image Folder'),
'article_image_alt' => t('Article Image Alt'),
'article_image_title' => t('Article Image Title'),
'article_image_caption' => t('Article Image Caption'),
'article_image_credit' => t('Article Image Credit'),
'article_sidebar_element' => t('Article Side Bar Content'),
'article_sidebar_element_margin' => t('Article Margin between Sidebar Content'),
'article_archived_html_content' => t('Article HTML Content from old system'),
'article_video_id' => t('Article ID of Associated Video Article'),
'metadata_title' => t('Metadata Title'),
'metadata_description' => t('Metadata Description'),
'metadata_keywords' => t('Metadata Keywords'),
'metadata_google_sitemap_priority' => t('Metadata Google Sitemap Priority'),
'metadata_google_sitemap_change_frequency' => t('Metadata Google Sitemap Change Freequency'),
'metadata_collection_number' => t('Metadata Collection Number'),
'title' => t('Title'),
'teaser' => t('Teaser'),
'alias' => t('Alias from old system'),
'taxonomy' => t('Taxonomy'),
'created_date' => t('Date Created')

So what doe this mean?
You will need a field name below. It has nothing to do with your xml file, you will need a field for each thing you want to import. Such as article_image_alt is the alt text for the image. Later you will define the xpath to load this variable. This will start to come together below, just remember each unique piece of information needs a variable.


// The source ID here is the one retrieved from the XML listing URL, and
// used to identify the specific item's URL.
$this->map = new MigrateSQLMap($this->machineName,
'ID' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => 'Source ID',

This has to do with setting up the migration table in the database. This has to do with the input database, the Source ID is the field in the input file that has the pointer to the data record. My source file looks like:


So we need a table with a field for the id which an integer.


// Source list URL.
$list_url = '';
// Each ID retrieved from the list URL will be plugged into :id in the
// item URL to fetch the specific objects.
// @todo: Add langtype for importing translated content.
$item_url = '';

// We use the MigrateSourceList class for any source where we obtain the
// list of IDs to process separately from the data for each item. The
// listing and item are represented by separate classes, so for example we
// could replace the XML listing with a file directory listing, or the XML
// item with a JSON item.
$this->source = new MigrateSourceList(new MigrateListXML($list_url),
new MigrateItemXML($item_url), $fields);

$this->destination = new MigrateDestinationNode('vision_article');


Now we are setting up the magic. We setup a list url that contains the ID's of all the content to import, then another one that uses this ID to fetch the details for this ID. Then you tell Migrate to use the MigrateListXML to find the items to import with MigrateItemXML. Then finally in the MigrateDestinationNode to tell Migrate which content type to use. This means we need a separate migration class for each content type to import. I have been creating each class in it's own inc file and adding this to the files section in the info file.


// TIP: Note that for XML sources, in addition to the source field passed to
// addFieldMapping (the name under which it will be saved in the data row
// passed through the migration process) we specify the Xpath used to retrieve
// the value from the XML.
$this->addFieldMapping('created', 'created_date')

Now we map the source field with the destination field. Created is the field name in the content type (vision_article), created_date is from our fields section above. Remember I said we needed a definiation for each part of the content we want to import. The xpath then points to the data in the xml feed. So this says take the content of the /contnet/CreateDate in the xml file and load this into the source variable created_date, then store this in the created field in a new vision_article content item. I say this in this way because if you do like me and cut and paste and forget to change the source varable, the source varable will contain the bottom data from xpath.


$this->addFieldMapping('field_category', 'article_category')


You can set a default value in case the xml does not contain any data


$this->addFieldMapping('field_series_title', 'article_series_title')
$this->addFieldMapping('field_part_number', 'article_part_no')
$this->addFieldMapping('field_h1_title', 'article_title')
->arguments(array('format' => 'filtered_html'))
$this->addFieldMapping('field_display_date', 'article_display_date')
$this->addFieldMapping('field_drophead', 'article_dropheader')
->arguments(array('format' => 'filtered_html'))


Another field argument, the default content type is plain text, so if your content contains HTML you need to set the correct format here.


$this->addFieldMapping('body', 'article_body')
->arguments(array('format' => 'filtered_html'))
$this->addFieldMapping('body:summary', 'teaser')
->arguments(array('format' => 'filtered_html'))


Note you can set the teaser as a part of the body. One of the drush migrate commands make is easy to discover the additional parts of your content field, drush mfd (Migrate Field Destinations). This will display all the destination fields and their options.


$this->addFieldMapping('field_author', 'article_author_email')
$this->addFieldMapping('field_author:title', 'article_author_name')
$this->addFieldMapping('field_ext_reference_title', 'article_postscript')
->arguments(array('format' => 'filtered_html'))

see explanation below
->defaultValue(MigrateFile::FILE_EXISTS_REUSE); //FILE_EXISTS_REUSE is in the MigrateFile class
$this->addFieldMapping('field_article_images', 'article_image')
$this->addFieldMapping('field_article_images:source_dir', 'article_image_folder')
$this->addFieldMapping('field_article_images:alt', 'article_image_alt')
$this->addFieldMapping('field_article_images:title', 'article_image_title')


This section gets tricky. You are importing an Image or other file. The default migration for a file is MigrateFileUrl. You can migrate all your files ahead of time or as I am doing do it inline. The main components for this is the main field, which is the file name, and the source_dir for the path to this image. Drual 7 has a database table for the files is uses with the url to the file. MigrateFile then uploads this file to the public folder and creates an entry into the files_,amaged table to indicate the url. What I did was copy all the images to a public location on S3 storage so I did not want Migrate to create a new file but use the existing file. Thus the file_replace setting to the constant MigrateFile::FILE_EXISTS_REUSE. This tells migrate to use the existing file and make an entry in the file_managed table for this file.

Later in the PrepareRow method I will show how we separate this and add it to the xml.


$this->addFieldMapping('field_archive', 'article_archived_html_content')
$this->addFieldMapping('field_ektron_id', 'id')
$this->addFieldMapping('field_ektron_alias', 'alias')
$this->addFieldMapping('field_sidebar', 'article_sidebar_element')
->arguments(array('format' => 'filtered_html'))
->defaultValue(MigrateFile::FILE_EXISTS_REUSE); //FILE_EXISTS_REUSE is in the MigrateFile class
$this->addFieldMapping('field_slider_image', 'image')
$this->addFieldMapping('field_slider_image:source_dir', 'image_folder')
$this->addFieldMapping('field_slider_image:alt', 'image_alt')
$this->addFieldMapping('field_slider_image:title', 'image_title')
$this->addFieldMapping('title', 'title')
$this->addFieldMapping('title_field', 'title')

// Declare unmapped source fields.
$unmapped_sources = array(



If you are not using a source field, best practices state that you declare it in the unmapped sources



// Declare unmapped destination fields.
$unmapped_destinations = array(


If you are not using a destination field best practices state that you declare in the unmaped destinations array. Note if you later use this field you need to remove it from the unused array.



if (module_exists('path')) {
if (module_exists('pathauto')) {
if (module_exists('statistics')) {
$this->addUnmigratedDestinations(array('totalcount', 'daycount', 'timestamp'));


The rest of the constructor is from the example. Did not cause me a problem so did not worry about it.

* {@inheritdoc}


Now we can add our own magic. We can effect the data from the content item before it is saved in to the content item.


public function prepareRow($row) {
if (parent::prepareRow($row) === FALSE) {
return FALSE;
$ctype = (string)$row->xml->Type;
//set variable for return code
$ret = FALSE;


You will see these scattered through the prepareRow function. These are the devel command to print to the screen for debuging. They should be commented out but you can see the process I went through to debug my particular prepareRow. Also note this is a great use of the Migrate UI, these print statment only help you in the web interface, if you use Drush you will not see these diagnostic prints.


if ($ctype == '12'){


This is specific to my migrate. The following code is only applicable to a content type of 12. The other content types have a different data structure. If prepareRow returns False the row will be skipped.


// Map the article_postscript source field to the new destination fields.
//if((string)$row->xml->root->article->Title == ''){
// $row->xml->root->article->Title = $row->xml->root->Title;
$postscript = $row->xml->html->root->article->Postscript->asXML();
$postscript = str_replace('','',$postscript);
$postscript = str_replace('','',$postscript);
$row->xml->html->root->article->Postscript = $postscript;


Again this is something unique to my migrate. The content structure is contained in xml so the HTML is recognized by SimpleXML as xml. So the asXML() function returns a string containing the xml of the node. Now I can save this string to the node and it becomes a string node and is back to straight html. So I need to do this for all the nodes that contain html. Most of the time you will be able to pass the html string as a node and will not have to do this transform.


//converts html nodes to string so they will load.
$body = $row->xml->html->root->article->Body->asXML();
$body = str_replace('','',$body);
$body = str_replace('','',$body);
$row->xml->html->root->article->Body = $body;
$title = $row->xml->html->root->article->Title->asXML();
$title = str_replace('','',$title);
$title = str_replace('','',$title);
$row->xml->html->root->article->Title = $title;
$drophead = $row->xml->html->root->article->Dropheader->asXML();
$drophead = str_replace('','',$drophead);
$drophead = str_replace('','',$drophead);
//If Dropheader is empty
$drophead = str_replace('','',$drophead);
$row->xml->html->root->article->Dropheader = $drophead;
//Array to allow conversion of Category text to IS
$cat_tax = array(
'Science and Environment' => 1,
'History' => 2,
'Social Issues' => 3,
'Family and Relationships' => 4,
'Life and Health' => 5,
'Religion and Spirituality' => 6,
'Biography' => 7,
'Ethics and Morality'=> 8,
'Society and Culture' => 9,
'Current Events and Politics' => 10,
'Philosophy and Ideas' => 11,
'Personal Development' => 12,
'Reviews' => 13,
'From the Publisher' => 14,
'Interviews' => 17,
//Convert additional taxonomies to tags
//$tax_id_in = (string)$row->xml->Taxonomy;
//$tax_id_array = explode(',',$tax_id_in);
//$tax_in_array = array();
//foreach($tax_id_array as $tax){
// If(is_null($cat_tax[tax]))
// $tax_in_array[] = $cat_tax[$tax];
//$new_tax = implode(',',$tax_in_array);
//$row->xml->Taxomomy = $new_tax;
// Change category text to ID
$category = (string)$row->xml->html->root->article->Category;
//Specify unknown category if we do not recognize the category
//This allows the migrate and allow us to fix later.
$tax_cat = $cat_tax[trim($category)];
if(is_null($tax_cat)) {$tax_cat = 18;}
$row->xml->html->root->article->Category = $tax_cat;


The category field in the source is a text field. The categories are a entity reference to a taxonomy field, which requires an id rather than text. I manually setup the categories ahead of time so I created an array that has the text as the key and the is as the content. Then you can use this to quickly look up the id for the text in he category field. Then we can replace the text in Category with the id. This works, another way to do this is migrate the categories first then use this migration to translate this for you. This is a feature built into migrate. The explanation of this will come later.


//modify the image file node.
if((string)$row->xml->html->root->article->Image->File->asXML() != ''){
$src = (string)$row->xml->html->root->article->Image->File->img->attributes()->src;
$src_new = str_replace('/visionmedia/uploadedImages/','',$src);
$row->xml->html->root->article->Image->File->img->attributes()->src = $src_new;
$file_name = basename($src_new);
$file_path = rtrim(str_replace($file_name,'', $src_new), '/');;


There is alot of stuff here. Remember for the MigrateFile you need to present the file name and source directory. The Image/File node contains an img tag. So we need to get the scr attribute and extract the file name and source directory. So why the if? Migrate will import a null node as null, but this is php code running on the row. If you try to get the src attribute on a null node it will throw an error. So the if statement checks to see if the File node is empty (only contains /File) and skips this tranformation, Migrate will simply import a null or empty field.

The src is the relative path to the website, so the first thing we do is change this to full url to the s3 content storage. The path is basically the same except in the uploadedimages the i in the database is uppercase. This was a Windows server so it did not make a difference but the s3 url is case sensitive. We then use base name to extract the file name and use this to remove the file from the path for the file path and create a new child in the xml row to store these. I did not point this out but this is the xpath use in the field mapping above.


$email = (string)$row->xml->html->root->article->AuthorURL;
if (!empty($email)){
$email = 'mailto:'.$email;
$row->xml->html->root->article->AuthorURL = $email;


The author url is the email to the author of the article. We turn this into a mailto link so that it will generate a link to send the author an email.


$archive_html = (string)$row->xml->html->asXML();
$sidebar_element = (string)$row->xml->html->root->article->SidebarElement->SidebarElementInformation->asXML();
$row->xml->html->root->article->SidebarElement->SidebarElementInformation = $sidebar_element;
$slider_src = (string)$row->xml->Image;
$slider_src_new = str_replace('/visionmedia/uploadedImages/','',$slider_src);
$row->xml->Image = $slider_src_new;
$slider_file_name = basename($slider_src_new);
$slider_file_path = rtrim(str_replace($slider_file_name,'', $slider_src_new), '/');;


The rest is repitition of the above techniques. Note that we return TRUE if we want to process the row and false if we do not want to process the row.


//Need to add processing for other Article Content types especially 0 (HTML content)
return $ret;



This is the class I use for one of the imports. I told you that I would show the use of another migrate in the field mappings. Below is a snippet of code from the issues migration. The issue contains entity reference to vision_articles that were imported from above.


$this->addFieldMapping('field_articles', 'article_id')


So this says use the VisionArticle (I will show you were to find this next), it knows to look up the source ID and relate it to the DestinationID and store this in the field_articles field.


Migrate has been around for a while. Initially they said that the class would automaticall be registed and you could manually register them if needed. Then they changed to say that they will not manually register and you should register your classes. So you should have as part of your migration module the following that will register your classes. Note the name of the array element is the name used above.


function vision_migrate_migrate_api() {
$api = array(
'api' => 2,
// Give the group a human readable title.
'groups' => array(
'vision' => array(
'title' => t('Vision'),
'migrations' => array(
'VisionArticle' => array('class_name' => 'VisionArticleMigration'),
'VisionIssue' => array('class_name' => 'VisionIssueMigration'),
'VisionVideoArticle' => array('class_name' => 'VisionVideoArticleMigration'),
'VisionFrontpage' => array('class_name' => 'VisionFrontpageMigration'),

return $api;


I hope this makes things a little easier to understand. You will need some basic module building skills, knowing the file names and things like that, but this should help you through the more obscure parts of creating your migration class.

Tags: Planet Drupal
Categories: Drupal

Drupal Association News: Why we moved to a CDN

Planet Drupal - 22 July 2014 - 10:54am

As of a little after 19:00 UTC on 2 July 2014, is now delivering as many sites as possible via our EdgeCast CDN.

Why a CDN?

We are primarily concerned with the network level security that a CDN will provide

The CDN enables us to restrict access to our origin servers and disallow directly connecting to origin web nodes (which is currently possible). The two big advantages are:

  1. Accelerate cacheable content (static assets, static pages, etc).
  2. Allow us to easily manage network access and have a very large network in front of ours to absorb some levels of attacks.

Here are some examples of how the CDN helps

  • We were having issues with a .js file on The network was having routing issues to Europe and people were complaining about stalling on page loads. There was basically nothing we could do but wait for the route to get better. This should never be a problem again with EdgeCast's global network.
  • We constantly have reports of because blacklisted because it serves a ton of traffic coming in and out of a small number of IP addresses. This should also not happen again because the traffic is distributed through EdgeCast's network.
  • A few months ago we were under consistent attack from a group of IPs that was sub-HTTP and was saturating the origin network's bandwidth. We now have EdgeCast's large network in front of us that can 'take the beating'.

By enabling EdgeCast's raw logs, rsync, and caching features, we were able to offload roughly 25 Mbps of traffic from our origin servers to EdgeCast. This change resulted in a drastic drop in origin network traffic, which freed up resources for The use of rsync and the raw log features of EdgeCast enabled us to continue using our current project usage statistics tools. We do this by syncing the access logs from EdgeCast to’s utility server that processes project usage statistics.

Minutes after switching to use the CDN, there were multiple reports of faster page load times from Europe and North America.

A quick check from France /
Pre-CDN results: first page load=4.387s. repeat view=2.155s
Post-CDN results: first page load=3.779s, repeat view=1.285s

Why was the rename required?

Our CDN uses a combination of Anycast IP addresses and DNS trickery. Each region (Asia, North America, Europe, etc.) has an Anycast IP address associated with it. For example might resolve to in North America, and in Japan.

Since,, etc. are Anycast IPs, generally their routes are as short as possible, and the IP will route to whatever POP is closest. This improves network performance globally.

Why can't be a CNAME?

The DNS trickery above works by using a CNAME DNS record. must be an A record because the root domain cannot be a CNAME. MX records and any other records are not allowed by the RFC on CNAME records. To work around this DNS limitation, URLs are now redirected to



Related issues

Categories: Drupal

Stanford Web Services Blog: Cherry Picking - Small Git lesson

Planet Drupal - 22 July 2014 - 9:56am

Small commits allow for big wins.

Something that I have been using a lot lately is GIT's cherry pick command. I find the command very usefull and it saves me bunches of time. Here is a quick lesson on what it does and an example use case.

What is GIT cherry-pick? man page

Git cherry pick allows you to merge a single commit from one branch into another.  To use the cherry pick command follow these steps:

Categories: Drupal

The MOBA Market Expansion - by Ulyana Chernyak Blogs - 22 July 2014 - 9:45am
MOBAs have been rising in popularity over recent years and today's post examines it's growth and things to consider if you are thinking of working on one in the near future.
Categories: Game Theory & Design

Rovio and Twitch join the speaker lineup for GDC Europe 2014

Social/Online Games - Gamasutra - 22 July 2014 - 9:06am

European experts from Rovio and Twitch join the lineup of speakers exploring the art and business of games at GDC Europe 2014 this August in Cologne. ...

Categories: Game Theory & Design

Five nightmares of a modern Game Designer - by Svyatoslav Torick Blogs - 22 July 2014 - 8:15am
The Game Designer is the only person who is utterly and completely in charge of the game experience, so to make the impressions seamless and thorough they have to meet compromises and make sacrifices. Here are five basic working issues we experience.
Categories: Game Theory & Design

2bits: Improve Your Drupal Site Performance While Reducing Your Hosting Costs

Planet Drupal - 22 July 2014 - 8:00am
We were recently apporached by a non-profit site that runs on Drupal. Major Complains Their major complaint was that the "content on the site does not show up". The other main complain is that the site is very slow. Diagnosis First ... In order to troubleshoot the disappearing content, we created a copy of the site in our lab, and proceeded to test it, to see if we can replicate the issues.

read more

Categories: Drupal

Entity Reference Prepopulate Token

New Drupal Modules - 22 July 2014 - 7:49am

This module supplements Entityreference prepopulate module by adding prepopulate provider which sets default field values using tokens and replacement patterns of panel pane contexts.

Categories: Drupal

Invite User Relationships

New Drupal Modules - 22 July 2014 - 7:04am

Invite User Relationships integrates the Invite with the User Relationships module and allows users to specify a relationship to a user that they invite to join the site.

Categories: Drupal

The Hard Lesson of Games Workshop - by Mark Slabinski Blogs - 22 July 2014 - 6:33am
This blog deals with the tabletop gaming company Games workshop and the predicament they have found themselves in with their players. In their story is a lesson all studios should take to heart.
Categories: Game Theory & Design

Drupalize.Me: Drupal 8 Has All the Hotness, but So Can Drupal 7

Planet Drupal - 22 July 2014 - 6:30am

Drupal 8 is moving along at a steady pace, but not as quickly as we all had hoped. One great advantage this has is it gives developers time to backport lots of the features Drupal 8 has in core as modules for Drupal 7. My inspiration and blatant rip-off for this blog came from the presentation fellow Lullabot Dave Reid did at Drupalcon Austin about how to Future-Proof Your Drupal 7 Site. Dave’s presentation was more about what you can do to make your Drupal 7 “ready” where this article is more about showing off Drupal 8 “hotness” that we can use in production today.

Categories: Drupal

FULL CIRCLE: Videogame Music & All That Jazz - by John Broomhall Blogs - 22 July 2014 - 6:27am
Reflections on re-imagining the distinctive soundtrack of a classic videogame
Categories: Game Theory & Design

Castles and Crusades F5 A Shattered Night

New RPG Product Reviews - 22 July 2014 - 6:11am
Publisher: Troll Lord Games
Rating: 4
Originally published at:

A Shattered Night is the latest adventure in the “F” series for Castles and Crusades. These adventures take place in a fantasy version of Post-Roman Britain. This includes adventures like The Goblins of Mount Shadow, The Crimson Pact and of course, To Kill a King, the winner of last years “Best Adventure (Solo)” award.

A Shattered Night is a pretty open ended affair where the PCs attempt to save a kidnapped princesses from an unsavory Saxon prince. The adventure is designed for two to players whose characters range between levels 4-6. I think a Rogue, Druid, Ranger and Assassin would all be quite helpful in this piece as the adventure is one that is as much stealth as it is combat, and much of the adventure takes place in the woods or outside. The adventure is divided into three parts: getting to the princess, storming the location where the princess is held, and then saving the princess. The first part is mostly left up to the CK. Random encounters and some specific events are provided in the adventure, but the Castle Keeper will really need to flesh things out as well as have a plan in place for how they want this first act to flow. In this first act you might encounter everything from a friendly giant to an ally who just seems to make a mess of all the PCs well-laid plans. You might even encounter a certain Nordic All-Father. There is definitely a lot of good stuff here, but you’ll want a more experienced Keeper to connect the dots between all these pieces.

Act 2 is essentially a very straight-forward dungeon crawl. Of course, the dungeon is actually a well-stocked and heavily guarded castle complete with 100 well trained soldiers and a powerful witch, so this is again where stealth is just as important as NPC slaughter for A Shattered Night. There are a lot of magic items to be found here, but there are also a lot of monsters or enemy soldiers looks to disembowel you, so risk vs reward is a huge part of this section of the adventure. The third act is saving the princess and escaping enemy territory. There is a bit of a plot twist here, but nothing super shocking or “Vince Russo-Esque.” If players can abscond back to the country that hired them, riches, fame and reward await them. If they can’t…well, they will probably die horribly.

The adventure feels a bit short and is not something you want to give to a new or inexperienced C and C Keeper, but it is a very fun one. There are many ways to tackle this adventure. We’ve already covered stealth and straight forward combat, but you’d be surprised how good the gift of gab can come in with this adventure. A highly charismatic PC with some lucky rolls could actually make it so the adventures have to do little if any of the messier side of A Shattered Night. Players will also walk away from this adventure with some potential new allies and/or various enemies who will be seeking revenge on them soon. With several dangling plot threads in this piece, an enterprising Keeper can definitely spin this off in several different ways.

Besides the adventure in its own right, A Shattered Night also gives us a slight preview of the upcoming Codex Germania, which should delight fans of Codex Celtarum and Codex Nordica. There are also six new monsters provided in the back of this adventure ranging from a shape-shifting cat demon that can shoot vampiric butterflies out of its mouth to a two headed hell hound. All of these monsters are fine additions to the Castles and Crusades rogue’s gallery.

In all A Shattered Night is fine adventure. You don’t have to be familiar with any of the previous F series adventures to understand or enjoy this one. It works just fine as a standalone or as part of a continuing campaign utilizing the full series. About the only real complaint I have about the piece is the territorial map on page seven is exceptionally blurry and you can’t really make out the words on it. This is a very minor issue though. Other than that this adventure is a short affair that should keep you and your friends busy for a session or two. It’s not the most memorable Castles and Crusades adventure, but it is an entertaining one, that you will definitely get your money’s worth out of.
Categories: Game Theory & Design

Views Panes More Link

New Drupal Modules - 22 July 2014 - 6:11am

Adding the ability to define a custom "More Link" link for View Panes.

Just go to the view pane settings and you will find a new fieldset which will allow you to define the new custom "More Link".

This module is sponsored and developed by Vardot.

Categories: Drupal

Drupal Easy: DrupalEasy Podcast 135: Deltron 3030 (Ronan Dowling, Backup and Migrate 3.0)

Planet Drupal - 22 July 2014 - 6:09am
Download Podcast 135

Ronan Dowling (ronan), lead developer at Gorton Studios joins Ted and Mike to talk about all the new features in Backup and Migrate 3.0 including file and code backup and a improved plugin architecture. We also get up-to-speed with Drupal 8 development, review some Drupal-y statistics, make our picks of the week, and ask Ronan 5-ish questions.

read more

Categories: Drupal

Getting/Making Game Music that Fits - World Tour Series - Beach Music - by Harry Mack Blogs - 22 July 2014 - 6:04am
Tips for new audio designers composing video game music out of their comfort zone. Useful for producers as well, looking to put together design directions for their audio designers. This entry focuses on Beach music.
Categories: Game Theory & Design
Syndicate content

about seo