1
|
<?php
|
2
|
|
3
|
/**
|
4
|
* @file
|
5
|
* Tests for plugins/FeedsNodeProcessor.inc.
|
6
|
*/
|
7
|
|
8
|
/**
|
9
|
* Test aggregating a feed as node items.
|
10
|
*/
|
11
|
class FeedsRSStoNodesTest extends FeedsWebTestCase {
|
12
|
|
13
|
/**
|
14
|
* {@inheritdoc}
|
15
|
*/
|
16
|
public static function getInfo() {
|
17
|
return array(
|
18
|
'name' => 'Processor: Node',
|
19
|
'description' => 'Tests for the node processor.',
|
20
|
'group' => 'Feeds',
|
21
|
);
|
22
|
}
|
23
|
|
24
|
/**
|
25
|
* Set up test.
|
26
|
*/
|
27
|
public function setUp() {
|
28
|
parent::setUp();
|
29
|
|
30
|
// Set the front page to show 20 nodes so we can easily see what is aggregated.
|
31
|
variable_set('default_nodes_main', 20);
|
32
|
|
33
|
// Set the teaser length display to unlimited otherwise tests looking for
|
34
|
// text on nodes will fail.
|
35
|
$edit = array('fields[body][type]' => 'text_default');
|
36
|
$this->drupalPost('admin/structure/types/manage/article/display/teaser', $edit, 'Save');
|
37
|
|
38
|
// Create an importer configuration.
|
39
|
$this->createImporterConfiguration('Syndication', 'syndication');
|
40
|
$this->addMappings('syndication',
|
41
|
array(
|
42
|
0 => array(
|
43
|
'source' => 'title',
|
44
|
'target' => 'title',
|
45
|
'unique' => FALSE,
|
46
|
),
|
47
|
1 => array(
|
48
|
'source' => 'description',
|
49
|
'target' => 'body',
|
50
|
),
|
51
|
2 => array(
|
52
|
'source' => 'timestamp',
|
53
|
'target' => 'created',
|
54
|
),
|
55
|
3 => array(
|
56
|
'source' => 'url',
|
57
|
'target' => 'url',
|
58
|
'unique' => TRUE,
|
59
|
),
|
60
|
4 => array(
|
61
|
'source' => 'guid',
|
62
|
'target' => 'guid',
|
63
|
'unique' => TRUE,
|
64
|
),
|
65
|
)
|
66
|
);
|
67
|
}
|
68
|
|
69
|
/**
|
70
|
* Test node creation, refreshing/deleting feeds and feed items.
|
71
|
*/
|
72
|
public function test() {
|
73
|
$nid = $this->createFeedNode();
|
74
|
|
75
|
// Assert 10 items aggregated after creation of the node.
|
76
|
$this->assertText('Created 10 nodes');
|
77
|
$article_nid = db_query_range("SELECT nid FROM {node} WHERE type = 'article'", 0, 1)->fetchField();
|
78
|
$this->assertEqual("Created by FeedsNodeProcessor", db_query("SELECT nr.log FROM {node} n JOIN {node_revision} nr ON n.vid = nr.vid WHERE n.nid = :nid", array(':nid' => $article_nid))->fetchField());
|
79
|
|
80
|
// Navigate to feed node, there should be Feeds tabs visible.
|
81
|
$this->drupalGet("node/$nid");
|
82
|
$this->assertRaw("node/$nid/import");
|
83
|
$this->assertRaw("node/$nid/delete-items");
|
84
|
|
85
|
// Assert accuracy of aggregated information.
|
86
|
$this->drupalGet('node');
|
87
|
$this->assertRaw('<span class="username">Anonymous (not verified)</span>');
|
88
|
$this->assertDevseedFeedContent();
|
89
|
|
90
|
// Assert DB status.
|
91
|
$count = db_query("SELECT COUNT(*) FROM {node} n INNER JOIN {feeds_item} fi ON fi.entity_type = 'node' AND n.nid = fi.entity_id")->fetchField();
|
92
|
$this->assertEqual($count, 10, 'Accurate number of items in database.');
|
93
|
|
94
|
// Assert default input format on first imported feed node.
|
95
|
|
96
|
// NEEDS update.
|
97
|
// $format = db_query_range("SELECT nr.format FROM {feeds_node_item} fi JOIN {node} n ON fi.nid = n.nid JOIN {node_revision} nr ON n.vid = nr.vid", 0, 1)->fetchField();
|
98
|
// $this->assertEqual($format, filter_fallback_format(), 'Using default Input format.');
|
99
|
|
100
|
// Import again.
|
101
|
$this->drupalPost("node/$nid/import", array(), 'Import');
|
102
|
$this->assertText('There are no new nodes');
|
103
|
|
104
|
// Assert DB status, there still shouldn't be more than 10 items.
|
105
|
$count = db_query("SELECT COUNT(*) FROM {node} n INNER JOIN {feeds_item} fi ON fi.entity_type = 'node' AND n.nid = fi.entity_id")->fetchField();
|
106
|
$this->assertEqual($count, 10, 'Accurate number of items in database.');
|
107
|
|
108
|
// All of the above tests should have produced published nodes, set default
|
109
|
// to unpublished, import again.
|
110
|
$count = db_query("SELECT COUNT(*) FROM {node} n INNER JOIN {feeds_item} fi ON fi.entity_type = 'node' AND n.nid = fi.entity_id WHERE n.status = 1")->fetchField();
|
111
|
$this->assertEqual($count, 10, 'All items are published.');
|
112
|
$edit = array(
|
113
|
'node_options[status]' => FALSE,
|
114
|
);
|
115
|
$this->drupalPost('admin/structure/types/manage/article', $edit, t('Save content type'));
|
116
|
$this->drupalPost("node/$nid/delete-items", array(), 'Delete');
|
117
|
$this->drupalPost("node/$nid/import", array(), 'Import');
|
118
|
$count = db_query("SELECT COUNT(*) FROM {node} n INNER JOIN {feeds_item} fi ON fi.entity_type = 'node' AND n.nid = fi.entity_id WHERE n.status = 0")->fetchField();
|
119
|
$this->assertEqual($count, 10, 'No items are published.');
|
120
|
$edit = array(
|
121
|
'node_options[status]' => TRUE,
|
122
|
);
|
123
|
$this->drupalPost('admin/structure/types/manage/article', $edit, t('Save content type'));
|
124
|
$this->drupalPost("node/$nid/delete-items", array(), 'Delete');
|
125
|
|
126
|
// Enable replace existing and import updated feed file.
|
127
|
$this->drupalPost("node/$nid/import", array(), 'Import');
|
128
|
$this->setSettings('syndication', 'FeedsNodeProcessor', array('update_existing' => 1));
|
129
|
$feed_url = $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'feeds') . '/tests/feeds/developmentseed_changes.rss2';
|
130
|
$this->editFeedNode($nid, $feed_url);
|
131
|
$this->drupalPost("node/$nid/import", array(), 'Import');
|
132
|
$this->assertText('Updated 2 nodes');
|
133
|
|
134
|
// Assert accuracy of aggregated content (check 2 updates, one original).
|
135
|
$this->drupalGet('node');
|
136
|
$this->assertText('Managing News Translation Workflow: Two Way Translation Updates');
|
137
|
$this->assertText('Presenting on Features in Drupal and Managing News');
|
138
|
$this->assertText('Scaling the Open Atrium UI');
|
139
|
|
140
|
// Import again.
|
141
|
$this->drupalPost("node/$nid/import", array(), 'Import');
|
142
|
$this->assertText('There are no new nodes');
|
143
|
$this->assertFeedItemCount(10);
|
144
|
|
145
|
// Now delete all items.
|
146
|
$this->drupalPost("node/$nid/delete-items", array(), 'Delete');
|
147
|
$this->assertText('Deleted 10 nodes');
|
148
|
$this->assertFeedItemCount(0);
|
149
|
|
150
|
// Change author and turn off authorization.
|
151
|
$this->auth_user = $this->drupalCreateUser(array('access content'));
|
152
|
$this->setSettings('syndication', 'FeedsNodeProcessor', array('author' => $this->auth_user->name, 'authorize' => FALSE));
|
153
|
|
154
|
// Change input format.
|
155
|
$this->setSettings('syndication', 'FeedsNodeProcessor', array('input_format' => 'plain_text'));
|
156
|
|
157
|
// Import again.
|
158
|
$this->drupalPost("node/$nid/import", array(), 'Import');
|
159
|
$this->assertText('Created 10 nodes');
|
160
|
|
161
|
// Assert author.
|
162
|
$this->drupalGet('node');
|
163
|
$this->assertPattern('/<span class="username">' . check_plain($this->auth_user->name) . '<\/span>/');
|
164
|
$count = db_query("SELECT COUNT(*) FROM {feeds_item} fi JOIN {node} n ON fi.entity_type = 'node' AND fi.entity_id = n.nid WHERE n.uid = :uid", array(':uid' => $this->auth_user->uid))->fetchField();
|
165
|
$this->assertEqual($count, 10, 'Accurate number of items in database.');
|
166
|
|
167
|
// Assert input format.
|
168
|
|
169
|
// NEEDS update.
|
170
|
// $format = db_query_range("SELECT nr.format FROM {feeds_node_item} fi JOIN {node} n ON fi.nid = n.nid JOIN {node_revision} nr ON n.vid = nr.vid", 0, 1)->fetchField();
|
171
|
// $this->assertEqual($format, filter_fallback_format() + 1, 'Set non-default Input format.');
|
172
|
|
173
|
// Set to update existing, remove authorship of above nodes and import again.
|
174
|
$this->setSettings('syndication', 'FeedsNodeProcessor', array('update_existing' => 2));
|
175
|
$nids = db_query("SELECT nid FROM {node} n INNER JOIN {feeds_item} fi ON fi.entity_type = 'node' AND n.nid = fi.entity_id")->fetchCol();
|
176
|
db_update('node')
|
177
|
->fields(array('uid' => 0))
|
178
|
->condition('nid', $nids, 'IN')
|
179
|
->execute();
|
180
|
db_update('feeds_item')
|
181
|
->fields(array('hash' => ''))
|
182
|
->condition('entity_type', 'node')
|
183
|
->condition('entity_id', $nids, 'IN')
|
184
|
->execute();
|
185
|
$this->drupalPost("node/$nid/import", array(), 'Import');
|
186
|
$this->drupalGet('node');
|
187
|
$this->assertNoPattern('/<span class="username">' . check_plain($this->auth_user->name) . '<\/span>/');
|
188
|
$count = db_query("SELECT COUNT(*) FROM {feeds_item} fi JOIN {node} n ON fi.entity_type = 'node' AND fi.entity_id = n.nid WHERE n.uid = :uid", array(':uid' => $this->auth_user->uid))->fetchField();
|
189
|
$this->assertEqual($count, 0, 'Accurate number of items in database.');
|
190
|
|
191
|
// Map feed node's author to feed item author, update - feed node's items
|
192
|
// should now be assigned to feed node author.
|
193
|
$this->addMappings('syndication',
|
194
|
array(
|
195
|
5 => array(
|
196
|
'source' => 'parent:uid',
|
197
|
'target' => 'uid',
|
198
|
),
|
199
|
)
|
200
|
);
|
201
|
$this->drupalPost("node/$nid/import", array(), 'Import');
|
202
|
$this->drupalGet('node');
|
203
|
$this->assertNoPattern('/<span class="username">' . check_plain($this->auth_user->name) . '<\/span>/');
|
204
|
$uid = db_query("SELECT uid FROM {node} WHERE nid = :nid", array(':nid' => $nid))->fetchField();
|
205
|
$count = db_query("SELECT COUNT(*) FROM {node} WHERE uid = :uid", array(':uid' => $uid))->fetchField();
|
206
|
$this->assertEqual($count, 11, 'All feed item nodes are assigned to feed node author.');
|
207
|
|
208
|
// Login with new user with only access content permissions.
|
209
|
$this->drupalLogin($this->auth_user);
|
210
|
|
211
|
// Navigate to feed node, there should be no Feeds tabs visible.
|
212
|
$this->drupalGet("node/$nid");
|
213
|
$this->assertNoRaw("node/$nid/import");
|
214
|
$this->assertNoRaw("node/$nid/delete-items");
|
215
|
|
216
|
// Now create a second feed configuration that is not attached to a content
|
217
|
// type and run tests on importing/purging.
|
218
|
// Login with sufficient permissions.
|
219
|
$this->drupalLogin($this->admin_user);
|
220
|
// Remove all items again so that next test can check for them.
|
221
|
$this->drupalPost("node/$nid/delete-items", array(), 'Delete');
|
222
|
|
223
|
// Create an importer, not attached to content type.
|
224
|
$this->createImporterConfiguration('Syndication standalone', 'syndication_standalone');
|
225
|
$edit = array(
|
226
|
'content_type' => '',
|
227
|
);
|
228
|
$this->drupalPost('admin/structure/feeds/syndication_standalone/settings', $edit, 'Save');
|
229
|
$this->addMappings('syndication_standalone',
|
230
|
array(
|
231
|
0 => array(
|
232
|
'source' => 'title',
|
233
|
'target' => 'title',
|
234
|
'unique' => FALSE,
|
235
|
),
|
236
|
1 => array(
|
237
|
'source' => 'description',
|
238
|
'target' => 'body',
|
239
|
),
|
240
|
2 => array(
|
241
|
'source' => 'timestamp',
|
242
|
'target' => 'created',
|
243
|
),
|
244
|
3 => array(
|
245
|
'source' => 'url',
|
246
|
'target' => 'url',
|
247
|
'unique' => TRUE,
|
248
|
),
|
249
|
4 => array(
|
250
|
'source' => 'guid',
|
251
|
'target' => 'guid',
|
252
|
'unique' => TRUE,
|
253
|
),
|
254
|
)
|
255
|
);
|
256
|
|
257
|
// Import, assert 10 items aggregated after creation of the node.
|
258
|
$this->importURL('syndication_standalone');
|
259
|
$this->assertText('Created 10 nodes');
|
260
|
|
261
|
// Assert accuracy of aggregated information.
|
262
|
$this->drupalGet('node');
|
263
|
$this->assertDevseedFeedContent();
|
264
|
$this->assertFeedItemCount(10);
|
265
|
|
266
|
// Import again.
|
267
|
$this->drupalPost('import/syndication_standalone', array(), 'Import');
|
268
|
$this->assertText('There are no new nodes');
|
269
|
$this->assertFeedItemCount(10);
|
270
|
|
271
|
// Enable replace existing and import updated feed file.
|
272
|
$this->setSettings('syndication_standalone', 'FeedsNodeProcessor', array('update_existing' => 1));
|
273
|
$feed_url = $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'feeds') . '/tests/feeds/developmentseed_changes.rss2';
|
274
|
$this->importURL('syndication_standalone', $feed_url);
|
275
|
$this->assertText('Updated 2 nodes');
|
276
|
|
277
|
// Assert accuracy of aggregated information (check 2 updates, one orig).
|
278
|
$this->drupalGet('node');
|
279
|
$this->assertText('Managing News Translation Workflow: Two Way Translation Updates');
|
280
|
$this->assertText('Presenting on Features in Drupal and Managing News');
|
281
|
$this->assertText('Scaling the Open Atrium UI');
|
282
|
|
283
|
// Import again.
|
284
|
$this->drupalPost('import/syndication_standalone', array(), 'Import');
|
285
|
$this->assertText('There are no new nodes');
|
286
|
$this->assertFeedItemCount(10);
|
287
|
|
288
|
// Now delete all items.
|
289
|
$this->drupalPost('import/syndication_standalone/delete-items', array(), 'Delete');
|
290
|
$this->assertText('Deleted 10 nodes');
|
291
|
$this->assertFeedItemCount(0);
|
292
|
|
293
|
// Import again, we should find new content.
|
294
|
$this->drupalPost('import/syndication_standalone', array(), 'Import');
|
295
|
$this->assertText('Created 10 nodes');
|
296
|
$this->assertFeedItemCount(10);
|
297
|
|
298
|
// Enable unpublish missing nodes and import updated feed file.
|
299
|
$this->setSettings('syndication_standalone', 'FeedsNodeProcessor', array('update_non_existent' => FEEDS_UNPUBLISH_NON_EXISTENT, 'update_existing' => FEEDS_REPLACE_EXISTING));
|
300
|
$missing_url = $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'feeds') . '/tests/feeds/developmentseed_missing.rss2';
|
301
|
$this->importURL('syndication_standalone', $missing_url);
|
302
|
$this->assertText('Unpublished 1 node');
|
303
|
$this->assertFeedItemCount(10);
|
304
|
|
305
|
// Import again to ensure the message that one node is unpublished doesn't
|
306
|
// reappear (since the node was already unpublished during the previous
|
307
|
// import).
|
308
|
$this->drupalPost('import/syndication_standalone', array(), 'Import');
|
309
|
$this->assertText('There are no new nodes');
|
310
|
$this->assertFeedItemCount(10);
|
311
|
|
312
|
// Re-import the original feed to ensure the unpublished node is updated,
|
313
|
// even though the item is the same since the last time it was available in
|
314
|
// the feed. Fact is that the node was not available in the previous import
|
315
|
// and that should be seen as a change.
|
316
|
$this->importURL('syndication_standalone', $feed_url);
|
317
|
$this->assertText('Updated 1 node');
|
318
|
$this->assertFeedItemCount(10);
|
319
|
|
320
|
// Now delete all items.
|
321
|
$this->drupalPost('import/syndication_standalone/delete-items', array(), 'Delete');
|
322
|
$this->assertText('Deleted 10 nodes');
|
323
|
$this->assertFeedItemCount(0);
|
324
|
|
325
|
// Import again, to reset node counts.
|
326
|
$this->importURL('syndication_standalone', $feed_url);
|
327
|
$this->assertText('Created 10 nodes');
|
328
|
$this->assertFeedItemCount(10);
|
329
|
|
330
|
// Change settings to delete non-existent nodes from feed.
|
331
|
$this->setSettings('syndication_standalone', 'FeedsNodeProcessor', array('update_non_existent' => 'delete'));
|
332
|
$this->importURL('syndication_standalone', $missing_url);
|
333
|
$this->assertText('Removed 1 node');
|
334
|
$this->assertFeedItemCount(9);
|
335
|
|
336
|
// Now delete all items.
|
337
|
$this->drupalPost('import/syndication_standalone/delete-items', array(), 'Delete');
|
338
|
$this->assertText('Deleted 9 nodes');
|
339
|
$this->assertFeedItemCount(0);
|
340
|
|
341
|
// Login with new user with only access content permissions.
|
342
|
$this->drupalLogin($this->auth_user);
|
343
|
|
344
|
// Navigate to feed import form, access should be denied.
|
345
|
$this->drupalGet('import/syndication_standalone');
|
346
|
$this->assertResponse(403);
|
347
|
|
348
|
// Use File Fetcher.
|
349
|
$this->drupalLogin($this->admin_user);
|
350
|
|
351
|
$edit = array('plugin_key' => 'FeedsFileFetcher');
|
352
|
$this->drupalPost('admin/structure/feeds/syndication_standalone/fetcher', $edit, 'Save');
|
353
|
|
354
|
$edit = array(
|
355
|
'allowed_extensions' => 'rss2',
|
356
|
);
|
357
|
$this->drupalPost('admin/structure/feeds/syndication_standalone/settings/FeedsFileFetcher', $edit, 'Save');
|
358
|
|
359
|
// Create a feed node.
|
360
|
$edit = array(
|
361
|
'files[feeds]' => $this->absolutePath() . '/tests/feeds/drupalplanet.rss2',
|
362
|
);
|
363
|
$this->drupalPost('import/syndication_standalone', $edit, 'Import');
|
364
|
$this->assertText('Created 25 nodes');
|
365
|
}
|
366
|
|
367
|
/**
|
368
|
* Check that the total number of entries in the feeds_item table is correct.
|
369
|
*/
|
370
|
public function assertFeedItemCount($num) {
|
371
|
$count = db_query("SELECT COUNT(*) FROM {feeds_item} WHERE entity_type = 'node'")->fetchField();
|
372
|
$this->assertEqual($count, $num, 'Accurate number of items in database.');
|
373
|
}
|
374
|
|
375
|
/**
|
376
|
* Check thet contents of the current page for the DS feed.
|
377
|
*/
|
378
|
public function assertDevseedFeedContent() {
|
379
|
$this->assertText('Open Atrium Translation Workflow: Two Way Translation Updates');
|
380
|
$this->assertText('Tue, 10/06/2009');
|
381
|
$this->assertText('A new translation process for Open Atrium & integration with Localize Drupal');
|
382
|
$this->assertText('Week in DC Tech: October 5th Edition');
|
383
|
$this->assertText('Mon, 10/05/2009');
|
384
|
$this->assertText('There are some great technology events happening this week');
|
385
|
$this->assertText('Mapping Innovation at the World Bank with Open Atrium');
|
386
|
$this->assertText('Fri, 10/02/2009');
|
387
|
$this->assertText('is being used as a base platform for collaboration at the World Bank');
|
388
|
$this->assertText('September GeoDC Meetup Tonight');
|
389
|
$this->assertText('Wed, 09/30/2009');
|
390
|
$this->assertText('Today is the last Wednesday of the month');
|
391
|
$this->assertText('Week in DC Tech: September 28th Edition');
|
392
|
$this->assertText('Mon, 09/28/2009');
|
393
|
$this->assertText('Looking to geek out this week? There are a bunch of');
|
394
|
$this->assertText('Open Data for Microfinance: The New MIXMarket.org');
|
395
|
$this->assertText('Thu, 09/24/2009');
|
396
|
$this->assertText('There are profiles for every country that the MIX Market is hosting.');
|
397
|
$this->assertText('Integrating the Siteminder Access System in an Open Atrium-based Intranet');
|
398
|
$this->assertText('Tue, 09/22/2009');
|
399
|
$this->assertText('In addition to authentication, the Siteminder system');
|
400
|
$this->assertText('Week in DC Tech: September 21 Edition');
|
401
|
$this->assertText('Mon, 09/21/2009');
|
402
|
$this->assertText('an interesting variety of technology events happening in Washington, DC ');
|
403
|
$this->assertText('s Software Freedom Day: Impressions & Photos');
|
404
|
$this->assertText('Mon, 09/21/2009');
|
405
|
$this->assertText('Presenting on Features in Drupal and Open Atrium');
|
406
|
$this->assertText('Scaling the Open Atrium UI');
|
407
|
$this->assertText('Fri, 09/18/2009');
|
408
|
$this->assertText('The first major change is switching');
|
409
|
}
|
410
|
|
411
|
/**
|
412
|
* Test validation of feed URLs.
|
413
|
*/
|
414
|
public function testFeedURLValidation() {
|
415
|
$edit['feeds[FeedsHTTPFetcher][source]'] = 'invalid://url';
|
416
|
$this->drupalPost('node/add/page', $edit, 'Save');
|
417
|
$this->assertText('The URL invalid://url is invalid.');
|
418
|
}
|
419
|
|
420
|
/**
|
421
|
* Test using non-normal URLs like feed:// and webcal://.
|
422
|
*/
|
423
|
public function testOddFeedSchemes() {
|
424
|
$url = $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'feeds') . '/tests/feeds/developmentseed.rss2';
|
425
|
|
426
|
$schemes = array('feed', 'webcal');
|
427
|
$item_count = 0;
|
428
|
foreach ($schemes as $scheme) {
|
429
|
$feed_url = strtr($url, array('http://' => $scheme . '://', 'https://' => $scheme . '://'));
|
430
|
|
431
|
$edit['feeds[FeedsHTTPFetcher][source]'] = $feed_url;
|
432
|
$this->drupalPost('node/add/page', $edit, 'Save');
|
433
|
$this->assertText('Basic page Development Seed - Technological Solutions for Progressive Organizations has been created.');
|
434
|
$this->assertText('Created 10 nodes.');
|
435
|
$this->assertFeedItemCount($item_count + 10);
|
436
|
$item_count += 10;
|
437
|
}
|
438
|
}
|
439
|
|
440
|
/**
|
441
|
* Test that feed elements and links are not found on non-feed nodes.
|
442
|
*/
|
443
|
public function testNonFeedNodeUI() {
|
444
|
// There should not be feed links on an article node.
|
445
|
$non_feed_node = $this->drupalCreateNode(array('type' => 'article'));
|
446
|
$this->drupalGet('node/' . $non_feed_node->nid);
|
447
|
$this->assertNoLinkByHref('node/' . $non_feed_node->nid . '/import');
|
448
|
$this->assertNoLink('Delete items');
|
449
|
|
450
|
// Navigate to a non-feed node form, there should be no Feed field visible.
|
451
|
$this->drupalGet('node/add/article');
|
452
|
$this->assertNoFieldByName('feeds[FeedsHTTPFetcher][source]');
|
453
|
}
|
454
|
|
455
|
/**
|
456
|
* Test that nodes will not be created if the user is unauthorized to create
|
457
|
* them.
|
458
|
*/
|
459
|
public function testAuthorize() {
|
460
|
// Create a user with limited permissions. We can't use
|
461
|
// $this->drupalCreateUser here because we need to to set a specific user
|
462
|
// name.
|
463
|
$edit = array(
|
464
|
'name' => 'Development Seed',
|
465
|
'mail' => 'devseed@example.com',
|
466
|
'pass' => user_password(),
|
467
|
'status' => 1,
|
468
|
);
|
469
|
|
470
|
$account = user_save(drupal_anonymous_user(), $edit);
|
471
|
|
472
|
// Adding a mapping to the user_name will invoke authorization.
|
473
|
$this->addMappings('syndication',
|
474
|
array(
|
475
|
5 => array(
|
476
|
'source' => 'author_name',
|
477
|
'target' => 'user_name',
|
478
|
),
|
479
|
)
|
480
|
);
|
481
|
|
482
|
$nid = $this->createFeedNode();
|
483
|
|
484
|
$this->assertText('Failed importing 10 nodes.');
|
485
|
$this->assertText('The user ' . $account->name . ' is not authorized to create content of type article.');
|
486
|
$node_count = db_query("SELECT COUNT(*) FROM {node}")->fetchField();
|
487
|
|
488
|
// We should have 1 node, the feed node.
|
489
|
$this->assertEqual($node_count, 1, t('Correct number of nodes in the database.'));
|
490
|
|
491
|
// Give the user our admin powers.
|
492
|
$edit = array(
|
493
|
'roles' => $this->admin_user->roles,
|
494
|
);
|
495
|
$account = user_save($account, $edit);
|
496
|
|
497
|
$this->drupalPost("node/$nid/import", array(), 'Import');
|
498
|
$this->assertText('Created 10 nodes.');
|
499
|
$node_count = db_query("SELECT COUNT(*) FROM {node}")->fetchField();
|
500
|
$this->assertEqual($node_count, 11, t('Correct number of nodes in the database.'));
|
501
|
}
|
502
|
|
503
|
/**
|
504
|
* Tests expiring nodes.
|
505
|
*/
|
506
|
public function testExpiry() {
|
507
|
// Create importer configuration.
|
508
|
$this->setSettings('syndication', NULL, array('content_type' => ''));
|
509
|
$this->setSettings('syndication', 'FeedsNodeProcessor', array(
|
510
|
'expire' => 2592000,
|
511
|
));
|
512
|
|
513
|
// Create importer.
|
514
|
$this->importURL('syndication');
|
515
|
|
516
|
// Set date of a few nodes to current date so they don't expire.
|
517
|
$edit = array(
|
518
|
'date' => date('Y-m-d'),
|
519
|
);
|
520
|
$this->drupalPost('node/2/edit', $edit, 'Save');
|
521
|
$this->assertText(date('m/d/Y'), 'Found correct date.');
|
522
|
$this->drupalPost('node/5/edit', $edit, 'Save');
|
523
|
$this->assertText(date('m/d/Y'), 'Found correct date.');
|
524
|
|
525
|
// Run cron to schedule jobs.
|
526
|
$this->cronRun();
|
527
|
|
528
|
// Set feeds source expire to run immediately.
|
529
|
db_update('job_schedule')
|
530
|
->fields(array(
|
531
|
'next' => 0,
|
532
|
))
|
533
|
->condition('name', 'feeds_source_expire')
|
534
|
->execute();
|
535
|
|
536
|
// Run cron to execute scheduled jobs.
|
537
|
$this->cronRun();
|
538
|
|
539
|
// Query the feeds_items table and count the number of entries.
|
540
|
$row_count = db_query('SELECT COUNT(*) FROM {feeds_item}')->fetchField();
|
541
|
|
542
|
// Check that number of feeds items is equal to the expected items.
|
543
|
$this->assertEqual($row_count, 2, 'Nodes expired.');
|
544
|
}
|
545
|
|
546
|
/**
|
547
|
* Tests process in background option.
|
548
|
*/
|
549
|
public function testImportInBackground() {
|
550
|
// Just remove the mappings rather than creating a new importer.
|
551
|
$this->removeMappings('syndication', $this->getCurrentMappings('syndication'));
|
552
|
|
553
|
$this->setPlugin('syndication', 'FeedsFileFetcher');
|
554
|
$this->setPlugin('syndication', 'FeedsCSVParser');
|
555
|
|
556
|
$this->setSettings('syndication', NULL, array(
|
557
|
'content_type' => '',
|
558
|
'process_in_background' => TRUE,
|
559
|
'import_period' => FEEDS_SCHEDULE_NEVER,
|
560
|
));
|
561
|
|
562
|
$this->addMappings('syndication', array(
|
563
|
0 => array(
|
564
|
'source' => 'title',
|
565
|
'target' => 'title',
|
566
|
),
|
567
|
1 => array(
|
568
|
'source' => 'GUID',
|
569
|
'target' => 'guid',
|
570
|
'unique' => TRUE,
|
571
|
),
|
572
|
));
|
573
|
|
574
|
$this->importFile('syndication', $this->absolutePath() . '/tests/feeds/many_nodes_ordered.csv', 'Schedule import');
|
575
|
$this->assertEqual(0, db_query("SELECT COUNT(*) FROM {node}")->fetchField());
|
576
|
|
577
|
// Assert that the import button is disabled.
|
578
|
$this->assertFieldDisabled('op');
|
579
|
|
580
|
// Assert that there is one import task in the queue.
|
581
|
$this->assertEqual(1, db_query("SELECT COUNT(*) FROM {queue} WHERE name = 'feeds_source_import'")->fetchField());
|
582
|
|
583
|
// The feed should still be scheduled because it is being processed.
|
584
|
// @see https://drupal.org/node/2275893
|
585
|
$this->cronRun();
|
586
|
$this->assertEqual(86, db_query("SELECT COUNT(*) FROM {node}")->fetchField());
|
587
|
|
588
|
// Assert that the import button is no longer disabled.
|
589
|
$this->drupalGet('import/syndication');
|
590
|
$this->assertFieldEnabled('op');
|
591
|
|
592
|
// Assert that there are no more import tasks in the queue.
|
593
|
$this->assertEqual(0, db_query("SELECT COUNT(*) FROM {queue} WHERE name = 'feeds_source_import'")->fetchField());
|
594
|
}
|
595
|
|
596
|
/**
|
597
|
* Tests clearing items in background.
|
598
|
*/
|
599
|
public function testClearInBackground() {
|
600
|
// Just remove the mappings rather than creating a new importer.
|
601
|
$this->removeMappings('syndication', $this->getCurrentMappings('syndication'));
|
602
|
|
603
|
$this->setSettings('syndication', NULL, array(
|
604
|
'content_type' => '',
|
605
|
));
|
606
|
|
607
|
$this->setPlugin('syndication', 'FeedsFileFetcher');
|
608
|
$this->setPlugin('syndication', 'FeedsCSVParser');
|
609
|
|
610
|
$this->addMappings('syndication', array(
|
611
|
0 => array(
|
612
|
'source' => 'title',
|
613
|
'target' => 'title',
|
614
|
),
|
615
|
1 => array(
|
616
|
'source' => 'GUID',
|
617
|
'target' => 'guid',
|
618
|
'unique' => TRUE,
|
619
|
),
|
620
|
));
|
621
|
|
622
|
// Import 86 nodes.
|
623
|
$this->importFile('syndication', $this->absolutePath() . '/tests/feeds/many_nodes_ordered.csv');
|
624
|
$this->assertEqual(86, db_query("SELECT COUNT(*) FROM {node}")->fetchField());
|
625
|
|
626
|
// Turn on "Process in background option" and turn off periodic import.
|
627
|
$this->setSettings('syndication', NULL, array(
|
628
|
'process_in_background' => TRUE,
|
629
|
'import_period' => FEEDS_SCHEDULE_NEVER,
|
630
|
));
|
631
|
|
632
|
// Now schedule clearing in background.
|
633
|
$this->drupalPost('import/syndication/delete-items', array(), t('Schedule delete'));
|
634
|
$this->assertEqual(86, db_query("SELECT COUNT(*) FROM {node}")->fetchField());
|
635
|
|
636
|
// Assert that the delete button is disabled.
|
637
|
$this->drupalGet('import/syndication/delete-items');
|
638
|
$this->assertFieldDisabled('op');
|
639
|
|
640
|
// Assert that there is one clear task in the queue.
|
641
|
$this->assertEqual(1, db_query("SELECT COUNT(*) FROM {queue} WHERE name = 'feeds_source_clear'")->fetchField());
|
642
|
|
643
|
// And run cron.
|
644
|
$this->cronRun();
|
645
|
$this->assertEqual(0, db_query("SELECT COUNT(*) FROM {node}")->fetchField());
|
646
|
|
647
|
// Assert that the delete button is no longer disabled.
|
648
|
$this->drupalGet('import/syndication/delete-items');
|
649
|
$this->assertFieldEnabled('op');
|
650
|
|
651
|
// Assert that there are no more clear tasks in the queue.
|
652
|
$this->assertEqual(0, db_query("SELECT COUNT(*) FROM {queue} WHERE name = 'feeds_source_clear'")->fetchField());
|
653
|
}
|
654
|
|
655
|
/**
|
656
|
* Tests unlocking a feed.
|
657
|
*/
|
658
|
public function testUnlock() {
|
659
|
// Just remove the mappings rather than creating a new importer.
|
660
|
$this->removeMappings('syndication', $this->getCurrentMappings('syndication'));
|
661
|
|
662
|
$this->setPlugin('syndication', 'FeedsFileFetcher');
|
663
|
$this->setPlugin('syndication', 'FeedsCSVParser');
|
664
|
|
665
|
$this->setSettings('syndication', NULL, array(
|
666
|
'content_type' => '',
|
667
|
'process_in_background' => TRUE,
|
668
|
'import_period' => FEEDS_SCHEDULE_NEVER,
|
669
|
));
|
670
|
|
671
|
$this->addMappings('syndication', array(
|
672
|
0 => array(
|
673
|
'source' => 'title',
|
674
|
'target' => 'title',
|
675
|
),
|
676
|
1 => array(
|
677
|
'source' => 'GUID',
|
678
|
'target' => 'guid',
|
679
|
'unique' => TRUE,
|
680
|
),
|
681
|
));
|
682
|
|
683
|
$this->importFile('syndication', $this->absolutePath() . '/tests/feeds/many_nodes_ordered.csv', 'Schedule import');
|
684
|
$this->assertEqual(0, db_query("SELECT COUNT(*) FROM {node}")->fetchField());
|
685
|
|
686
|
// Assert that the import button is disabled.
|
687
|
$this->assertFieldDisabled('op');
|
688
|
|
689
|
// Assert that there is one import task in the queue.
|
690
|
$this->assertEqual(1, db_query("SELECT COUNT(*) FROM {queue} WHERE name = 'feeds_source_import'")->fetchField());
|
691
|
|
692
|
// Now unlock the feed.
|
693
|
$this->drupalPost('import/syndication/unlock', array(), 'Unlock');
|
694
|
$this->assertText('Importer unlocked.');
|
695
|
|
696
|
// Assert that the unlock button is now disabled.
|
697
|
$this->drupalGet('import/syndication/unlock');
|
698
|
$this->assertFieldDisabled('op');
|
699
|
$this->assertText('This importer is not locked, therefore it cannot be unlocked.');
|
700
|
|
701
|
// And assert that the import button is no longer disabled.
|
702
|
$this->drupalGet('import/syndication');
|
703
|
$this->assertFieldEnabled('op');
|
704
|
}
|
705
|
|
706
|
/**
|
707
|
* Tests skip new items.
|
708
|
*/
|
709
|
public function testSkipNewItems() {
|
710
|
// Include FeedsProcessor.inc so processor related constants are available.
|
711
|
module_load_include('inc', 'feeds', 'plugins/FeedsProcessor');
|
712
|
|
713
|
// Attach to standalone importer.
|
714
|
$this->setSettings('syndication', NULL, array('content_type' => ''));
|
715
|
// Set that new items should not be imported.
|
716
|
$this->setSettings('syndication', 'FeedsNodeProcessor', array(
|
717
|
'insert_new' => FEEDS_SKIP_NEW,
|
718
|
'update_existing' => FEEDS_SKIP_EXISTING,
|
719
|
));
|
720
|
|
721
|
// Make title unique target.
|
722
|
$this->removeMappings('syndication', $this->getCurrentMappings('syndication'));
|
723
|
$this->addMappings('syndication', array(
|
724
|
0 => array(
|
725
|
'source' => 'title',
|
726
|
'target' => 'title',
|
727
|
'unique' => TRUE,
|
728
|
),
|
729
|
1 => array(
|
730
|
'source' => 'description',
|
731
|
'target' => 'body',
|
732
|
),
|
733
|
2 => array(
|
734
|
'source' => 'timestamp',
|
735
|
'target' => 'created',
|
736
|
),
|
737
|
));
|
738
|
|
739
|
// Do a first import, no nodes should be created.
|
740
|
$edit = array(
|
741
|
'feeds[FeedsHTTPFetcher][source]' => $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'feeds') . '/tests/feeds/developmentseed.rss2',
|
742
|
);
|
743
|
$this->drupalPost('import/syndication', $edit, 'Import');
|
744
|
$this->assertText('There are no new nodes');
|
745
|
|
746
|
// Now create two nodes with titles that are present in the source
|
747
|
// "developmentseed.rss2".
|
748
|
$this->drupalCreateNode(array(
|
749
|
'type' => 'article',
|
750
|
'title' => 'Open Atrium Translation Workflow: Two Way Translation Updates',
|
751
|
));
|
752
|
$this->drupalCreateNode(array(
|
753
|
'type' => 'article',
|
754
|
'title' => 'Week in DC Tech: October 5th Edition',
|
755
|
));
|
756
|
|
757
|
// Import again. Since the processor is set to not update as well, nothing
|
758
|
// should be imported.
|
759
|
$this->drupalPost('import/syndication', array(), 'Import');
|
760
|
$this->assertText('There are no new nodes');
|
761
|
|
762
|
// Now set importer to update existing.
|
763
|
$this->setSettings('syndication', 'FeedsNodeProcessor', array(
|
764
|
'update_existing' => FEEDS_UPDATE_EXISTING,
|
765
|
));
|
766
|
// And import again. Two nodes should be updated.
|
767
|
$this->drupalPost('import/syndication', array(), 'Import');
|
768
|
$this->assertText('Updated 2 nodes.');
|
769
|
|
770
|
// Change "insert_new" setting to insert new items to verify if changing the
|
771
|
// setting later has the effect that new items will be imported as yet.
|
772
|
$this->setSettings('syndication', 'FeedsNodeProcessor', array(
|
773
|
'insert_new' => FEEDS_INSERT_NEW,
|
774
|
));
|
775
|
// Import. Eight nodes should be created. No nodes should be updated.
|
776
|
$this->drupalPost('import/syndication', array(), 'Import');
|
777
|
$this->assertText('Created 8 nodes.');
|
778
|
$this->assertNoText('Updated 2 nodes.');
|
779
|
}
|
780
|
|
781
|
/**
|
782
|
* Tests mapping to the node author using a float value.
|
783
|
*
|
784
|
* @see feeds_tests_feeds_after_parse()
|
785
|
*/
|
786
|
public function testUidTargetWithFloat() {
|
787
|
// Set flag to turn uid values to floats.
|
788
|
variable_set('feeds_tests_feeds_after_parse_uid_float_value', TRUE);
|
789
|
|
790
|
// Rebuild node type information.
|
791
|
drupal_static_reset();
|
792
|
node_types_rebuild();
|
793
|
|
794
|
// Create a role with permission to create articles.
|
795
|
$rid = $this->drupalCreateRole(array(
|
796
|
'access content',
|
797
|
'create article content',
|
798
|
));
|
799
|
|
800
|
// Create account with uid 201.
|
801
|
user_save(drupal_anonymous_user(), array(
|
802
|
'uid' => 201,
|
803
|
'name' => 'Morticia',
|
804
|
'mail' => 'morticia@example.com',
|
805
|
'pass' => 'mort',
|
806
|
'status' => 1,
|
807
|
'roles' => array(
|
808
|
$rid => $rid,
|
809
|
),
|
810
|
));
|
811
|
|
812
|
// Create account with uid 202.
|
813
|
user_save(drupal_anonymous_user(), array(
|
814
|
'uid' => 202,
|
815
|
'name' => 'Joe',
|
816
|
'mail' => 'joe@example.com',
|
817
|
'pass' => 'joe',
|
818
|
'status' => 1,
|
819
|
'roles' => array(
|
820
|
$rid => $rid,
|
821
|
),
|
822
|
));
|
823
|
|
824
|
// Create and configure importer.
|
825
|
$this->createImporterConfiguration('Content CSV', 'csv');
|
826
|
$this->setSettings('csv', NULL, array(
|
827
|
'content_type' => '',
|
828
|
'import_period' => FEEDS_SCHEDULE_NEVER,
|
829
|
));
|
830
|
$this->setPlugin('csv', 'FeedsFileFetcher');
|
831
|
$this->setPlugin('csv', 'FeedsCSVParser');
|
832
|
// Ensure that the "Authorize" option is enabled.
|
833
|
$this->setSettings('csv', 'FeedsNodeProcessor', array(
|
834
|
'authorize' => TRUE,
|
835
|
));
|
836
|
$this->addMappings('csv', array(
|
837
|
0 => array(
|
838
|
'source' => 'title',
|
839
|
'target' => 'title',
|
840
|
),
|
841
|
1 => array(
|
842
|
'source' => 'uid',
|
843
|
'target' => 'uid',
|
844
|
),
|
845
|
));
|
846
|
|
847
|
// Import CSV file.
|
848
|
$this->importFile('csv', $this->absolutePath() . '/tests/feeds/content_uid.csv');
|
849
|
$this->assertText('Created 2 nodes');
|
850
|
|
851
|
// And assert that the two created nodes have the expected author assigned.
|
852
|
$expected_values = array(
|
853
|
1 => array(
|
854
|
'uid' => 201,
|
855
|
),
|
856
|
2 => array(
|
857
|
'uid' => 202,
|
858
|
),
|
859
|
);
|
860
|
for ($i = 1; $i <= 2; $i++) {
|
861
|
$node = node_load($i);
|
862
|
$this->assertEqual($expected_values[$i]['uid'], $node->uid);
|
863
|
}
|
864
|
}
|
865
|
|
866
|
/**
|
867
|
* Tests if the target "changed" works as expected.
|
868
|
*/
|
869
|
public function testChangedTarget() {
|
870
|
// Create and configure importer.
|
871
|
$this->createImporterConfiguration('Content CSV', 'csv');
|
872
|
$this->setSettings('csv', NULL, array('content_type' => '', 'import_period' => FEEDS_SCHEDULE_NEVER));
|
873
|
$this->setPlugin('csv', 'FeedsFileFetcher');
|
874
|
$this->setPlugin('csv', 'FeedsCSVParser');
|
875
|
$this->addMappings('csv', array(
|
876
|
0 => array(
|
877
|
'source' => 'title',
|
878
|
'target' => 'title',
|
879
|
),
|
880
|
// Borrow the timestamp value from the "created" column in the csv.
|
881
|
1 => array(
|
882
|
'source' => 'created',
|
883
|
'target' => 'changed',
|
884
|
),
|
885
|
));
|
886
|
|
887
|
// Import CSV file.
|
888
|
$this->importFile('csv', $this->absolutePath() . '/tests/feeds/content.csv');
|
889
|
$this->assertText('Created 2 nodes');
|
890
|
|
891
|
// Assert changed date of nodes.
|
892
|
$expected_values = array(
|
893
|
1 => array(
|
894
|
'changed' => 1251936720,
|
895
|
),
|
896
|
2 => array(
|
897
|
'changed' => 1251932360,
|
898
|
),
|
899
|
);
|
900
|
for ($i = 1; $i <= 2; $i++) {
|
901
|
$node = node_load($i);
|
902
|
$this->assertEqual($expected_values[$i]['changed'], $node->changed);
|
903
|
}
|
904
|
}
|
905
|
|
906
|
/**
|
907
|
* Tests the FeedsSource::pushImport() method.
|
908
|
*/
|
909
|
public function testPushImport() {
|
910
|
// Attach to standalone importer.
|
911
|
$this->setSettings('syndication', NULL, array('content_type' => ''));
|
912
|
|
913
|
$raw = file_get_contents(dirname(__FILE__) . '/feeds/developmentseed.rss2');
|
914
|
feeds_source('syndication', 0)->pushImport(new FeedsFetcherResult($raw));
|
915
|
$this->assertEqual(10, db_query("SELECT COUNT(*) FROM {node}")->fetchField());
|
916
|
}
|
917
|
|
918
|
/**
|
919
|
* Tests the FeedsSource::pushImport() method with a CSV file.
|
920
|
*/
|
921
|
public function testPushImportWithCSV() {
|
922
|
// Attach to standalone importer and configure.
|
923
|
$this->setSettings('syndication', NULL, array('content_type' => ''));
|
924
|
$this->setPlugin('syndication', 'FeedsCSVParser');
|
925
|
$this->removeMappings('syndication', $this->getCurrentMappings('syndication'));
|
926
|
$this->addMappings('syndication', array(
|
927
|
0 => array(
|
928
|
'source' => 'title',
|
929
|
'target' => 'title',
|
930
|
),
|
931
|
));
|
932
|
|
933
|
$raw = file_get_contents($this->absolutePath() . '/tests/feeds/many_nodes.csv');
|
934
|
feeds_source('syndication', 0)->pushImport(new FeedsFetcherResult($raw));
|
935
|
$this->assertEqual(86, db_query("SELECT COUNT(*) FROM {node}")->fetchField());
|
936
|
}
|
937
|
|
938
|
/**
|
939
|
* Tests if target item is not updated when only non-mapped data on the source changed.
|
940
|
*/
|
941
|
public function testIrrelevantUpdate() {
|
942
|
// Include FeedsProcessor.inc so processor related constants are available.
|
943
|
module_load_include('inc', 'feeds', 'plugins/FeedsProcessor');
|
944
|
|
945
|
// Attach to standalone importer and configure.
|
946
|
$this->setSettings('syndication', NULL, array('content_type' => ''));
|
947
|
$this->setPlugin('syndication', 'FeedsFileFetcher');
|
948
|
$this->setPlugin('syndication', 'FeedsCSVParser');
|
949
|
$this->removeMappings('syndication', $this->getCurrentMappings('syndication'));
|
950
|
$this->addMappings('syndication', array(
|
951
|
0 => array(
|
952
|
'source' => 'name',
|
953
|
'target' => 'title',
|
954
|
'unique' => TRUE,
|
955
|
),
|
956
|
));
|
957
|
|
958
|
// Import file.
|
959
|
$this->importFile('syndication', $this->absolutePath() . '/tests/feeds/users.csv');
|
960
|
$this->assertText('Created 5 nodes');
|
961
|
|
962
|
// Ensure that no nodes are updated when only non-mapped columns changed.
|
963
|
$this->setSettings('syndication', 'FeedsNodeProcessor', array(
|
964
|
'skip_hash_check' => FALSE,
|
965
|
'update_existing' => FEEDS_UPDATE_EXISTING,
|
966
|
));
|
967
|
$this->importFile('syndication', $this->absolutePath() . '/tests/feeds/users_updated.csv');
|
968
|
$this->assertText('There are no new nodes.');
|
969
|
}
|
970
|
|
971
|
}
|