feed_import_base.module in Feed Import 8
Same filename and directory in other branches
Basic settings for feed import base module
File
feed_import_base/feed_import_base.moduleView source
<?php
/**
* @file
* Basic settings for feed import base module
*/
use Drupal\feed_import_base\FeedImportProcessor;
use Drupal\feed_import_base\FeedImport;
/**
* Implements hook_help().
*/
function feed_import_base_help($path, $arg) {
if ($path == 'admin/help#feed_import_base') {
$vars = array(
'!project_page' => \Drupal::l('Feed Import', \Drupal\Core\Url::fromUri('http://drupal.org/project/feed_import')),
);
$help = t('The basic functionality used to import content into entities.');
$help .= '<br />';
$help .= t('For more info please read README.txt or go to !project_page.', $vars);
return $help;
}
}
/**
* Implements hook_permision().
*/
function feed_import_base_permission() {
return array(
'feed import' => array(
'title' => t('Administer feed import settings'),
'description' => t('Change settings for feed import'),
),
'feed import process' => array(
'title' => t('Run feed import'),
'description' => t('Import a feed'),
),
);
}
/**
* Implements hook_menu().
*/
function feed_import_base_menu() {
$items = array();
$items['admin/config/services/feed_import/settings'] = array(
'title' => 'Settings',
'description' => 'Manages feed import global settings',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'feed_import_base_settings_form',
),
'access callback' => 'user_access',
'access arguments' => array(
'feed import',
),
'type' => MENU_LOCAL_ACTION,
'weight' => 10,
);
return $items;
}
/**
* Implements hook_cron().
*/
function feed_import_base_cron() {
// Check if cron import is enabled.
// @FIXME
// // @FIXME
// // This looks like another module's variable. You'll need to rewrite this call
// // to ensure that it uses the correct configuration object.
// if (variable_get('feed_import_use_cron', FALSE)) {
// $overlap = variable_get('feed_import_let_overlap', array());
// $running = variable_get('feed_import_import_running', array());
// // Check if we can import.
// if (_feed_import_base_cron_in_time()) {
// $feeds = array_filter(FeedImport::loadAllFeeds(), '_feed_import_base_feed_enabled');
// uasort($feeds, '_feed_import_base_sort_feeds');
// $to_import = NULL;
// foreach ($feeds as $name => $feed) {
// if (in_array($feed->entity, $overlap) ||
// empty($running[$feed->entity]) ||
// !in_array($name, $running[$feed->entity])) {
// $to_import = $feed;
// break;
// }
// }
// unset($feeds, $feed);
// if ($to_import) {
// _feed_import_base_process_feed($to_import);
// variable_set('feed_import_last_executed_import', REQUEST_TIME);
// }
// }
// }
// Delete expired items.
// @FIXME
// // @FIXME
// // This looks like another module's variable. You'll need to rewrite this call
// // to ensure that it uses the correct configuration object.
// if (($d = variable_get('feed_import_delete_items_per_cron', 300)) > 0) {
// $d = FeedImport::deleteExpired($d);
// if ($d && variable_get('feed_import_reports', TRUE)) {
// watchdog('Feed Import', 'Deleted @count expired items', array(
// '@count' => $d,
// ), WATCHDOG_NOTICE);
// }
// }
}
/**
* Imports a feed.
*/
function _feed_import_base_process_feed($feed, $recheck = FALSE) {
$state = \Drupal::state();
$config = \Drupal::config('feed_import_base.settings');
$running = $state
->get('feed_import.running', array());
$invoke_hooks = $config
->get('invoke_hooks', FALSE);
$overlap = $config
->get('let_overlap');
if ($recheck) {
if (is_array($overlap) && !in_array($feed->entity, $overlap) && !empty($running[$feed->entity]) && in_array($feed->machine_name, $running[$feed->entity])) {
// Invoke hooks.
$invoke_hooks && \Drupal::moduleHandler()
->invokeAll('feed_import_error', [
FeedImport::FEED_OVERLAP_ERR,
$feed,
array(),
]);
return FeedImport::FEED_OVERLAP_ERR;
}
unset($overlap);
}
$running[$feed->entity][] = $feed->machine_name;
$state
->set('feed_import.running', $running);
unset($running);
register_shutdown_function('_feed_import_base_remove_running', $feed);
$report = FeedImport::import($feed, _feed_import_base_get_filters_dir());
_feed_import_base_remove_running($feed);
$feed->skip_remove_running = TRUE;
$report_errors = $config
->get('feed_import_reports');
// Check for errors.
if (empty($report['init_error'])) {
// Save last run and duration.
$feed->last_run = $report['started'];
$d = $report['finished'] - $report['started'];
$feed->last_run_duration = $d > 0 ? $d : 0;
// Check fewer items protection.
if (strlen($feed->settings['uniq_path']) && $feed->settings['feed']['protect_on_fewer_items']) {
$d = trim($feed->settings['feed']['protect_on_fewer_items']);
if (substr($d, -1) == '%') {
// Do not update last import items if percentage is used!
$d = rtrim($d, '%') * $feed->last_run_items / 100;
}
else {
$feed->last_run_items = $report['total'];
}
$d = (int) $d;
if ($report['total'] < $d) {
// Save import status.
FeedImport::saveFeedImportStatus($feed);
// Reschedul all.
$class = $feed->settings['hashes']['class'];
$class::rescheduleAll($feed->machine_name, $feed->settings['hashes']['options']['ttl']);
if ($report_errors) {
// Report base info.
_feed_import_base_save_report($report, $feed);
// Report rescheduled all.
\Drupal::logger('Feed Import')
->warning('Rescheduled all items for @name because source contained only @total items but expected @expected. !errors', array(
'@name' => $feed->name,
'@total' => $report['total'],
'@expected' => $d,
'!errors' => '<br />' . _feed_import_base_get_error_table($report['errors']),
));
}
$report['expected'] = $d;
// Invoke hooks.
$invoke_hooks && \Drupal::moduleHandler()
->invokeAll('feed_import_error', [
FeedImport::FEED_ITEMS_ERR,
$feed,
$report,
]);
return FeedImport::FEED_ITEMS_ERR;
}
}
$feed->last_run_items = $report['total'];
FeedImport::saveFeedImportStatus($feed);
// Save report if needed.
$report_errors && _feed_import_base_save_report($report, $feed);
// Invoke hooks.
$invoke_hooks && \Drupal::moduleHandler()
->invokeAll('feed_import_success', [
$feed,
$report,
]);
return FeedImport::FEED_OK;
}
else {
$ret = FALSE;
// Reader init problem.
if (strlen($feed->settings['uniq_path']) && $feed->settings['feed']['protect_on_invalid_source']) {
$ret = TRUE;
$feed->last_run = time();
$feed->last_run_duration = 0;
if (substr(trim($feed->settings['feed']['protect_on_fewer_items']), -1) != '%') {
$feed->last_run_items = 0;
}
FeedImport::saveFeedImportStatus($feed);
// Reschedule all.
$class = $feed->settings['hashes']['class'];
$class::rescheduleAll($feed->machine_name, $feed->settings['hashes']['options']['ttl']);
if ($report_errors) {
// Report reschedule all.
\Drupal::logger('Feed Import')
->warning('Rescheduled all items for @name due to a source problem. !errors', array(
'@name' => $feed->name,
':errors' => '<br />' . _feed_import_base_get_error_table($report['errors']),
));
}
}
// Invoke hooks.
$invoke_hooks && \Drupal::moduleHandler()
->invokeAll('feed_import_error', [
FeedImport::FEED_SOURCE_ERR,
$feed,
$report,
]);
if ($ret) {
return FeedImport::FEED_SOURCE_ERR;
}
else {
$invoke_hooks = FALSE;
}
}
if ($report_errors) {
dpm(_feed_import_base_get_error_table($report['errors']));
\Drupal::logger('Feed Import')
->warning('Cannot process feed @name because it is misconfigured. :errors', [
'@name' => $feed->name,
':errors' => _feed_import_base_get_error_table($report['errors']),
]);
}
// Invoke hooks.
$invoke_hooks && \Drupal::moduleHandler()
->invokeAll('feed_import_error', [
FeedImport::FEED_CONFIG_ERR,
$feed,
$report,
]);
return FeedImport::FEED_CONFIG_ERR;
}
/**
* Removes a feed from running array.
*/
function _feed_import_base_remove_running($feed) {
if (isset($feed->skip_remove_running)) {
unset($feed->skip_remove_running);
return;
}
$state = \Drupal::state();
$running = $state
->get('feed_import.running', array());
if (!empty($running[$feed->entity]) && ($pos = array_search($feed->machine_name, $running[$feed->entity])) !== FALSE) {
unset($running[$feed->entity][$pos]);
$state
->set('feed_import.running', $running);
}
}
/**
* Saves report in log.
*
* @param array $r
* Report array
* @param object $feed
* Related feed
*/
function _feed_import_base_save_report($r, $feed) {
// @FIXME
// theme() has been renamed to _theme() and should NEVER be called directly.
// Calling _theme() directly can alter the expected output and potentially
// introduce security issues (see https://www.drupal.org/node/2195739). You
// should use renderable arrays instead.
//
//
// @see https://www.drupal.org/node/2195739
// $info = theme('table', array(
// 'header' => array(
// t('Feed name'),
// t('Duration'),
// t('Found'),
// t('New'),
// t('Updated'),
// t('Rescheduled'),
// t('Skipped'),
// t('Skipped protected'),
// t('New protected'),
// t('Missing entities'),
// ),
// 'rows' => array(array(
// $feed->name,
// gmdate('H:i:s', $r['finished'] - $r['started']) . '<br>' . format_date($r['started']) . ' - ' . format_date($r['finished']),
// $r['total'],
// $r['new'],
// $r['updated'],
// $r['rescheduled'],
// $r['skipped'],
// $r['protected_skipped'],
// $r['protected'],
// $r['missing'],
// )),
// ));
$err = _feed_import_base_get_error_table($r['errors']);
\Drupal::logger('Feed Import')
->notice('Feed @name imported !status Errors !errors', array(
'@name' => $feed->name,
'!status' => '<br>' . $info,
'!errors' => '<br>' . $err,
));
}
/**
* Returns a table containig error messages.
*/
function _feed_import_base_get_error_table($errors) {
$table = [
'#type' => 'table',
'#header' => array(
t('Error'),
t('Error number'),
t('Line'),
t('File'),
),
'#rows' => $errors,
'#empty' => t('No errors reported'),
'#caption' => t('Errors'),
];
// $output = render($table)->__toString();
return $table;
}
/**
* Gets a variable directly from table.
* @see variable_get()
*/
function _feed_import_base_variable_get($name, $default = NULL) {
$name = db_query('SELECT value FROM {variable} WHERE name=:name', array(
':name' => $name,
))
->fetchColumn(0);
if (!$name) {
return $default;
}
return unserialize($name);
}
/**
* Checks if a feed is enabled.
*/
function _feed_import_base_feed_enabled($feed) {
return $feed->cron_import > 0;
}
/**
* Sort callback for feeds.
*/
function _feed_import_base_sort_feeds($a, $b) {
return $a->last_run - $b->last_run;
}
/**
* Returns curent filters dir.
*/
function _feed_import_base_get_filters_dir() {
$config = \Drupal::config('feed_import_base.settings');
$dir = $config
->get('filters_dir');
if (empty($dir)) {
$dir = drupal_get_path('module', 'feed_import_base') . '/filters';
}
return $dir;
}
/**
* Helper for cron.
*
* @return bool
* Cron can run
*/
function _feed_import_base_cron_in_time() {
// @FIXME
// // @FIXME
// // This looks like another module's variable. You'll need to rewrite this call
// // to ensure that it uses the correct configuration object.
// if (variable_get('feed_import_time_settings', 0)) {
// // Specified interval.
// $time1 = $time2 = 0;
// list($h, $m) = explode(':', variable_get('feed_import_interval_start', '00:00'));
// $time1 = mktime($h, $m, 0);
// list($h, $m) = explode(':', variable_get('feed_import_interval_stop', '00:00'));
// $time2 = mktime($h, $m, 0);
// return $time1 < $time2 && $time1 <= REQUEST_TIME && REQUEST_TIME <= $time2;
// }
// else {
// $last_executed = variable_get('feed_import_last_executed_import', 0);
// $time_between = variable_get('feed_import_time_between_imports', 3600);
// return ($last_executed + $time_between) < REQUEST_TIME;
// }
}
/**
* Settings form
*/
function feed_import_base_settings_form($form, &$form_state) {
// @FIXME
// // @FIXME
// // This looks like another module's variable. You'll need to rewrite this call
// // to ensure that it uses the correct configuration object.
// $form['feed_import_reports'] = array(
// '#type' => 'checkbox',
// '#default_value' => variable_get('feed_import_reports', TRUE),
// '#title' => t('Provide import reports'),
// '#description' => t('These are log reports.'),
// );
// @FIXME
// // @FIXME
// // This looks like another module's variable. You'll need to rewrite this call
// // to ensure that it uses the correct configuration object.
// $form['feed_import_use_cron'] = array(
// '#type' => 'checkbox',
// '#default_value' => variable_get('feed_import_use_cron', 0),
// '#title' => t('Cron import'),
// '#description' => t('Run import for enabled feeds at cron'),
// );
$form['container'] = array(
'#type' => 'fieldset',
'#states' => array(
'invisible' => array(
'input[name="feed_import_use_cron"]' => array(
'checked' => FALSE,
),
),
),
);
// @FIXME
// // @FIXME
// // This looks like another module's variable. You'll need to rewrite this call
// // to ensure that it uses the correct configuration object.
// $form['container']['feed_import_time_settings'] = array(
// '#type' => 'radios',
// '#options' => array(t('From time to time'), t('Specified time interval')),
// '#default_value' => variable_get('feed_import_time_settings', 0),
// '#title' => t('When feeds can be imported'),
// );
// @FIXME
// // @FIXME
// // This looks like another module's variable. You'll need to rewrite this call
// // to ensure that it uses the correct configuration object.
// $form['container']['feed_import_time_between_imports'] = array(
// '#type' => 'textfield',
// '#default_value' => variable_get('feed_import_time_between_imports', 3600),
// '#title' => t('Time between two imports at cron (seconds)'),
// '#description' => t('Time betwen two cron imports.'),
// '#states' => array(
// 'visible' => array(
// 'input[name="feed_import_time_settings"]' => array('value' => 0),
// ),
// ),
// );
// @FIXME
// // @FIXME
// // This looks like another module's variable. You'll need to rewrite this call
// // to ensure that it uses the correct configuration object.
// $form['container']['feed_import_interval_start'] = array(
// '#type' => 'textfield',
// '#default_value' => variable_get('feed_import_interval_start', '00:00'),
// '#title' => t('Start time'),
// '#description' => t('Format is hh:mm.'),
// '#states' => array(
// 'visible' => array(
// 'input[name="feed_import_time_settings"]' => array('value' => 1),
// ),
// ),
// );
// @FIXME
// // @FIXME
// // This looks like another module's variable. You'll need to rewrite this call
// // to ensure that it uses the correct configuration object.
// $form['container']['feed_import_interval_stop'] = array(
// '#type' => 'textfield',
// '#default_value' => variable_get('feed_import_interval_stop', '24:00'),
// '#title' => t('End time'),
// '#description' => t('Format is hh:mm. This must be greater than start time.'),
// '#states' => array(
// 'visible' => array(
// 'input[name="feed_import_time_settings"]' => array('value' => 1),
// ),
// ),
// );
// @FIXME
// // @FIXME
// // This looks like another module's variable. You'll need to rewrite this call
// // to ensure that it uses the correct configuration object.
// $form['feed_import_delete_items_per_cron'] = array(
// '#type' => 'textfield',
// '#default_value' => variable_get('feed_import_delete_items_per_cron', 300),
// '#title' => t('Expired items delete per cron'),
// '#description' => t('How many expired items to delete when cron runs.'),
// '#required' => TRUE,
// );
$form['feed_import_filters_dir'] = array(
'#type' => 'textfield',
'#default_value' => _feed_import_base_get_filters_dir(),
'#title' => t('Extra filters base path'),
'#description' => t('Where to look for filter files.'),
);
// @FIXME
// // @FIXME
// // This looks like another module's variable. You'll need to rewrite this call
// // to ensure that it uses the correct configuration object.
// $form['feed_import_let_overlap'] = array(
// '#type' => 'select',
// '#multiple' => TRUE,
// '#options' => FeedImport::getAllEntities(),
// '#default_value' => variable_get('feed_import_let_overlap', array()),
// '#title' => t('Allow import overlap for specified entities'),
// '#description' => t('This is not indicated for nodes.'),
// );
// @FIXME
// // @FIXME
// // This looks like another module's variable. You'll need to rewrite this call
// // to ensure that it uses the correct configuration object.
// $form['feed_import_invoke_hooks'] = array(
// '#type' => 'checkbox',
// '#default_value' => variable_get('feed_import_invoke_hooks', FALSE),
// '#title' => t('Invoke hooks on import sucess or error'),
// '#description' => t('This can be useful for sending alerts.'),
// );
return system_settings_form($form);
}
/**
* Settings form validate
*/
function feed_import_base_settings_form_validate($form, &$form_state) {
$numeric_fields = array(
'feed_import_time_between_imports',
'feed_import_delete_items_per_cron',
);
// Checking numeric fields.
foreach ($numeric_fields as &$field) {
if (!is_numeric($form_state['values'][$field])) {
form_error($form[$field], t('Field value must be numeric.'));
}
}
// Check interval.
$ok = TRUE;
if (!preg_match("/^[0-2][0-9]:[0-5][0-9]\$/", $form_state['values']['feed_import_interval_start'])) {
form_error($form['container']['feed_import_interval_start'], t('Invalid start time.'));
$ok = FALSE;
}
if (!preg_match("/^[0-2][0-9]:[0-5][0-9]\$/", $form_state['values']['feed_import_interval_stop'])) {
form_error($form['container']['feed_import_interval_stop'], t('Invalid end time.'));
$ok = FALSE;
}
if ($ok) {
if ($form_state['values']['feed_import_interval_stop'] < $form_state['values']['feed_import_interval_start']) {
form_error($form['container']['feed_import_interval_stop'], t('End time must be greater than start time.'));
}
}
}
/**
* Implements hook_entity_delete().
*/
function feed_import_base_entity_delete($entity, $type) {
return;
$e = FeedImport::getEntityInfo($type);
if (isset($entity->{$e->idKey})) {
$id = $entity->{$e->idKey};
$id && is_numeric($id) && FeedImport::addDeletedEntity($type, $id);
}
}
/**
* Implements hook_feed_import_field_merge_classes().
*/
function feed_import_base_feed_import_field_merge_classes() {
return array(
FeedImportProcessor::UPDATE_COMBINE => array(
'title' => t('Merge field - no duplicates'),
'description' => t('Tries to avoid updates by checking if field values already exists.'),
'class' => 'Drupal\\feed_import_base\\FeedImportMergeNoDuplicates',
),
FeedImportProcessor::UPDATE_MERGE => array(
'title' => t('Merge field - allow duplicates'),
'description' => t('Appends new values to field.'),
'class' => 'Drupal\\feed_import_base\\FeedImportMergeDuplicates',
),
FeedImportProcessor::UPDATE_OVERWRITE => array(
'title' => t('Overwrite field'),
'description' => t('Overwrites field values if are distinct. Field will be removed if there are no values.'),
'class' => 'Drupal\\feed_import_base\\FeedImportMergeOverwrite',
),
FeedImportProcessor::UPDATE_OVERWRITE_FAST => array(
'title' => t('Fast overwrite field'),
'description' => t('Always overwrites field values. Field will NOT be removed if there are no values.'),
'class' => 'Drupal\\feed_import_base\\FeedImportMergeOverwriteFast',
),
);
}
/**
* Implements hook_feed_import_field_compare_functions().
*/
function feed_import_base_feed_import_field_compare_functions() {
return array(
'image:image' => '_feed_import_base_compare_image_field',
'taxonomy:taxonomy_term_reference' => '_feed_import_base_compare_taxonomy_term_reference_field',
);
}
/**
* Field compare any.
*
* @param array $new New field data
* @param array $current Current field data
*
* @return bool True if they are the same
*/
function _feed_import_base_compare_other_fields(&$new, &$current) {
return !array_diff_assoc($new, $current);
}
/**
* Field compare callback for image:image.
*/
function _feed_import_base_compare_image_field(&$new, &$current) {
return $new['fid'] == $current['fid'];
}
/**
* Field compare callback for taxonomy:taxonomy_term_reference.
*/
function _feed_import_base_compare_taxonomy_term_reference_field(&$new, &$current) {
return $new['tid'] == $current['tid'];
}