123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475 |
- <?php
- /**
- * @file
- * Migration class to import user accounts from an Unfuddle dump.
- *
- * @copyright Coyright (c) 2011 Ouest Systèmes Informatiques (OSI, OSInet)
- *
- * @license Licensed under the General Public License version 2 and later, and the CeCILL 2.0 license.
- */
- abstract class UnfuddleMigration extends XMLMigration {
- public $items_url;
- public $item_ID_xpath = 'id';
- 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 afterConstruct() {
- $this->addUnmigratedDestinations($this->unmigratedDestinations, t('White hole'));
- $this->addUnmigratedSources($this->unmigratedSources, t('Black hole'));
- }
- /**
- * Return a field-name-indexed hash of field descriptions.
- *
- * @return array
- */
- protected static function getFieldInfo() {
- 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);
- }
- }
- /**
- * Import "people" data from the Unfuddle backup into Drupal users
- */
- class UnfuddlePeopleMigration extends UnfuddleMigration {
- public $unmigratedDestinations = array(
- 'pass',
- 'roles',
- 'theme',
- 'signature',
- 'signature_format',
- 'language',
- 'picture',
- );
- public $unmigratedSources = array(
- 'account-id',
- 'first-name',
- 'identity-url',
- 'is-administrator',
- 'last-name',
- 'notification-frequency',
- 'notification-ignore-self',
- 'notification-last-sent',
- 'notification-scope-messages',
- 'notification-scope-milestones',
- 'notification-scope-notebooks',
- 'notification-scope-source',
- 'notification-scope-tickets',
- 'text-markup',
- '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'));
- $item_xpath = '/account/people/person';
- $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->destination = new MigrateDestinationUser();
- $this->map = new MigrateSQLMap('unfuddle_people',
- $source_key,
- MigrateDestinationUser::getKeySchema());
- // Do not map uid: this prevents user_save from creating the accounts.
- $this->addFieldMapping('uid', NULL)
- ->defaultValue(0);
- $this->addXPathFieldMapping('created', 'created-at');
- $this->addXPathFieldMapping('mail', 'email');
- $this->addXPathFieldMapping('init', 'email')
- ->defaultValue('support@osinet.fr')
- ->description(t('Unfuddle does not keep the original email address'));
- $this->addXPathFieldMapping('status', 'is-removed')
- ->description(t('Inverted when converting to user.status'));
- $this->addXPathFieldMapping('login', 'last-signed-in');
- $this->addXPathFieldMapping('access', 'last-signed-in')
- ->description(t('Unfuddle does not log access time, only sign in.'));
- $this->addXPathFieldMapping('timezone', 'time-zone')
- ->description(t('Only Paris and London currently supported, without DST'));
- $this->addXPathFieldMapping('name', 'username');
- $this->afterConstruct();
- }
- /**
- * - Timestamp format conversion not needed, as per MigrationBase::timestamp()
- * - Invert is-removed to convert to status
- * - Convert Unfuddle timezones to Drupal offset seconds
- *
- * Do not forget type casts when reading SimpleXML elements.
- *
- * @param object $row
- */
- public function prepareRow(stdClass $row) {
- // Automatic via MigrationBase::timestamp()
- // $row->xml->{'created-at'} = strtotime($row->xml->{'created-at'});
- // $row->xml->{'last-signed-in'} = strtotime($row->xml->{'last-signed-in'});
- $row->xml->{'is-removed'} = ((int) $row->xml->{'is-removed'}) ? 0 : 1;
- // @todo TODO Primitive and does not account for DST
- $timezones = array(
- 'London' => 0,
- 'Paris' => 7200,
- );
- $timezone = (string) $row->xml->{'time-zone'};
- $row->xml->{'time-zone'} = empty($timezones[$timezone])
- ? 0
- : $timezones[$timezone];
- }
- }
- /**
- * Import "project" core data from Unfuddle into CaseTracker project nodes
- */
- class UnfuddleProjectMigration extends UnfuddleMigration {
- public $unmigratedDestinations = array(
- 'promote',
- 'sticky',
- 'revision',
- 'language',
- );
- public $unmigratedSources = array(
- 'account-id',
- 'assignee-on-resolve',
- 'backup-frequency',
- 'close-ticket-simultaneously-default',
- 'default-ticket-report-id',
- 'disk-usage',
- 'enable-time-tracking',
- 's3-access-key-id',
- 's3-backup-enabled',
- 's3-bucket-name',
- 'theme',
- 'categories', // @todo TODO Taxonomy ?
- );
- protected static function getFieldInfo() {
- self::$fields = array(
- 'account-id' => t('Unfuddle account Id'),
- 'archived' => t('Unpublished'),
- 'assignee-on-resolve' => t('The user being assigned a ticket once it is resolved'),
- 'backup-frequency' => t('Current email address for the account'),
- 'close-ticket-simultaneously-default' => t('Close tickets on Resolved status'),
- 'created-at' => t('Project creation timestamp'),
- 'default-ticket-report-id' => t('User reporting tickets by default'),
- 'description' => t('Description'),
- 'disk-usage' => t('Disk usage on Unfuddle, in bytes'),
- 'enable-time-tracking' => t('Enable issue time tracking'),
- 's3-access-key-id' => t('Amazon S3 key'),
- 's3-backup-enabled' => t('Periodic backups sent to Amazon S3'),
- 's3-bucket-name' => t('Amazon S3 destination bucket for backups'),
- 'short-name' => t('Short project name (machine name)'),
- 'theme' => t('Unfuddle main color for project theme'),
- /**
- * @todo TODO insert as CCK fields
- *
- 'ticket-fieldn-active' => (boolean),
- 'ticket-fieldn-disposition' => 'list',
- 'ticket-fieldn-title' => (label)
- *
- */
- 'title' => t('Title'),
- 'updated-at' => t('Timestamp of latest project change'),
- 'categories' => t('Categories'),
- /*
- * Ignored:
- components
- custom-field-values
- messages
- milestones
- notebooks
- severities
- tickets -> separate import
- ticket-reports (views)
- versions
- *
- */
- );
- return self::$fields;
- }
- 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();
- }
- 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
- }
- }
- /**
- * Import "project" ticket data from Unfuddle into CaseTracker project nodes
- */
- class UnfuddleTicketMigration extends UnfuddleMigration {
- public $unmigratedDestinations = array(
- 'status',
- 'promote',
- 'sticky',
- 'revision',
- 'language',
- );
- public $unmigratedSources = array(
- 'component-id',
- 'description-format',
- 'due-on',
- 'hours-estimate-current',
- 'hours-estimate-initial',
- 'milestone-id',
- 'resolution',
- 'resolution-description',
- 'severity-id',
- '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';
- $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', 'UnfuddleProject');
- $this->destination = new MigrateDestinationNodeCase();
- $this->map = new MigrateSQLMap('unfuddle_ticket',
- $source_key,
- MigrateDestinationNode::getKeySchema());
- $this->addXPathFieldMapping('assign_to', 'assignee-id')
- ->sourceMigration('UnfuddlePeople');
- $this->addXPathFieldMapping('created', 'created-at');
- $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('case_priority_id', 'priority');
- $this->addXPathFieldMapping('pid', 'project-id')
- ->sourceMigration('UnfuddleProject');
- $this->addXPathFieldMapping('uid', 'reporter-id')
- ->sourceMigration('UnfuddlePeople');
- $this->addXPathFieldMapping('revision_uid', 'reporter-id')
- ->sourceMigration('UnfuddlePeople');
- $this->addXPathFieldMapping('case_status_id', 'status');
- $this->addXPathFieldMapping('case_number', 'number');
- $this->addXPathFieldMapping('title', 'summary');
- $this->addXPathFieldMapping('path', 'slug'); // slugged
- $this->addXPathFieldMapping('changed', 'updated-at');
- $this->addFieldMapping('case_type_id', NULL);
- $this->afterConstruct();
- }
- public function preparerow(stdClass $row) {
- /*
- $row->xml->{'archived'} = ((int) $row->xml->{'archived'}) ? 0 : 1;
- $row->xml->{'short-name'} = 'project/'. $row->xml->{'short-name'};
- */
- $row->xml->{'priority'} = 2; // "Normal"
- $status = (string) $row->xml->{'status'};
- $ct_status_map = array(
- 'Open' => 4,
- 'Resolved' => 5,
- 'Deferred' => 6, // Is not a status in Unfuddle
- 'Duplicate' => 7, // Is not a status in Unfuddle
- 'Closed' => 8,
- );
- $u_status_map = array(
- 'fixed' => $ct_status_map['Resolved'],
- 'closed' => $ct_status_map['Closed'],
- 'reopened' => $ct_status_map['Open'],
- 'reassigned' => $ct_status_map['Open'],
- 'new' => $ct_status_map['Open'],
- 'accepted' => $ct_status_map['Open'],
- );
- $row->xml->{'status'} = isset($u_status_map[$status])
- ? $u_status_map[$status]
- : $ct_status_map['Open'];
- $row->xml->{'slug'} = 'case/'. drupal_strtolower(drupal_clean_css_identifier((string) $row->xml->{'summary'}));
- }
- }
|