[177a560] | 1 | <?php |
---|
| 2 | |
---|
| 3 | /** |
---|
| 4 | * @file |
---|
| 5 | * FeedsImporter class and related. |
---|
| 6 | */ |
---|
| 7 | |
---|
| 8 | // Including FeedsImporter.inc automatically includes dependencies. |
---|
| 9 | require_once(dirname(__FILE__) .'/FeedsConfigurable.inc'); |
---|
| 10 | require_once(dirname(__FILE__) .'/FeedsSource.inc'); |
---|
| 11 | require_once(dirname(__FILE__) .'/FeedsBatch.inc'); |
---|
| 12 | |
---|
| 13 | /** |
---|
| 14 | * A FeedsImporter object describes how an external source should be fetched, |
---|
| 15 | * parsed and processed. Feeds can manage an arbitrary amount of importers. |
---|
| 16 | * |
---|
| 17 | * A FeedsImporter holds a pointer to a FeedsFetcher, a FeedsParser and a |
---|
| 18 | * FeedsProcessor plugin. It further contains the configuration for itself and |
---|
| 19 | * each of the three plugins. |
---|
| 20 | * |
---|
| 21 | * Its most important responsibilities are configuration management, interfacing |
---|
| 22 | * with the job scheduler and expiring of all items produced by this |
---|
| 23 | * importer. |
---|
| 24 | * |
---|
| 25 | * When a FeedsImporter is instantiated, it loads its configuration. Then it |
---|
| 26 | * instantiates one fetcher, one parser and one processor plugin depending on |
---|
| 27 | * the configuration information. After instantiating them, it sets them to |
---|
| 28 | * the configuration information it holds for them. |
---|
| 29 | */ |
---|
| 30 | class FeedsImporter extends FeedsConfigurable { |
---|
| 31 | |
---|
| 32 | // Every feed has a fetcher, a parser and a processor. |
---|
| 33 | // These variable names match the possible return values of |
---|
| 34 | // feeds_plugin_type(). |
---|
| 35 | protected $fetcher, $parser, $processor; |
---|
| 36 | |
---|
| 37 | // This array defines the variable names of the plugins above. |
---|
| 38 | protected $plugin_types = array('fetcher', 'parser', 'processor'); |
---|
| 39 | |
---|
| 40 | /** |
---|
| 41 | * Instantiate class variables, initialize and configure |
---|
| 42 | * plugins. |
---|
| 43 | */ |
---|
| 44 | protected function __construct($id) { |
---|
| 45 | parent::__construct($id); |
---|
| 46 | |
---|
| 47 | // Try to load information from database. |
---|
| 48 | $this->load(); |
---|
| 49 | |
---|
| 50 | // Instantiate fetcher, parser and processor, set their configuration if |
---|
| 51 | // stored info is available. |
---|
| 52 | foreach ($this->plugin_types as $type) { |
---|
| 53 | $plugin = feeds_plugin_instance($this->config[$type]['plugin_key'], $this->id); |
---|
| 54 | |
---|
| 55 | if (isset($this->config[$type]['config'])) { |
---|
| 56 | $plugin->setConfig($this->config[$type]['config']); |
---|
| 57 | } |
---|
| 58 | $this->$type = $plugin; |
---|
| 59 | } |
---|
| 60 | } |
---|
| 61 | |
---|
| 62 | /** |
---|
| 63 | * Remove items older than $time. |
---|
| 64 | * |
---|
| 65 | * @param $time |
---|
| 66 | * All items older than FEEDS_REQUEST_TIME - $time will be deleted. If not |
---|
| 67 | * given, internal processor settings will be used. |
---|
| 68 | * |
---|
| 69 | * @return |
---|
| 70 | * FEEDS_BATCH_COMPLETE if the expiry process finished. A decimal between |
---|
| 71 | * 0.0 and 0.9 periodic if expiry is still in progress. |
---|
| 72 | * |
---|
| 73 | * @throws |
---|
| 74 | * Throws Exception if an error occurs when expiring items. |
---|
| 75 | */ |
---|
| 76 | public function expire($time = NULL) { |
---|
| 77 | return $this->processor->expire($time); |
---|
| 78 | } |
---|
| 79 | |
---|
| 80 | /** |
---|
| 81 | * Schedule this importer. |
---|
| 82 | */ |
---|
| 83 | public function schedule() { |
---|
| 84 | $job = array( |
---|
| 85 | 'callback' => 'feeds_importer_expire', |
---|
| 86 | 'type' => $this->id, |
---|
| 87 | 'period' => 0, |
---|
| 88 | 'periodic' => TRUE, |
---|
| 89 | ); |
---|
| 90 | if (FEEDS_EXPIRE_NEVER != $this->processor->expiryTime()) { |
---|
| 91 | $job['period'] = 3600; |
---|
| 92 | job_scheduler()->set($job); |
---|
| 93 | } |
---|
| 94 | else { |
---|
| 95 | job_scheduler()->remove($job); |
---|
| 96 | } |
---|
| 97 | } |
---|
| 98 | |
---|
| 99 | /** |
---|
| 100 | * Save configuration. |
---|
| 101 | */ |
---|
| 102 | public function save() { |
---|
| 103 | $save = new stdClass(); |
---|
| 104 | $save->id = $this->id; |
---|
| 105 | $save->config = $this->getConfig(); |
---|
| 106 | |
---|
| 107 | if ($config = db_result(db_query("SELECT config FROM {feeds_importer} WHERE id = '%s'", $this->id))) { |
---|
| 108 | drupal_write_record('feeds_importer', $save, 'id'); |
---|
| 109 | // Only rebuild menu if content_type has changed. Don't worry about |
---|
| 110 | // rebuilding menus when creating a new importer since it will default |
---|
| 111 | // to the standalone page. |
---|
| 112 | $config = unserialize($config); |
---|
| 113 | if ($config['content_type'] != $save->config['content_type']) { |
---|
| 114 | variable_set('menu_rebuild_needed', TRUE); |
---|
| 115 | } |
---|
| 116 | } |
---|
| 117 | else { |
---|
| 118 | drupal_write_record('feeds_importer', $save); |
---|
| 119 | } |
---|
| 120 | } |
---|
| 121 | |
---|
| 122 | /** |
---|
| 123 | * Load configuration and unpack. |
---|
| 124 | */ |
---|
| 125 | public function load() { |
---|
| 126 | ctools_include('export'); |
---|
| 127 | if ($config = ctools_export_load_object('feeds_importer', 'conditions', array('id' => $this->id))) { |
---|
| 128 | $config = array_shift($config); |
---|
| 129 | $this->export_type = $config->export_type; |
---|
| 130 | $this->disabled = isset($config->disabled) ? $config->disabled : FALSE; |
---|
| 131 | $this->config = $config->config; |
---|
| 132 | return TRUE; |
---|
| 133 | } |
---|
| 134 | return FALSE; |
---|
| 135 | } |
---|
| 136 | |
---|
| 137 | /** |
---|
| 138 | * Delete configuration. Removes configuration information |
---|
| 139 | * from database, does not delete configuration itself. |
---|
| 140 | */ |
---|
| 141 | public function delete() { |
---|
| 142 | db_query("DELETE FROM {feeds_importer} WHERE id = '%s'", $this->id); |
---|
| 143 | $job = array( |
---|
| 144 | 'callback' => 'feeds_importer_expire', |
---|
| 145 | 'type' => $this->id, |
---|
| 146 | 'id' => 0, |
---|
| 147 | ); |
---|
| 148 | if ($this->export_type & EXPORT_IN_CODE) { |
---|
| 149 | feeds_reschedule($this->id); |
---|
| 150 | } |
---|
| 151 | else { |
---|
| 152 | job_scheduler()->remove($job); |
---|
| 153 | } |
---|
| 154 | } |
---|
| 155 | |
---|
| 156 | /** |
---|
| 157 | * Set plugin. |
---|
| 158 | * |
---|
| 159 | * @param $plugin_key |
---|
| 160 | * A fetcher, parser or processor plugin. |
---|
| 161 | * |
---|
| 162 | * @todo Error handling, handle setting to the same plugin. |
---|
| 163 | */ |
---|
| 164 | public function setPlugin($plugin_key) { |
---|
| 165 | // $plugin_type can be either 'fetcher', 'parser' or 'processor' |
---|
| 166 | if ($plugin_type = feeds_plugin_type($plugin_key)) { |
---|
| 167 | if ($plugin = feeds_plugin_instance($plugin_key, $this->id)) { |
---|
| 168 | // Unset existing plugin, switch to new plugin. |
---|
| 169 | unset($this->$plugin_type); |
---|
| 170 | $this->$plugin_type = $plugin; |
---|
| 171 | // Set configuration information, blow away any previous information on |
---|
| 172 | // this spot. |
---|
| 173 | $this->config[$plugin_type] = array('plugin_key' => $plugin_key); |
---|
| 174 | } |
---|
| 175 | } |
---|
| 176 | } |
---|
| 177 | |
---|
| 178 | /** |
---|
| 179 | * Copy a FeedsImporter configuration into this importer. |
---|
| 180 | * |
---|
| 181 | * @param FeedsImporter $importer |
---|
| 182 | * The feeds importer object to copy from. |
---|
| 183 | */ |
---|
| 184 | public function copy(FeedsConfigurable $importer) { |
---|
| 185 | $this->setConfig($importer->config); |
---|
| 186 | |
---|
| 187 | // Instantiate new fetcher, parser and processor and initialize their |
---|
| 188 | // configurations. |
---|
| 189 | foreach ($this->plugin_types as $plugin_type) { |
---|
| 190 | $this->setPlugin($importer->config[$plugin_type]['plugin_key']); |
---|
| 191 | $this->$plugin_type->setConfig($importer->config[$plugin_type]['config']); |
---|
| 192 | } |
---|
| 193 | } |
---|
| 194 | |
---|
| 195 | /** |
---|
| 196 | * Get configuration of this feed. |
---|
| 197 | */ |
---|
| 198 | public function getConfig() { |
---|
| 199 | foreach (array('fetcher', 'parser', 'processor') as $type) { |
---|
| 200 | $this->config[$type]['config'] = $this->$type->getConfig(); |
---|
| 201 | } |
---|
| 202 | return $this->config;// Collect information from plugins. |
---|
| 203 | } |
---|
| 204 | |
---|
| 205 | /** |
---|
| 206 | * Return defaults for feed configuration. |
---|
| 207 | */ |
---|
| 208 | public function configDefaults() { |
---|
| 209 | return array( |
---|
| 210 | 'name' => '', |
---|
| 211 | 'description' => '', |
---|
| 212 | 'fetcher' => array( |
---|
| 213 | 'plugin_key' => 'FeedsHTTPFetcher', |
---|
| 214 | ), |
---|
| 215 | 'parser' => array( |
---|
| 216 | 'plugin_key' => 'FeedsSyndicationParser', |
---|
| 217 | ), |
---|
| 218 | 'processor' => array( |
---|
| 219 | 'plugin_key' => 'FeedsNodeProcessor', |
---|
| 220 | ), |
---|
| 221 | 'content_type' => '', |
---|
| 222 | 'update' => 0, |
---|
| 223 | 'import_period' => 1800, // Refresh every 30 minutes by default. |
---|
| 224 | 'expire_period' => 3600, // Expire every hour by default, this is a hidden setting. |
---|
| 225 | 'import_on_create' => TRUE, // Import on create. |
---|
| 226 | ); |
---|
| 227 | } |
---|
| 228 | |
---|
| 229 | /** |
---|
| 230 | * Override parent::configForm(). |
---|
| 231 | */ |
---|
| 232 | public function configForm(&$form_state) { |
---|
| 233 | $form = array(); |
---|
| 234 | $form['name'] = array( |
---|
| 235 | '#type' => 'textfield', |
---|
| 236 | '#title' => t('Name'), |
---|
| 237 | '#description' => t('The name of this configuration.'), |
---|
| 238 | '#default_value' => $this->config['name'], |
---|
| 239 | '#required' => TRUE, |
---|
| 240 | ); |
---|
| 241 | $form['description'] = array( |
---|
| 242 | '#type' => 'textfield', |
---|
| 243 | '#title' => t('Description'), |
---|
| 244 | '#description' => t('A description of this configuration.'), |
---|
| 245 | '#default_value' => $this->config['description'], |
---|
| 246 | ); |
---|
| 247 | $form['content_type'] = array( |
---|
| 248 | '#type' => 'select', |
---|
| 249 | '#title' => t('Attach to content type'), |
---|
| 250 | '#description' => t('If an importer is attached to a content type, content is imported by creating a node. If the standalone form is selected, content is imported by using the standalone form under http://example.com/import.'), |
---|
| 251 | '#options' => array('' => t('Use standalone form')) + node_get_types('names'), |
---|
| 252 | '#default_value' => $this->config['content_type'], |
---|
| 253 | ); |
---|
| 254 | $period = drupal_map_assoc(array(0, 900, 1800, 3600, 10800, 21600, 43200, 86400, 259200, 604800, 2419200), 'format_interval'); |
---|
| 255 | $period[FEEDS_SCHEDULE_NEVER] = t('Never'); |
---|
| 256 | $period[0] = t('As often as possible'); |
---|
| 257 | $form['import_period'] = array( |
---|
| 258 | '#type' => 'select', |
---|
| 259 | '#title' => t('Minimum refresh period'), |
---|
| 260 | '#options' => $period, |
---|
| 261 | '#description' => t('This is the minimum time that must elapse before a feed may be refreshed automatically.'), |
---|
| 262 | '#default_value' => $this->config['import_period'], |
---|
| 263 | ); |
---|
| 264 | $form['import_on_create'] = array( |
---|
| 265 | '#type' => 'checkbox', |
---|
| 266 | '#title' => t('Import on submission'), |
---|
| 267 | '#description' => t('Check if content should be imported at the moment of feed submission.'), |
---|
| 268 | '#default_value' => $this->config['import_on_create'], |
---|
| 269 | ); |
---|
| 270 | return $form; |
---|
| 271 | } |
---|
| 272 | |
---|
| 273 | /** |
---|
| 274 | * Reschedule if import period changes. |
---|
| 275 | */ |
---|
| 276 | public function configFormSubmit(&$values) { |
---|
| 277 | if ($this->config['import_period'] != $values['import_period']) { |
---|
| 278 | feeds_reschedule($this->id); |
---|
| 279 | } |
---|
| 280 | parent::configFormSubmit($values); |
---|
| 281 | } |
---|
| 282 | } |
---|
| 283 | |
---|
| 284 | /** |
---|
| 285 | * Helper, see FeedsDataProcessor class. |
---|
| 286 | */ |
---|
| 287 | function feeds_format_expire($timestamp) { |
---|
| 288 | if ($timestamp == FEEDS_EXPIRE_NEVER) { |
---|
| 289 | return t('Never'); |
---|
| 290 | } |
---|
| 291 | return t('after !time', array('!time' => format_interval($timestamp))); |
---|
| 292 | } |
---|