1 |
85ad3d82
|
Assos Assos
|
<?php
|
2 |
|
|
|
3 |
|
|
/**
|
4 |
|
|
* @file
|
5 |
|
|
* FeedsImporter class and related.
|
6 |
|
|
*/
|
7 |
|
|
|
8 |
|
|
/**
|
9 |
|
|
* A FeedsImporter object describes how an external source should be fetched,
|
10 |
|
|
* parsed and processed. Feeds can manage an arbitrary amount of importers.
|
11 |
|
|
*
|
12 |
|
|
* A FeedsImporter holds a pointer to a FeedsFetcher, a FeedsParser and a
|
13 |
|
|
* FeedsProcessor plugin. It further contains the configuration for itself and
|
14 |
|
|
* each of the three plugins.
|
15 |
|
|
*
|
16 |
|
|
* Its most important responsibilities are configuration management, interfacing
|
17 |
|
|
* with the job scheduler and expiring of all items produced by this
|
18 |
|
|
* importer.
|
19 |
|
|
*
|
20 |
|
|
* When a FeedsImporter is instantiated, it loads its configuration. Then it
|
21 |
|
|
* instantiates one fetcher, one parser and one processor plugin depending on
|
22 |
|
|
* the configuration information. After instantiating them, it sets them to
|
23 |
|
|
* the configuration information it holds for them.
|
24 |
|
|
*/
|
25 |
|
|
class FeedsImporter extends FeedsConfigurable {
|
26 |
|
|
|
27 |
|
|
// Every feed has a fetcher, a parser and a processor.
|
28 |
|
|
// These variable names match the possible return values of
|
29 |
|
|
// FeedsPlugin::typeOf().
|
30 |
|
|
protected $fetcher, $parser, $processor;
|
31 |
|
|
|
32 |
|
|
// This array defines the variable names of the plugins above.
|
33 |
|
|
protected $plugin_types = array('fetcher', 'parser', 'processor');
|
34 |
|
|
|
35 |
|
|
/**
|
36 |
|
|
* Instantiate class variables, initialize and configure
|
37 |
|
|
* plugins.
|
38 |
|
|
*/
|
39 |
|
|
protected function __construct($id) {
|
40 |
|
|
parent::__construct($id);
|
41 |
|
|
|
42 |
|
|
// Try to load information from database.
|
43 |
|
|
$this->load();
|
44 |
|
|
|
45 |
|
|
// Instantiate fetcher, parser and processor, set their configuration if
|
46 |
|
|
// stored info is available.
|
47 |
|
|
foreach ($this->plugin_types as $type) {
|
48 |
|
|
$plugin = feeds_plugin($this->config[$type]['plugin_key'], $this->id);
|
49 |
|
|
|
50 |
|
|
if (isset($this->config[$type]['config'])) {
|
51 |
|
|
$plugin->setConfig($this->config[$type]['config']);
|
52 |
|
|
}
|
53 |
|
|
$this->$type = $plugin;
|
54 |
|
|
}
|
55 |
|
|
}
|
56 |
|
|
|
57 |
|
|
/**
|
58 |
|
|
* Report how many items *should* be created on one page load by this
|
59 |
|
|
* importer.
|
60 |
|
|
*
|
61 |
|
|
* Note:
|
62 |
|
|
*
|
63 |
|
|
* It depends on whether parser implements batching if this limit is actually
|
64 |
|
|
* respected. Further, if no limit is reported it doesn't mean that the
|
65 |
|
|
* number of items that can be created on one page load is actually without
|
66 |
|
|
* limit.
|
67 |
|
|
*
|
68 |
|
|
* @return
|
69 |
|
|
* A positive number defining the number of items that can be created on
|
70 |
|
|
* one page load. 0 if this number is unlimited.
|
71 |
|
|
*/
|
72 |
|
|
public function getLimit() {
|
73 |
|
|
return $this->processor->getLimit();
|
74 |
|
|
}
|
75 |
|
|
|
76 |
|
|
/**
|
77 |
|
|
* Save configuration.
|
78 |
|
|
*/
|
79 |
|
|
public function save() {
|
80 |
|
|
$save = new stdClass();
|
81 |
|
|
$save->id = $this->id;
|
82 |
|
|
$save->config = $this->getConfig();
|
83 |
|
|
|
84 |
|
|
if ($config = db_query("SELECT config FROM {feeds_importer} WHERE id = :id", array(':id' => $this->id))->fetchField()) {
|
85 |
|
|
drupal_write_record('feeds_importer', $save, 'id');
|
86 |
|
|
// Only rebuild menu if content_type has changed. Don't worry about
|
87 |
|
|
// rebuilding menus when creating a new importer since it will default
|
88 |
|
|
// to the standalone page.
|
89 |
|
|
$config = unserialize($config);
|
90 |
|
|
if ($config['content_type'] != $save->config['content_type']) {
|
91 |
|
|
variable_set('menu_rebuild_needed', TRUE);
|
92 |
|
|
}
|
93 |
|
|
}
|
94 |
|
|
else {
|
95 |
|
|
drupal_write_record('feeds_importer', $save);
|
96 |
|
|
}
|
97 |
|
|
}
|
98 |
|
|
|
99 |
|
|
/**
|
100 |
|
|
* Load configuration and unpack.
|
101 |
|
|
*/
|
102 |
|
|
public function load() {
|
103 |
|
|
ctools_include('export');
|
104 |
|
|
if ($config = ctools_export_load_object('feeds_importer', 'conditions', array('id' => $this->id))) {
|
105 |
|
|
$config = array_shift($config);
|
106 |
|
|
$this->export_type = $config->export_type;
|
107 |
|
|
$this->disabled = isset($config->disabled) ? $config->disabled : FALSE;
|
108 |
|
|
$this->config = $config->config;
|
109 |
|
|
return TRUE;
|
110 |
|
|
}
|
111 |
|
|
return FALSE;
|
112 |
|
|
}
|
113 |
|
|
|
114 |
|
|
/**
|
115 |
41cc1b08
|
Assos Assos
|
* Deletes configuration.
|
116 |
|
|
*
|
117 |
|
|
* Removes configuration information from database, does not delete
|
118 |
|
|
* configuration itself.
|
119 |
85ad3d82
|
Assos Assos
|
*/
|
120 |
|
|
public function delete() {
|
121 |
|
|
db_delete('feeds_importer')
|
122 |
|
|
->condition('id', $this->id)
|
123 |
|
|
->execute();
|
124 |
41cc1b08
|
Assos Assos
|
|
125 |
|
|
feeds_reschedule($this->id);
|
126 |
85ad3d82
|
Assos Assos
|
}
|
127 |
|
|
|
128 |
|
|
/**
|
129 |
|
|
* Set plugin.
|
130 |
|
|
*
|
131 |
|
|
* @param $plugin_key
|
132 |
|
|
* A fetcher, parser or processor plugin.
|
133 |
|
|
*
|
134 |
|
|
* @todo Error handling, handle setting to the same plugin.
|
135 |
|
|
*/
|
136 |
|
|
public function setPlugin($plugin_key) {
|
137 |
|
|
// $plugin_type can be either 'fetcher', 'parser' or 'processor'
|
138 |
|
|
if ($plugin_type = FeedsPlugin::typeOf($plugin_key)) {
|
139 |
|
|
if ($plugin = feeds_plugin($plugin_key, $this->id)) {
|
140 |
|
|
// Unset existing plugin, switch to new plugin.
|
141 |
|
|
unset($this->$plugin_type);
|
142 |
|
|
$this->$plugin_type = $plugin;
|
143 |
|
|
// Set configuration information, blow away any previous information on
|
144 |
|
|
// this spot.
|
145 |
|
|
$this->config[$plugin_type] = array('plugin_key' => $plugin_key);
|
146 |
|
|
}
|
147 |
|
|
}
|
148 |
|
|
}
|
149 |
|
|
|
150 |
|
|
/**
|
151 |
|
|
* Copy a FeedsImporter configuration into this importer.
|
152 |
|
|
*
|
153 |
7295e063
|
Assos Assos
|
* @param FeedsConfigurable $configurable
|
154 |
85ad3d82
|
Assos Assos
|
* The feeds importer object to copy from.
|
155 |
|
|
*/
|
156 |
|
|
public function copy(FeedsConfigurable $configurable) {
|
157 |
|
|
parent::copy($configurable);
|
158 |
|
|
|
159 |
|
|
if ($configurable instanceof FeedsImporter) {
|
160 |
|
|
// Instantiate new fetcher, parser and processor and initialize their
|
161 |
|
|
// configurations.
|
162 |
|
|
foreach ($this->plugin_types as $plugin_type) {
|
163 |
|
|
$this->setPlugin($configurable->config[$plugin_type]['plugin_key']);
|
164 |
|
|
$this->$plugin_type->setConfig($configurable->config[$plugin_type]['config']);
|
165 |
|
|
}
|
166 |
|
|
}
|
167 |
|
|
}
|
168 |
|
|
|
169 |
|
|
/**
|
170 |
|
|
* Get configuration of this feed.
|
171 |
|
|
*/
|
172 |
|
|
public function getConfig() {
|
173 |
|
|
foreach (array('fetcher', 'parser', 'processor') as $type) {
|
174 |
|
|
$this->config[$type]['config'] = $this->$type->getConfig();
|
175 |
|
|
}
|
176 |
|
|
return parent::getConfig();
|
177 |
|
|
}
|
178 |
|
|
|
179 |
|
|
/**
|
180 |
|
|
* Return defaults for feed configuration.
|
181 |
|
|
*/
|
182 |
|
|
public function configDefaults() {
|
183 |
|
|
return array(
|
184 |
|
|
'name' => '',
|
185 |
|
|
'description' => '',
|
186 |
|
|
'fetcher' => array(
|
187 |
|
|
'plugin_key' => 'FeedsHTTPFetcher',
|
188 |
|
|
),
|
189 |
|
|
'parser' => array(
|
190 |
|
|
'plugin_key' => 'FeedsSyndicationParser',
|
191 |
|
|
),
|
192 |
|
|
'processor' => array(
|
193 |
|
|
'plugin_key' => 'FeedsNodeProcessor',
|
194 |
|
|
),
|
195 |
|
|
'content_type' => '',
|
196 |
|
|
'update' => 0,
|
197 |
|
|
'import_period' => 1800, // Refresh every 30 minutes by default.
|
198 |
|
|
'expire_period' => 3600, // Expire every hour by default, this is a hidden setting.
|
199 |
|
|
'import_on_create' => TRUE, // Import on submission.
|
200 |
|
|
'process_in_background' => FALSE,
|
201 |
|
|
);
|
202 |
|
|
}
|
203 |
|
|
|
204 |
|
|
/**
|
205 |
|
|
* Override parent::configForm().
|
206 |
|
|
*/
|
207 |
|
|
public function configForm(&$form_state) {
|
208 |
|
|
$config = $this->getConfig();
|
209 |
|
|
$form = array();
|
210 |
|
|
$form['name'] = array(
|
211 |
|
|
'#type' => 'textfield',
|
212 |
|
|
'#title' => t('Name'),
|
213 |
|
|
'#description' => t('A human readable name of this importer.'),
|
214 |
|
|
'#default_value' => $config['name'],
|
215 |
|
|
'#required' => TRUE,
|
216 |
|
|
);
|
217 |
|
|
$form['description'] = array(
|
218 |
|
|
'#type' => 'textfield',
|
219 |
|
|
'#title' => t('Description'),
|
220 |
|
|
'#description' => t('A description of this importer.'),
|
221 |
|
|
'#default_value' => $config['description'],
|
222 |
|
|
);
|
223 |
|
|
$node_types = node_type_get_names();
|
224 |
|
|
array_walk($node_types, 'check_plain');
|
225 |
|
|
$form['content_type'] = array(
|
226 |
|
|
'#type' => 'select',
|
227 |
|
|
'#title' => t('Attach to content type'),
|
228 |
|
|
'#description' => t('If "Use standalone form" is selected a source is imported by using a form under !import_form.
|
229 |
|
|
If a content type is selected a source is imported by creating a node of that content type.',
|
230 |
|
|
array('!import_form' => l(url('import', array('absolute' => TRUE)), 'import', array('attributes' => array('target' => '_new'))))),
|
231 |
|
|
'#options' => array('' => t('Use standalone form')) + $node_types,
|
232 |
|
|
'#default_value' => $config['content_type'],
|
233 |
|
|
);
|
234 |
|
|
$cron_required = ' ' . l(t('Requires cron to be configured.'), 'http://drupal.org/cron', array('attributes' => array('target' => '_new')));
|
235 |
|
|
$period = drupal_map_assoc(array(900, 1800, 3600, 10800, 21600, 43200, 86400, 259200, 604800, 2419200), 'format_interval');
|
236 |
|
|
foreach ($period as &$p) {
|
237 |
|
|
$p = t('Every !p', array('!p' => $p));
|
238 |
|
|
}
|
239 |
|
|
$period = array(
|
240 |
|
|
FEEDS_SCHEDULE_NEVER => t('Off'),
|
241 |
|
|
0 => t('As often as possible'),
|
242 |
|
|
) + $period;
|
243 |
|
|
$form['import_period'] = array(
|
244 |
|
|
'#type' => 'select',
|
245 |
|
|
'#title' => t('Periodic import'),
|
246 |
|
|
'#options' => $period,
|
247 |
|
|
'#description' => t('Choose how often a source should be imported periodically.') . $cron_required,
|
248 |
|
|
'#default_value' => $config['import_period'],
|
249 |
|
|
);
|
250 |
|
|
$form['import_on_create'] = array(
|
251 |
|
|
'#type' => 'checkbox',
|
252 |
|
|
'#title' => t('Import on submission'),
|
253 |
|
|
'#description' => t('Check if import should be started at the moment a standalone form or node form is submitted.'),
|
254 |
|
|
'#default_value' => $config['import_on_create'],
|
255 |
|
|
);
|
256 |
|
|
$form['process_in_background'] = array(
|
257 |
|
|
'#type' => 'checkbox',
|
258 |
|
|
'#title' => t('Process in background'),
|
259 |
|
|
'#description' => t('For very large imports. If checked, import and delete tasks started from the web UI will be handled by a cron task in the background rather than by the browser. This does not affect periodic imports, they are handled by a cron task in any case.') . $cron_required,
|
260 |
|
|
'#default_value' => $config['process_in_background'],
|
261 |
|
|
);
|
262 |
|
|
return $form;
|
263 |
|
|
}
|
264 |
|
|
|
265 |
|
|
/**
|
266 |
|
|
* Reschedule if import period changes.
|
267 |
|
|
*/
|
268 |
|
|
public function configFormSubmit(&$values) {
|
269 |
|
|
if ($this->config['import_period'] != $values['import_period']) {
|
270 |
|
|
feeds_reschedule($this->id);
|
271 |
|
|
}
|
272 |
|
|
parent::configFormSubmit($values);
|
273 |
|
|
}
|
274 |
41cc1b08
|
Assos Assos
|
|
275 |
|
|
/**
|
276 |
|
|
* Implements FeedsConfigurable::dependencies().
|
277 |
|
|
*/
|
278 |
|
|
public function dependencies() {
|
279 |
|
|
$dependencies = parent::dependencies();
|
280 |
|
|
foreach ($this->plugin_types as $plugin_type) {
|
281 |
|
|
$dependencies = array_merge($dependencies, $this->$plugin_type->dependencies());
|
282 |
|
|
}
|
283 |
|
|
return $dependencies;
|
284 |
|
|
}
|
285 |
85ad3d82
|
Assos Assos
|
}
|
286 |
|
|
|
287 |
|
|
/**
|
288 |
|
|
* Helper, see FeedsDataProcessor class.
|
289 |
|
|
*/
|
290 |
|
|
function feeds_format_expire($timestamp) {
|
291 |
|
|
if ($timestamp == FEEDS_EXPIRE_NEVER) {
|
292 |
|
|
return t('Never');
|
293 |
|
|
}
|
294 |
|
|
return t('after !time', array('!time' => format_interval($timestamp)));
|
295 |
|
|
} |