Browse Source

Issue #0 by fgm: added UnfuddleTicketFollowup migration

- can now import follow-ups on tickets
- still very crude, no attachments
Frederic G. MARAND 12 years ago
parent
commit
f038250106
4 changed files with 491 additions and 145 deletions
  1. 275 144
      UnfuddleMigration.inc
  2. 4 0
      migrateunfuddle.info
  3. 38 1
      migrateunfuddle.module
  4. 174 0
      views/childless.view.inc

+ 275 - 144
UnfuddleMigration.inc

@@ -8,18 +8,128 @@
  * @license Licensed under the General Public License version 2 and later, and the CeCILL 2.0 license.
  */
 
+/**
+ * Information/utility class about CaseTracker in a Migrate context.
+ */
+class CaseTrackerMeta {
+  public static function getFields() {
+    $fields = array(
+      'pid'              => t('Case project'),
+      'case_number'      => t('Case number'),
+      'assign_to'        => t('Case assigned to'),
+      'case_priority_id' => t('Case priority'),
+      'case_type_id'     => t('Case type'),
+      'case_status_id'   => t('Case status'),
+    );
+    return $fields;
+  }
+}
+
+class MigrateDestinationCommentFollowup extends MigrateDestinationComment {
+
+  public function __construct() {
+    parent::__construct(MigrateDestinationNodeCase::ct_bundle);
+  }
+
+  /**
+   * Could also be implemented as a migrate hook_migrate_fields() in module.
+   * Which is best ?
+   *
+   * (non-PHPdoc)
+   * @see MigrateDestinationComment::fields()
+   */
+  public function fields() {
+    $fields = parent::fields();
+    $fields['revision_id'] = t('Ticket revision ID');
+    $fields += CaseTrackerMeta::getFields();
+    return $fields;
+  }
+
+  public function prepare($entity, stdClass $source_row) {
+    $ticket = node_load($entity->nid);
+
+    // ticket->casetracker is assigned by CT to both new and old, so clone is
+    // required, to avoid overwriting one state with the other
+    $entity->casetracker = clone $ticket->casetracker;
+
+    // CT needs a name in assign_to, not an uid
+    $entity->casetracker->assign_to = casetracker_get_name($entity->casetracker->assign_to);
+    //print_r($entity);
+
+    $entity->revision_id = $ticket->vid;
+  }
+}
+
+/**
+ * Casetracker-specific destination node handling
+ */
+class MigrateDestinationNodeCase extends MigrateDestinationNode {
+  const ct_bundle = 'casetracker_basic_case';
+
+  public function __construct() {
+    parent::__construct(self::ct_bundle);
+  }
+
+  /**
+   * Could also be implemented as a migrate hook_migrate_fields() in module.
+   * Which is best ?
+   *
+   * (non-PHPdoc)
+   * @see MigrateDestinationNode::fields()
+   */
+  public function fields() {
+    $fields = parent::fields();
+    $fields += CaseTrackerMeta::getFields();
+    return $fields;
+  }
+
+  /**
+   * Casetracker saves its fields in a separate stdClass extra field.
+   *
+   * (non-PHPdoc)
+   * @see MigrateDestinationEntity::prepare()
+   */
+  public function prepare($entity, stdClass $source_row) {
+    $extraFields = array(
+      'pid',
+      'case_priority_id',
+      'case_type_id',
+      'assign_to',
+      'case_status_id',
+    );
+
+    $entity->casetracker = new stdClass();
+    foreach ($extraFields as $extra) {
+      if (isset($entity->$extra)) {
+        $entity->casetracker->$extra = $entity->$extra;
+        unset($entity->extra);
+      }
+    }
+
+    $entity->casetracker->case_type_id = 9; // Bug. Case type is not a default Unfuddle field
+  }
+}
+
 abstract class UnfuddleMigration extends XMLMigration {
-  public $items_url;
+  protected static $fields = array();
   public $item_ID_xpath = 'id';
+  public $items_url;
   public $unmigratedDestinations = array();
   public $unmigratedSources = array();
-  protected static $fields = array();
 
   public function __construct($group) {
     parent::__construct($group);
     $this->items_url = file_directory_path() . '/unfuddle/backup.xml';
   }
 
+  public function addXPathFieldMapping($destination_field, $source_field, $xpath = NULL) {
+    if (!isset($xpath)) {
+      $xpath = $source_field;
+    }
+    return $this->addFieldMapping($destination_field, $source_field)
+      ->xpath($xpath);
+  }
+
   public function afterConstruct() {
     $this->addUnmigratedDestinations($this->unmigratedDestinations, t('White hole'));
     $this->addUnmigratedSources($this->unmigratedSources, t('Black hole'));
@@ -34,13 +144,6 @@ abstract class UnfuddleMigration extends XMLMigration {
     return self::$fields;
   }
 
-  public function addXPathFieldMapping($destination_field, $source_field, $xpath = NULL) {
-    if (!isset($xpath)) {
-      $xpath = $source_field;
-    }
-    return $this->addFieldMapping($destination_field, $source_field)
-      ->xpath($xpath);
-  }
 }
 
 /**
@@ -75,35 +178,6 @@ class UnfuddlePeopleMigration extends UnfuddleMigration {
     'updated-at',
   );
 
-  protected static function getFieldInfo() {
-    self::$fields = array(
-      'account-id'                    => t('Unfuddle account Id'),
-      'id'                            => t('User account ID'),
-      'created-at'                    => t('User account creation timestamp'),
-      'email'                         => t('Current email address for the account'),
-      'first-name'                    => t('User given name'),
-      'identity-url'                  => t('OpenID URL for the user account'),
-      'is-administrator'              => t('User is an Unfuddle project administrator'),
-      'is-removed'                    => t('User account has been removed'),
-      'last-name'                     => t('User last name'),
-      'last-signed-in'                => t('Latest login timestamp'),
-      'notification-frequency'        => t('Unfuddle notification frequency'),
-      'notification-ignore-self'      => t('Ignore self when sending notifications'),
-      'notification-last-sent'        => t('Timestamp of latest notification sent'),
-      'notification-scope-messages'   => t('Send message notifications'),
-      'notification-scope-milestones' => t('Send milestones notifications'),
-      'notification-scope-notebooks'  => t('Send notebooks notifications'),
-      'notification-scope-source'     => t('Send source notifications'),
-      'notification-scope-tickets'    => t('Send tickets notifications'),
-      'text-markup'                   => t('Preferred markup format for issues'),
-      'time-zone'                     => t('Timezone'),
-      'updated-at'                    => t('Timestamp of latest user account change'),
-      'username'                      => t('The user account login name'),
-    );
-
-    return parent::getFieldInfo();
-  }
-
   public function __construct() {
     parent::__construct(MigrateGroup::getInstance('Unfuddle'));
 
@@ -145,6 +219,35 @@ class UnfuddlePeopleMigration extends UnfuddleMigration {
     $this->afterConstruct();
   }
 
+  protected static function getFieldInfo() {
+    self::$fields = array(
+      'account-id'                    => t('Unfuddle account Id'),
+      'id'                            => t('User account ID'),
+      'created-at'                    => t('User account creation timestamp'),
+      'email'                         => t('Current email address for the account'),
+      'first-name'                    => t('User given name'),
+      'identity-url'                  => t('OpenID URL for the user account'),
+      'is-administrator'              => t('User is an Unfuddle project administrator'),
+      'is-removed'                    => t('User account has been removed'),
+      'last-name'                     => t('User last name'),
+      'last-signed-in'                => t('Latest login timestamp'),
+      'notification-frequency'        => t('Unfuddle notification frequency'),
+      'notification-ignore-self'      => t('Ignore self when sending notifications'),
+      'notification-last-sent'        => t('Timestamp of latest notification sent'),
+      'notification-scope-messages'   => t('Send message notifications'),
+      'notification-scope-milestones' => t('Send milestones notifications'),
+      'notification-scope-notebooks'  => t('Send notebooks notifications'),
+      'notification-scope-source'     => t('Send source notifications'),
+      'notification-scope-tickets'    => t('Send tickets notifications'),
+      'text-markup'                   => t('Preferred markup format for issues'),
+      'time-zone'                     => t('Timezone'),
+      'updated-at'                    => t('Timestamp of latest user account change'),
+      'username'                      => t('The user account login name'),
+    );
+
+    return parent::getFieldInfo();
+  }
+
   /**
    * - Timestamp format conversion not needed, as per MigrationBase::timestamp()
    * - Invert is-removed to convert to status
@@ -198,6 +301,46 @@ class UnfuddleProjectMigration extends UnfuddleMigration {
     'categories', // @todo TODO Taxonomy ?
   );
 
+  public function __construct() {
+    parent::__construct(MigrateGroup::getInstance('Unfuddle'));
+    $item_xpath = '/account/projects/project';
+    $items_class = new MigrateItemsXML($this->items_url, $item_xpath, $this->item_ID_xpath);
+    $this->source = new MigrateSourceMultiItems($items_class, self::getFieldInfo());
+    $source_key = array(
+      'id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      )
+    );
+
+    $this->dependencies = array('UnfuddlePeople');
+    $this->destination = new MigrateDestinationNode('casetracker_basic_project');
+    $this->map = new MigrateSQLMap('unfuddle_project',
+      $source_key,
+      MigrateDestinationNode::getKeySchema());
+
+    $this->addXPathFieldMapping('created',  'created-at');
+    $this->addXPathFieldMapping('status',   'archived')
+      ->description(t('Inverted when converting to node.status'));
+    $this->addXPathFieldMapping('body',     'description')
+      ->description(t('Applying site default input format: will often be wrong'));
+    $this->addXPathFieldMapping('teaser',   node_teaser('description', FILTER_FORMAT_DEFAULT))
+      ->description(t('Applying site default input format: will often be wrong'));
+    $this->addXPathFieldMapping('changed',  'updated-at');
+    $this->addXPathFieldMapping('title',    'title');
+
+    $this->addXPathFieldMapping('uid',           NULL)
+      ->sourceMigration('UnfuddlePeople')
+      ->defaultValue(1);
+    $this->addXPathFieldMapping('revision_uid',  NULL)
+      ->sourceMigration('UnfuddlePeople')
+      ->defaultValue(1);
+    $this->addXPathFieldMapping('path',      'short-name')
+      ->description(t('Auto-aliased to project/(short-name)'));
+    $this->afterConstruct();
+  }
+
   protected static function getFieldInfo() {
     self::$fields = array(
       'account-id'                          => t('Unfuddle account Id'),
@@ -244,9 +387,51 @@ class UnfuddleProjectMigration extends UnfuddleMigration {
     return self::$fields;
   }
 
+  public function preparerow(stdClass $row) {
+    $row->xml->{'archived'} = ((int) $row->xml->{'archived'}) ? 0 : 1;
+    $row->xml->{'short-name'} = 'project/'. drupal_clean_css_identifier($row->xml->{'short-name'});
+  }
+}
+
+/**
+ * Import ticket follow-up (comments) data from Unfuddle into CaseTracker project comments
+ */
+class UnfuddleTicketFollowupMigration extends UnfuddleMigration {
+  public $unmigratedDestinations = array(
+    'hostname',
+    'status',
+    'thread',
+    'name',
+    'mail',
+    'homepage',
+    'language',
+    'revision_id',
+  );
+
+  public $unmigratedSources = array(
+    'created-at',
+    'body-format',
+    'parent-type',
+  );
+
+  protected static function getFieldInfo() {
+    self::$fields = array(
+      'author-id'                            => t('Comment author'),
+      'body'                                 => t('Comment text. Applying site default input format: will often be wrong'),
+      'body-format'                          => t('Comment text input format. No direct mapping to Drupal format defaults'),
+      'created-at'                           => t('The follow-up creation timestamp'),
+      'updated-at'                           => t('The follow-up latest update timestamp'),
+      'parent-id'                            => t('The ticket to which the follow-up applies'),
+      'parent-type'                          => t('The follow-up parent type: always Ticket'),
+      'x-subject'                            => t('Virtual: from body'),
+    );
+
+    return self::$fields;
+  }
+
   public function __construct() {
     parent::__construct(MigrateGroup::getInstance('Unfuddle'));
-    $item_xpath = '/account/projects/project';
+    $item_xpath = '/account/projects/project/tickets/ticket/comments/comment';
     $items_class = new MigrateItemsXML($this->items_url, $item_xpath, $this->item_ID_xpath);
     $this->source = new MigrateSourceMultiItems($items_class, self::getFieldInfo());
     $source_key = array(
@@ -257,93 +442,38 @@ class UnfuddleProjectMigration extends UnfuddleMigration {
       )
     );
 
-    $this->dependencies = array('UnfuddlePeople');
-    $this->destination = new MigrateDestinationNode('casetracker_basic_project');
-    $this->map = new MigrateSQLMap('unfuddle_project',
+    $this->dependencies = array('UnfuddlePeople', 'UnfuddleProject', 'UnfuddleTicket');
+    $this->destination = new MigrateDestinationCommentFollowup();
+    $this->map = new MigrateSQLMap('unfuddle_ticket_followup',
       $source_key,
       MigrateDestinationNode::getKeySchema());
 
-    $this->addXPathFieldMapping('created',  'created-at');
-    $this->addXPathFieldMapping('status',   'archived')
-      ->description(t('Inverted when converting to node.status'));
-    $this->addXPathFieldMapping('body',     'description')
-      ->description(t('Applying site default input format: will often be wrong'));
-    $this->addXPathFieldMapping('teaser',   node_teaser('description', FILTER_FORMAT_DEFAULT))
+    $ar1 = drupal_map_assoc($this->unmigratedDestinations);
+    $ar2 = CaseTrackerMeta::getFields();
+    $this->unmigratedDestinations = array_keys($ar1 + $ar2); // Note: PID ignored
+
+    $this->addXPathFieldMapping('uid',              'author-id')
+      ->sourceMigration('UnfuddlePeople');
+    $this->addXPathFieldMapping('comment',          'body')
       ->description(t('Applying site default input format: will often be wrong'));
-    $this->addXPathFieldMapping('changed',  'updated-at');
-    $this->addXPathFieldMapping('title',    'title');
+      $this->addXPathFieldMapping('nid',            'parent-id')
+      ->sourceMigration('UnfuddleTicket');
+    $this->addXPathFieldMapping('timestamp',        'updated-at');
+    $this->addXPathFieldMapping('subject',          'x-subject')
+      ->description(t('Generated'));
 
-    $this->addXPathFieldMapping('uid',           NULL)
-      ->sourceMigration('UnfuddlePeople')
-      ->defaultValue(1);
-    $this->addXPathFieldMapping('revision_uid',  NULL)
-      ->sourceMigration('UnfuddlePeople')
-      ->defaultValue(1);
-    $this->addXPathFieldMapping('path',      'short-name')
-      ->description(t('Auto-aliased to project/(short-name)'));
     $this->afterConstruct();
   }
 
   public function preparerow(stdClass $row) {
-    $row->xml->{'archived'} = ((int) $row->xml->{'archived'}) ? 0 : 1;
-    $row->xml->{'short-name'} = 'project/'. drupal_clean_css_identifier($row->xml->{'short-name'});
-  }
-}
-
-class MigrateDestinationNodeCase extends MigrateDestinationNode {
-  public function __construct() {
-    parent::__construct('casetracker_basic_case');
-  }
-
-  /**
-   * Could also be implemented as a migrate hook_migrate_fields() in module.
-   * Which is best ?
-   *
-   * (non-PHPdoc)
-   * @see MigrateDestinationNode::fields()
-   */
-  public function fields() {
-    $fields = parent::fields();
-    $fields += array(
-      'pid'              => t('Case project'),
-      'case_number'      => t('Case number'),
-      'assign_to'        => t('Case assigned to'),
-      'case_priority_id' => t('Case priority'),
-      'case_type_id'     => t('Case type'),
-      'case_status_id'   => t('Case status'),
-    );
-    return $fields;
-  }
-
-  /**
-   * Casetracker saves its fields in a separate stdClass extra field.
-   *
-   * (non-PHPdoc)
-   * @see MigrateDestinationEntity::prepare()
-   */
-  public function prepare($entity, stdClass $source_row) {
-    $extraFields = array(
-      'pid',
-      'case_priority_id',
-      'case_type_id',
-      'assign_to',
-      'case_status_id',
-    );
-
-    $entity->casetracker = new stdClass();
-    foreach ($extraFields as $extra) {
-      if (isset($entity->$extra)) {
-        $entity->casetracker->$extra = $entity->$extra;
-        unset($entity->extra);
-      }
-    }
-
-    $entity->casetracker->case_type_id = 9; // Bug. Case type is not a default Unfuddle field
+    $row->xml->{'x-subject'} = strip_tags(node_teaser($row->xml->{'body'}, FILTER_FORMAT_DEFAULT, 40));
+//    echo "SOURCE " . __FUNCTION__ . PHP_EOL;
+//    print_r($row->xml);
   }
 }
 
 /**
- * Import "project" ticket data from Unfuddle into CaseTracker project nodes
+ * Import ticket data from Unfuddle into CaseTracker project nodes
  */
 class UnfuddleTicketMigration extends UnfuddleMigration {
   public $unmigratedDestinations = array(
@@ -366,37 +496,6 @@ class UnfuddleTicketMigration extends UnfuddleMigration {
     'version-id',
   );
 
-  protected static function getFieldInfo() {
-    self::$fields = array(
-      'assignee-id'                         => t('User Id'),
-      'component-id'                        => t('The project component (unused)'),
-      'created-at'                          => t('Ticket creation timestamp'),
-      'description'                         => t('Description'),
-      'description-format'                  => t('The ticket body format - does not match Drupal formats'),
-      'due-on'                              => t('Timestamp the ticket should be resolved by'),
-    /*
-      'fieldn-value-id' => t('The value for custom field n'),
-     */
-      'hours-estimate-current'              => t('The current estimate for ticket resolution'),
-      'hours-estimate-initial'              => t('The initial estimate for ticket resolution'),
-      'milestone-id'                        => t('The milestone for which resolution of this ticket is due'),
-      'number'                              => t('An apparent duplicate of the ticket id'),
-      'priority'                            => t('The priority level for the ticket'),
-      'project-id'                          => t('The project this ticket belongs to'),
-      'reporter-id'                         => t('The user reporting the ticket'),
-      'resolution'                          => t('The way the ticket was resolved'),
-      'resolution-description'              => t('Details about the way the ticket was resolved'),
-      'severity-id'                         => t('The ticket severity level'),
-      'status'                              => t('The ticket status'),
-      'summary'                             => t('The ticket summary'),
-      'updated-at'                          => t('Timestamp of latest ticket change'),
-      'version-id'                          => t('The ticket version'),
-      'slug'                                => t('Virtual: ticket summary, converted to slug'),
-    );
-
-    return self::$fields;
-  }
-
   public function __construct() {
     parent::__construct(MigrateGroup::getInstance('Unfuddle'));
     $item_xpath = '/account/projects/project/tickets/ticket';
@@ -442,6 +541,37 @@ class UnfuddleTicketMigration extends UnfuddleMigration {
     $this->afterConstruct();
   }
 
+  protected static function getFieldInfo() {
+    self::$fields = array(
+      'assignee-id'                         => t('User Id'),
+      'component-id'                        => t('The project component (unused)'),
+      'created-at'                          => t('Ticket creation timestamp'),
+      'description'                         => t('Description'),
+      'description-format'                  => t('The ticket body format - does not match Drupal formats'),
+      'due-on'                              => t('Timestamp the ticket should be resolved by'),
+    /*
+      'fieldn-value-id' => t('The value for custom field n'),
+     */
+      'hours-estimate-current'              => t('The current estimate for ticket resolution'),
+      'hours-estimate-initial'              => t('The initial estimate for ticket resolution'),
+      'milestone-id'                        => t('The milestone for which resolution of this ticket is due'),
+      'number'                              => t('An apparent duplicate of the ticket id'),
+      'priority'                            => t('The priority level for the ticket'),
+      'project-id'                          => t('The project this ticket belongs to'),
+      'reporter-id'                         => t('The user reporting the ticket'),
+      'resolution'                          => t('The way the ticket was resolved'),
+      'resolution-description'              => t('Details about the way the ticket was resolved'),
+      'severity-id'                         => t('The ticket severity level'),
+      'status'                              => t('The ticket status'),
+      'summary'                             => t('The ticket summary'),
+      'updated-at'                          => t('Timestamp of latest ticket change'),
+      'version-id'                          => t('The ticket version'),
+      'slug'                                => t('Virtual: ticket summary, converted to slug'),
+    );
+
+    return self::$fields;
+  }
+
   public function preparerow(stdClass $row) {
     /*
     $row->xml->{'archived'} = ((int) $row->xml->{'archived'}) ? 0 : 1;
@@ -473,3 +603,4 @@ class UnfuddleTicketMigration extends UnfuddleMigration {
   $row->xml->{'slug'} = 'case/'. drupal_strtolower(drupal_clean_css_identifier((string) $row->xml->{'summary'}));
   }
 }
+

+ 4 - 0
migrateunfuddle.info

@@ -3,7 +3,11 @@ description = "Import issue data from an Unfuddle dump to Casetracker"
 core = 6.x
 php = 5.3
 
+; comment needed to map follow-ups
+dependencies[] = comment
+
 dependencies[] = path
+
 dependencies[] = casetracker
 dependencies[] = casetracker_basic
 dependencies[] = migrate

+ 38 - 1
migrateunfuddle.module

@@ -8,17 +8,54 @@
  *   the CeCILL 2.0 license.
  */
 
+/**
+ * Implements hook_migrate_api().
+ */
 function migrateunfuddle_migrate_api() {
   $api = array(
     'api' => 2,
   );
 
-  // dsm(get_defined_vars(), __FUNCTION__);
   return $api;
 }
 
 /**
+ * Implements hook_views_api().
+ */
+function migrateunfuddle_views_api() {
+  $ret = array(
+    'api' => '3.0',
+    'path' => drupal_get_path('module', 'migrateunfuddle') . '/views',
+  );
+
+  return $ret;
+}
+
+/**
+ * Implements hook_views_default_views().
+ *
+ * No need to create a .views_default.inc just for this
+ */
+function migrateunfuddle_views_default_views() {
+  $info = migrateunfuddle_views_api();
+  $ret = array();
+  $files = file_scan_directory($info['path'], '.*\.view\.inc', array(), NULL, FALSE);
+  foreach ($files as $file) {
+    require_once $file->filename;
+    $ret[$view->name] = $view;
+  }
+  return $ret;
+}
+
+/**
+ * Convert a string to a SEO-passable slug.
+ *
  * Lifted from Drupal 7
+ *
+ * @param string $identifier
+ * @param array $filter
+ *
+ * @return string
  */
 function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_' => '-', '/' => '-', '[' => '-', ']' => '')) {
   // By default, we filter using Drupal's coding standards.

+ 174 - 0
views/childless.view.inc

@@ -0,0 +1,174 @@
+<?php
+$view = new view;
+$view->name = 'casetracker_unfuddle_childless';
+$view->description = 'Case Tracker > Migrate Unfuddle > Tickets without any followup';
+$view->tag = 'casetracker';
+$view->view_php = '';
+$view->base_table = 'node';
+$view->is_cacheable = FALSE;
+$view->api_version = 2;
+$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
+$handler = $view->new_display('default', 'Defaults', 'default');
+$handler->override_option('fields', array(
+  'title' => array(
+    'label' => '',
+    'alter' => array(
+      'alter_text' => 0,
+      'text' => '',
+      'make_link' => 0,
+      'path' => '',
+      'link_class' => '',
+      'alt' => '',
+      'prefix' => '',
+      'suffix' => '',
+      'target' => '',
+      'help' => '',
+      'trim' => 1,
+      'max_length' => '50',
+      'word_boundary' => 1,
+      'ellipsis' => 1,
+      'html' => 0,
+      'strip_tags' => 0,
+    ),
+    'empty' => '',
+    'hide_empty' => 0,
+    'empty_zero' => 0,
+    'link_to_node' => 1,
+    'exclude' => 0,
+    'id' => 'title',
+    'table' => 'node',
+    'field' => 'title',
+    'relationship' => 'none',
+  ),
+  'created' => array(
+    'label' => '',
+    'alter' => array(
+      'alter_text' => 1,
+      'text' => '([created])',
+      'make_link' => 0,
+      'path' => '',
+      'link_class' => '',
+      'alt' => '',
+      'prefix' => '',
+      'suffix' => '',
+      'target' => '',
+      'help' => '',
+      'trim' => 0,
+      'max_length' => '',
+      'word_boundary' => 1,
+      'ellipsis' => 1,
+      'html' => 0,
+      'strip_tags' => 0,
+    ),
+    'empty' => '',
+    'hide_empty' => 0,
+    'empty_zero' => 0,
+    'date_format' => 'raw time ago',
+    'custom_date_format' => '',
+    'exclude' => 0,
+    'id' => 'created',
+    'table' => 'node',
+    'field' => 'created',
+    'relationship' => 'none',
+  ),
+));
+$handler->override_option('sorts', array(
+  'created' => array(
+    'order' => 'ASC',
+    'granularity' => 'second',
+    'id' => 'created',
+    'table' => 'node',
+    'field' => 'created',
+    'relationship' => 'none',
+  ),
+));
+$handler->override_option('filters', array(
+  'type' => array(
+    'operator' => 'in',
+    'value' => array(
+      'casetracker_basic_case' => 'casetracker_basic_case',
+    ),
+    'group' => '0',
+    'exposed' => FALSE,
+    'expose' => array(
+      'operator' => FALSE,
+      'label' => '',
+    ),
+    'id' => 'type',
+    'table' => 'node',
+    'field' => 'type',
+    'relationship' => 'none',
+  ),
+  'comment_count' => array(
+    'operator' => '=',
+    'value' => array(
+      'value' => '0',
+      'min' => '',
+      'max' => '',
+    ),
+    'group' => '0',
+    'exposed' => FALSE,
+    'expose' => array(
+      'operator' => FALSE,
+      'label' => '',
+    ),
+    'id' => 'comment_count',
+    'table' => 'node_comment_statistics',
+    'field' => 'comment_count',
+    'relationship' => 'none',
+  ),
+  'status_extra' => array(
+    'operator' => '=',
+    'value' => '',
+    'group' => '0',
+    'exposed' => FALSE,
+    'expose' => array(
+      'operator' => FALSE,
+      'label' => '',
+    ),
+    'id' => 'status_extra',
+    'table' => 'node',
+    'field' => 'status_extra',
+    'relationship' => 'none',
+  ),
+));
+$handler->override_option('access', array(
+  'type' => 'none',
+));
+$handler->override_option('cache', array(
+  'type' => 'none',
+));
+$handler->override_option('title', 'Cases without any followup');
+$handler->override_option('items_per_page', 50);
+$handler->override_option('use_pager', '1');
+$handler->override_option('style_plugin', 'grid');
+$handler->override_option('style_options', array(
+  'grouping' => '',
+  'columns' => '2',
+  'alignment' => 'vertical',
+  'fill_single_line' => 1,
+));
+$handler->override_option('row_options', array(
+  'inline' => array(
+    'title' => 'title',
+    'created' => 'created',
+  ),
+  'separator' => '',
+  'hide_empty' => 0,
+));
+$handler = $view->new_display('page', 'Page', 'page_1');
+$handler->override_option('path', 'casetracker/childless');
+$handler->override_option('menu', array(
+  'type' => 'normal',
+  'title' => 'Unfollowed',
+  'description' => 'Cases without follow-ups',
+  'weight' => '0',
+  'name' => 'navigation',
+));
+$handler->override_option('tab_options', array(
+  'type' => 'none',
+  'title' => '',
+  'description' => '',
+  'weight' => 0,
+  'name' => 'navigation',
+));