The reason I created OpenMusicFestival in the first place was so that I could migrate MotionNotion.com from WordPress (And its unsupported WordTour plugin) to Drupal 7.

While I suspect my original use of WordTour for a music festival was slightly weird (The system was designed for small record labels, but relationship between artists, events and venues made it work for my purposes), I’m releasing my Artist migration class in case somebody finds it useful and wants to migrate to OpenMusicFestival. Note that this only migrates Artists — the Event and Venues parts are incomplete (On that note, if somebody wants to do those, I’ll happily both give you credit on the project as well as include the code with OMF.).

migrate_wordtour.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
<?php
/**
 * @file
 *  Migrate content from WordTour (WP) to OMF (Drupal)
 *
 *  NOTE: THIS IS WOEFULLY INCOMPLETE. I would *love* to give somebody credit
 *  for tidying up and completing this.
 */

class WordTourMigration extends Migration {
  public $wp_prefix = 'wp_'; //This is the entire first part of the table name.
  public $wp_db = '';
  public $wp_user = '';
  public $wp_pass = '';
  public $wp_host = 'localhost';

  public function __construct() {
    // Always call the parent constructor first for basic setup
    parent::__construct();

    // With migrate_ui enabled, migration pages will indicate people involved in
    // the particular migration, with their role and contact info. We default the
    // list in the shared class; it can be overridden for specific migrations.
    $this->team = array(
      new MigrateTeamMember('Ændrew Rininsland', 'aendrew@aendrew.com', t('Developer')),
    );

    // Individual mappings in a migration can be linked to a ticket or issue
    // in an external tracking system. Define the URL pattern here in the shared
    // class with ':id:' representing the position of the issue number, then add
    // ->issueNumber(1234) to a mapping.
    $this->issuePattern = 'http://drupal.org/node/:id:';
  }
}

/**
 * There are four essential components to set up in your constructor:
 *  $this->source - An instance of a class derived from MigrateSource, this
 *    will feed data to the migration.
 *  $this->destination - An instance of a class derived from MigrateDestination,
 *    this will receive data that originated from the source and has been mapped
 *    by the Migration class, and create Drupal objects.
 *  $this->map - An instance of a class derived from MigrateMap, this will keep
 *    track of which source items have been imported and what destination objects
 *    they map to.
 *  Mappings - Use $this->addFieldMapping to tell the Migration class what source
 *    fields correspond to what destination fields, and additional information
 *    associated with the mappings.
 */
class ArtistMigration extends WordTourMigration {
  public function __construct() {
    parent::__construct();

    //Set up other database
    Database::addConnectionInfo('wp', 'default', array(
          'driver' => 'mysql',
          'database' => $this->wp_db,
          'username' => $this->wp_user,
          'password' => $this->wp_pass,
          'host' => $this->wp_host,
          'prefix' => $this->wp_prefix,
        ));

    $this->description = t('Migrate the artists!');

    $this->map = new MigrateSQLMap($this->machineName,
        array(
          'artist_id' => array(
                           'type' => 'int',
                           'length' => 7,
                           'not null' => TRUE,
                           'description' => 'Artist ID',
                          )
        ),
        MigrateDestinationNode::getKeySchema()
      );

    $query = Database::getConnection('default', 'wp')
           ->select('wtr_artists', 'a');
    $query->join('wtr_attachment', 'at', 'a.artist_id = at.attachment_target_id');
    $query->join('wtr_attachment', 'att', 'a.artist_id = att.attachment_target_id');
    $query->join('postmeta', 'pm', 'pm.post_id = att.attachment_type_id');
    $query->fields('a',
              array(
                  'artist_id',
                  'artist_name',
                  'artist_publish_date',
                  'artist_bio',
                  'artist_record_company',
                  'artist_social_links',
              )
            );
    //$query->fields('att', array('attachment_info'));
    $query->fields('pm', array('meta_value'));
    //$query->addField('pm', 'meta_value', 'photo');
    $query->addExpression('GROUP_CONCAT(DISTINCT at.attachment_info)', 'genres'); //Pull in genres.
    $query->condition('at.attachment_target', 'artist');
    $query->condition('at.attachment_type', 'genre');
    $query->condition('att.attachment_type', 'thumbnail');
    $query->condition('pm.meta_key', '_wp_attached_file');
    $query->groupBy('a.artist_id');


    // Create a MigrateSource object, which manages retrieving the input data.
    $this->source = new MigrateSourceSQL($query, array(), NULL, array('map_joinable' => FALSE));

    // Set up our destination
    $this->destination = new MigrateDestinationNode('artist', array('text_format' => 'full_html'));

    // Assign mappings TO destination fields FROM source fields.
    $this->addFieldMapping('title', 'artist_name');
    $this->addFieldMapping('uid')
         ->defaultValue(1);
    $this->addFieldMapping('changed', 'artist_publish_date');
    $this->addFieldMapping('status')
         ->defaultValue(1);
    $this->addFieldMapping('promote', '')
         ->defaultValue(0);
    $this->addFieldMapping('sticky', '')
         ->defaultValue(0);
    $this->addFieldMapping('revision')
         ->defaultValue(0);
    $this->addFieldMapping('log')
         ->defaultValue('Migrated to Drupal 7 from WordPress.');
    $this->addFieldMapping('comment')
         ->defaultValue(1);
    $this->addFieldMapping('body', 'artist_bio')
          ->arguments(array('format' => 'full_html'))
          ->description('See prepareRow()');
    $this->addFieldMapping('created', 'artist_publish_date');
    $this->addFieldMapping('field_labels', 'artist_record_company')
          ->separator(', ')
          ->arguments(array('create_term' => true));
    //$this->addFieldMapping('path', '');
    //$this->addFieldMapping('pathauto', '');
    $this->addFieldMapping('field_photo', 'meta_value');
    $this->addFieldMapping('field_photo:source_dir')
         ->defaultValue('/Users/aendrew/Sites/mn_wp');
    $this->addFieldMapping('field_photo:preserve_files')
         ->defaultValue(true);
    $this->addFieldMapping('field_photo:destination_file', 'meta_value');
    $this->addFieldMapping('field_photo:file_replace')
         ->defaultValue(MigrateFile::FILE_EXISTS_REUSE);
    $this->addFieldMapping('field_photo:alt', 'artist_name');
    $this->addFieldMapping('field_photo:title', 'artist_name');
    $this->addFieldMapping('field_links', 'artist_social_links')
         ->description('See prepare()'); //Needs to be unserialized
    $this->addFieldMapping('field_genres', 'genres')
          ->separator(',')
          ->arguments(array('create_term' => true));
  }

  public function prepareRow($row) {
    //Prepare Thumbnails
    $thumb = trim($row->meta_value);
    //watchdog('migrate', 'Filename is ' . $thumb);
    $row->meta_value = 'wp-content/uploads/' . $thumb;
  }

  public function prepare(stdClass $node, stdClass $row) {
    //Set up the links
    $links = unserialize($row->artist_social_links);
    $empty = 'a:10:{s:13:"artist_flickr";s:0:"";s:14:"artist_youtube";s:0:"";s:12:"artist_vimeo";s:0:"";s:15:"artist_facebook";s:0:"";s:14:"artist_twitter";s:0:"";s:13:"artist_lastfm";s:0:"";s:14:"artist_myspace";s:0:"";s:15:"artist_bandcamp";s:0:"";s:13:"artist_tumblr";s:0:"";s:19:"artist_reverbnation";s:0:"";}';
    if ($node->field_links[LANGUAGE_NONE][0]['url'] == $empty) {
      unset($node->field_links);
    } else {
      $i = 0;
      foreach ($links as $site => $link) {
        if (!empty($link)) {
          $site_name = ucfirst(str_replace('artist_', '', $site));
          if ($site_name == "Youtube") $site_name = "YouTube";
          if ($site_name == "Lastfm") $site_name = "Last.fm";
          if ($site_name == "Youtube") $site_name = "YouTube";
          if ($site_name == "Myspace") $site_name = "MySpace";
          $node->field_links[LANGUAGE_NONE][$i]['title'] = $site_name;
          $node->field_links[LANGUAGE_NONE][$i]['url'] = urldecode($link);
          $i++;
        }
      }
    }
  }
}

Did this help you out? Have I saved you a tonne of time? Please leave me a comment letting me know!