Newer
Older
Alex Barth
committed
<?php
// $Id$
/**
* @file
* Class definition of FeedsFeedNodeProcessor.
*/
/**
* Creates *feed* nodes from feed items. The difference to FeedsNodeProcessor is
* that this plugin only creates nodes that are feed nodes themselves.
*/
class FeedsFeedNodeProcessor extends FeedsProcessor {
/**
* Implementation of FeedsProcessor::process().
*/
public function process(FeedsParserResult $parserResult, FeedsSource $source) {
// Count number of created and updated nodes.
$created = $updated = 0;
foreach ($parserResult->value['items'] as $item) {
// If the target item does not exist OR if update_existing is enabled,
// map and save.
if (!$nid = $this->existingItemId($item, $source) || $this->config['update_existing']) {
// Map item to a node.
$node = $this->map($item);
// If updating populate nid and vid avoiding an expensive node_load().
Alex Barth
committed
if (!empty($nid)) {
$node->nid = $nid;
$node->vid = db_result(db_query('SELECT vid FROM {node} WHERE nid = %d', $nid));
Alex Barth
committed
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
}
// Save the node.
node_save($node);
if ($nid) {
$updated++;
}
else {
$created++;
}
}
}
// Set messages.
if ($created) {
drupal_set_message(t('Created !number !type nodes.', array('!number' => $created, '!type' => $this->config['content_type'])));
}
elseif ($updated) {
drupal_set_message(t('Updated !number !type nodes.', array('!number' => $updated, '!type' => $this->config['content_type'])));
}
else {
drupal_set_message(t('There is no new content.'));
}
}
/**
* Implementation of FeedsProcessor::clear().
*/
public function clear(FeedsSource $source) {
// Do not support deleting imported items as we would have to delete all
// items of the content type we imported which may contain nodes that a
// user created by hand.
throw new Exception(t('This configuration does not support deleting imported items.'));
}
/**
* Execute mapping on an item.
*/
protected function map($source_item) {
// Prepare node object.
static $included;
if (!$included) {
module_load_include('inc', 'node', 'node.pages');
$included = TRUE;
}
$target_node = new stdClass();
$target_node->type = $this->config['content_type'];
$target_node->feeds = array();
// Suppress auto import, we may be creating many feeds
$target_node->feeds['suppress_import'] = TRUE;
node_object_prepare($target_node);
/*
Assign an aggregated node always to current user.
@todo: this won't work in all cases as the assumption here is that
import is happening one off when user is logged in. Assumption breaks if
feed node processor is being used for aggregation on cron time and a
specific user should still be the owner of the imported feed nodes.
*/
global $user;
$target_node->uid = $user->uid;
// Have parent class do the iterating.
return parent::map($source_item, $target_node);
}
/**
* Override parent::configDefaults().
*/
public function configDefaults() {
return array(
'content_type' => '',
'update_existing' => 0,
'mappings' => array(),
);
}
/**
* Override parent::configForm().
*/
public function configForm(&$form_state) {
$feeds = feeds_importer_load_all();
$types = array();
foreach ($feeds as $feed) {
if ($feed->id != $this->id && !empty($feed->config['content_type'])) {
$types[$feed->config['content_type']] = node_get_types('name', $feed->config['content_type']);
}
}
if (empty($types)) {
$types[''] = t('No feed node content type available');
// @todo: configForm() is executed 4 times - why?
// drupal_set_message(t('There is no feed content type available. In order to import feed nodes, you must first create at least configuration that is attached to a content type.'), 'error');
}
else {
$types = array(
'' => t('Select'),
) + $types;
}
$form = array();
$form['content_type'] = array(
'#type' => 'select',
'#title' => t('Content type'),
'#description' => t('Choose node type to create from this feed. Only node types with attached importer configurations are listed here. <strong>Note:</strong> Users with "import !feed_id feeds" permissions will be able to <strong>import</strong> nodes of the content type selected here regardless of the node level permissions. However, users with "clear !feed_id permissions" need to have sufficient node level permissions to delete the imported nodes.', array('!feed_id' => $this->id)),
'#options' => $types,
'#default_value' => $this->config['content_type'],
);
$form['update_existing'] = array(
'#type' => 'checkbox',
'#title' => t('Update existing items'),
'#description' => t('Check if existing items should be updated from the feed.'),
'#default_value' => $this->config['update_existing'],
);
return $form;
}
/**
* Override setTargetElement to operate on a target item that is a node.
*/
public function setTargetElement($target_node, $target_element, $value) {
if ($target_element == 'source') {
// Get the class of the feed node importer's fetcher and set the source
// property. See feeds_nodeapi() how $node->feeds gets stored.
$class = get_class($this->feedNodeImporter()->fetcher);
$target_node->feeds[$class]['source'] = $value;
}
elseif ($target_element == 'body') {
$target_node->teaser = $value;
$target_node->body = $value;
}
elseif (in_array($target_element, array('title', 'status', 'created'))) {
Alex Barth
committed
$target_node->$target_element = $value;
}
}
/**
* Return available mapping targets.
*/
public function getMappingTargets() {
$targets = array(
'title' => array(
'name' => t('Title'),
'description' => t('The title of the feed node.'),
Alex Barth
committed
),
'status' => array(
'name' => t('Published status'),
'description' => t('Whether a feed node is published or not. 1 stands for published, 0 for not published.'),
Alex Barth
committed
),
'created' => array(
'name' => t('Published date'),
'description' => t('The UNIX time when a node has been published.'),
Alex Barth
committed
),
Alex Barth
committed
'name' => t('Body'),
'description' => t('The body of the node. The teaser will be the same as the entire body.'),
Alex Barth
committed
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
),
'source' => array(
'name' => t('Feed source'),
'description' => t('Depending on the selected fetcher, this could be for example a URL or a path to a file.'),
'optional_unique' => TRUE,
),
);
return $targets;
}
/**
* Get nid of an existing feed item node if available.
*/
protected function existingItemId($source_item, FeedsSource $source) {
// We only support one unique target: source
foreach ($this->uniqueTargets($source_item) as $target => $value) {
if ($target == 'source') {
return db_result(db_query('SELECT fs.feed_nid FROM {node} n JOIN {feeds_source} fs ON n.nid = fs.feed_nid WHERE fs.id = "%s" AND fs.source = "%s"', $this->feedNodeImporter()->id, $value));
}
}
return 0;
}
/**
* Helper for retrieving the importer object for the feed nodes to produce.
*/
protected function feedNodeImporter() {
if ($id = feeds_get_importer_id($this->config['content_type'])) {
return feeds_importer($id);
Alex Barth
committed
}
else {
throw new Exception(t('Content type to be created is not a valid Feed content type.'));
}
}
}