5 * Support for node destinations.
9 // Make sure this works with updates, explicit destination keys
12 * Destination class implementing migration into nodes.
14 class MigrateDestinationNode
extends MigrateDestinationEntity
{
15 static public
function getKeySchema() {
20 'description' => 'ID of destination node',
26 * Return an options array for node destinations.
28 * @param string $language
29 * Default language for nodes created via this destination class.
30 * @param string $text_format
31 * Default text format for nodes created via this destination class.
33 static public
function options($language, $text_format) {
34 return compact('language', 'text_format');
38 * Basic initialization
40 * @param string $bundle
41 * A.k.a. the content type (page, article, etc.) of the node.
42 * @param array $options
43 * Options applied to nodes.
45 public
function __construct($bundle, array $options = array()) {
46 parent
::__construct('node', $bundle, $options);
50 * Returns a list of fields available to be mapped for the node type (bundle)
52 * @param Migration $migration
53 * Optionally, the migration containing this destination.
55 * Keys: machine names of the fields (to be passed to addFieldMapping)
56 * Values: Human-friendly descriptions of the fields.
58 public
function fields($migration = NULL
) {
60 // First the core (node table) properties
61 $fields['nid'] = t('Node: <a href="@doc">Existing node ID</a>',
62 array('@doc' => 'http://drupal.org/node/1349696#nid'));
63 $node_type = node_type_load($this->bundle
);
64 if ($node_type->has_title
) {
65 $fields['title'] = t('Node: <a href="@doc">',
66 array('@doc' => 'http://drupal.org/node/1349696#title'))
67 .
$node_type->title_label .
'</a>';
69 $fields['uid'] = t('<a href="@doc">Authored by (uid)</a>',
70 array('@doc' => 'http://drupal.org/node/1349696#uid'));
71 $fields['created'] = t('<a href="@doc">Created timestamp</a>',
72 array('@doc' => 'http://drupal.org/node/1349696#created'));
73 $fields['changed'] = t('<a href="@doc">Modified timestamp</a>',
74 array('@doc' => 'http://drupal.org/node/1349696#changed'));
75 $fields['status'] = t('<a href="@doc">Published</a>',
76 array('@doc' => 'http://drupal.org/node/1349696#status'));
77 $fields['promote'] = t('<a href="@doc">Promoted to front page</a>',
78 array('@doc' => 'http://drupal.org/node/1349696#promote'));
79 $fields['sticky'] = t('<a href="@doc">Sticky at top of lists</a>',
80 array('@doc' => 'http://drupal.org/node/1349696#sticky'));
81 $fields['revision'] = t('<a href="@doc">Create new revision</a>',
82 array('@doc' => 'http://drupal.org/node/1349696#revision'));
83 $fields['log'] = t('<a href="@doc">Revision Log message</a>',
84 array('@doc' => 'http://drupal.org/node/1349696#log'));
85 $fields['language'] = t('<a href="@doc">Language (fr, en, ...)</a>',
86 array('@doc' => 'http://drupal.org/node/1349696#language'));
87 $fields['tnid'] = t('<a href="@doc">The translation set id for this node</a>',
88 array('@doc' => 'http://drupal.org/node/1349696#tnid'));
89 $fields['translate'] = t('<a href="@doc">A boolean indicating whether this translation page needs to be updated</a>',
90 array('@doc' => 'http://drupal.org/node/1349696#translate'));
91 $fields['revision_uid'] = t('<a href="@doc">Modified (uid)</a>',
92 array('@doc' => 'http://drupal.org/node/1349696#revision_uid'));
93 $fields['is_new'] = t('Option: <a href="@doc">Indicates a new node with the specified nid should be created</a>',
94 array('@doc' => 'http://drupal.org/node/1349696#is_new'));
96 // Then add in anything provided by handlers
97 $fields += migrate_handler_invoke_all('Entity', 'fields', $this->entityType
, $this->bundle
, $migration);
98 $fields += migrate_handler_invoke_all('Node', 'fields', $this->entityType
, $this->bundle
, $migration);
104 * Delete a batch of nodes at once.
107 * Array of node IDs to be deleted.
109 public
function bulkRollback(array $nids) {
110 migrate_instrument_start('node_delete_multiple');
111 $this->prepareRollback($nids);
112 node_delete_multiple($nids);
113 $this->completeRollback($nids);
114 migrate_instrument_stop('node_delete_multiple');
118 * Import a single node.
121 * Node object to build. Prefilled with any fields mapped in the Migration.
123 * Raw source data object - passed through to prepare/complete handlers.
125 * Array of key fields (nid only in this case) of the node that was saved if
126 * successful. FALSE on failure.
128 public
function import(stdClass
$node, stdClass
$row) {
129 // Updating previously-migrated content?
130 $migration = Migration
::currentMigration();
131 if (isset($row->migrate_map_destid1
)) {
132 // Make sure is_new is off
133 $node->is_new
= FALSE
;
134 if (isset($node->nid
)) {
135 if ($node->nid
!= $row->migrate_map_destid1
) {
136 throw new
MigrateException(t("Incoming nid !nid and map destination nid !destid1 don't match",
137 array('!nid' => $node->nid
, '!destid1' => $row->migrate_map_destid1
)));
141 $node->nid
= $row->migrate_map_destid1
;
143 // Get the existing vid, tnid so updates don't generate notices
144 $values = db_select('node', 'n')
145 ->fields('n', array('vid', 'tnid'))
146 ->condition('nid', $node->nid
)
149 if (empty($values)) {
150 throw new
MigrateException(t("Incoming node ID !nid no longer exists",
151 array('!nid' => $node->nid
)));
153 $node->vid
= $values['vid'];
154 if (empty($node->tnid
)) {
155 $node->tnid
= $values['tnid'];
158 if ($migration->getSystemOfRecord() == Migration
::DESTINATION
) {
159 if (!isset($node->nid
)) {
160 throw new
MigrateException(t('System-of-record is DESTINATION, but no destination nid provided'));
162 $old_node = node_load($node->nid
);
163 if (empty($old_node)) {
164 throw new
MigrateException(t('System-of-record is DESTINATION, but node !nid does not exist',
165 array('!nid' => $node->nid
)));
167 if (!isset($node->created
)) {
168 $node->created
= $old_node->created
;
170 if (!isset($node->vid
)) {
171 $node->vid
= $old_node->vid
;
173 if (!isset($node->status
)) {
174 $node->status
= $old_node->status
;
176 if (!isset($node->uid
)) {
177 $node->uid
= $old_node->uid
;
181 if (!isset($node->type
)) {
182 // Default the type to our designated destination bundle (by doing this
183 // conditionally, we permit some flexibility in terms of implementing
184 // migrations which can affect more than one type).
185 $node->type
= $this->bundle
;
188 // Set some required properties.
190 if ($migration->getSystemOfRecord() == Migration
::SOURCE
) {
191 if (empty($node->language
)) {
192 $node->language
= $this->language
;
195 // Apply defaults, allow standard node prepare hooks to fire.
196 // node_object_prepare() will blow these away, so save them here and
197 // stuff them in later if need be.
198 if (isset($node->created
)) {
199 $created = MigrationBase
::timestamp($node->created
);
202 // To keep node_object_prepare() from choking
203 $node->created
= REQUEST_TIME
;
205 if (isset($node->changed
)) {
206 $changed = MigrationBase
::timestamp($node->changed
);
208 if (isset($node->uid
)) {
211 node_object_prepare($node);
212 if (isset($created)) {
213 $node->created
= $created;
215 // No point to resetting $node->changed here, node_save() will overwrite it
221 // Invoke migration prepare handlers
222 $this->prepare($node, $row);
224 if (!isset($node->revision
)) {
225 $node->revision
= 0; // Saves disk space and writes. Can be overridden.
228 // Trying to update an existing node
229 if ($migration->getSystemOfRecord() == Migration
::DESTINATION
) {
230 // Incoming data overrides existing data, so only copy non-existent fields
231 foreach ($old_node as
$field => $value) {
232 // An explicit NULL in the source data means to wipe to old value (i.e.,
233 // don't copy it over from $old_node)
234 if (property_exists($node, $field) && $node->$field === NULL
) {
237 elseif (!isset($node->$field)) {
238 $node->$field = $old_node->$field;
243 if (isset($node->nid
) && !(isset($node->is_new
) && $node->is_new
)) {
250 migrate_instrument_start('node_save');
252 migrate_instrument_stop('node_save');
254 if (isset($node->nid
)) {
262 // Unfortunately, http://drupal.org/node/722688 was not accepted, so fix
263 // the changed timestamp
264 if (isset($changed)) {
266 ->fields(array('changed' => $changed))
267 ->condition('nid', $node->nid
)
269 $node->changed
= $changed;
272 // Potentially fix uid and timestamp in node_revisions.
273 $query = db_update('node_revision')
274 ->condition('vid', $node->vid
);
275 if (isset($changed)) {
276 $fields['timestamp'] = $changed;
278 $revision_uid = isset($node->revision_uid
) ?
$node->revision_uid
: $node->uid
;
279 if ($revision_uid != $GLOBALS['user']->uid
) {
280 $fields['uid'] = $revision_uid;
282 if (!empty($fields)) {
283 // We actually have something to update.
284 $query->fields($fields);
286 if (isset($changed)) {
287 $node->timestamp
= $changed;
290 $return = array($node->nid
);
296 $this->complete($node, $row);