You are here

feed_import_base.module in Feed Import 7.3

Same filename and directory in other branches
  1. 8 feed_import_base/feed_import_base.module

Basic settings for feed import base module

File

feed_import_base/feed_import_base.module
View source
<?php

/**
 * @file
 * Basic settings for feed import base module
 */

/**
 * Implements hook_help().
 */
function feed_import_base_help($path, $arg) {
  if ($path == 'admin/help#feed_import_base') {
    $vars = array(
      '!project_page' => l('Feed Import', '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.
  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.
  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) {
  $running = _feed_import_base_variable_get('feed_import_import_running', array());
  $invoke_hooks = variable_get('feed_import_invoke_hooks', FALSE);
  if ($recheck) {
    $overlap = variable_get('feed_import_let_overlap', array());
    if (!in_array($feed->entity, $overlap) && !empty($running[$feed->entity]) && in_array($feed->machine_name, $running[$feed->entity])) {

      // Invoke hooks.
      $invoke_hooks && module_invoke_all('feed_import_error', FeedImport::FEED_OVERLAP_ERR, $feed, array());
      return FeedImport::FEED_OVERLAP_ERR;
    }
    unset($overlap);
  }
  $running[$feed->entity][] = $feed->machine_name;
  variable_set('feed_import_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 = variable_get('feed_import_reports', TRUE);

  // 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.
          watchdog('Feed Import', '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']),
          ), WATCHDOG_WARNING);
        }
        $report['expected'] = $d;

        // Invoke hooks.
        $invoke_hooks && module_invoke_all('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 && module_invoke_all('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.
        watchdog('Feed Import', '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']),
        ), WATCHDOG_WARNING);
      }
    }

    // Invoke hooks.
    $invoke_hooks && module_invoke_all('feed_import_error', FeedImport::FEED_SOURCE_ERR, $feed, $report);
    if ($ret) {
      return FeedImport::FEED_SOURCE_ERR;
    }
    else {
      $invoke_hooks = FALSE;
    }
  }
  if ($report_errors) {
    watchdog('Feed Import', 'Cannot process feed @name because is misconfigured. !errors', array(
      '@name' => $feed->name,
      '!errors' => '<br />' . _feed_import_base_get_error_table($report['errors']),
    ), WATCHDOG_WARNING);
  }

  // Invoke hooks.
  $invoke_hooks && module_invoke_all('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;
  }
  $running = _feed_import_base_variable_get('feed_import_import_running', array());
  if (!empty($running[$feed->entity]) && ($pos = array_search($feed->machine_name, $running[$feed->entity])) !== FALSE) {
    unset($running[$feed->entity][$pos]);
    variable_set('feed_import_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) {
  $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']);
  watchdog('Feed Import', 'Feed @name imported !status Errors !errors', array(
    '@name' => $feed->name,
    '!status' => '<br>' . $info,
    '!errors' => '<br>' . $err,
  ), $r['errors'] ? WATCHDOG_WARNING : WATCHDOG_NOTICE);
}

/**
 * Returns a table containig error messages.
 */
function _feed_import_base_get_error_table($errors) {
  return theme('table', array(
    'header' => array(
      t('Error'),
      t('Error number'),
      t('Line'),
      t('File'),
    ),
    'rows' => $errors,
    'empty' => t('No errors reported'),
    'caption' => t('Errors'),
  ));
}

/**
 * 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() {
  if (($dir = variable_get('feed_import_filters_dir', FALSE)) === FALSE) {
    $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() {
  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) {
  $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.'),
  );
  $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,
        ),
      ),
    ),
  );
  $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'),
  );
  $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,
        ),
      ),
    ),
  );
  $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,
        ),
      ),
    ),
  );
  $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,
        ),
      ),
    ),
  );
  $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.'),
  );
  $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.'),
  );
  $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) {
  $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' => 'FeedImportMergeNoDuplicates',
    ),
    FeedImportProcessor::UPDATE_MERGE => array(
      'title' => t('Merge field - allow duplicates'),
      'description' => t('Appends new values to field.'),
      'class' => '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' => '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' => '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'];
}

Functions

Namesort descending Description
feed_import_base_cron Implements hook_cron().
feed_import_base_entity_delete Implements hook_entity_delete().
feed_import_base_feed_import_field_compare_functions Implements hook_feed_import_field_compare_functions().
feed_import_base_feed_import_field_merge_classes Implements hook_feed_import_field_merge_classes().
feed_import_base_help Implements hook_help().
feed_import_base_menu Implements hook_menu().
feed_import_base_permission Implements hook_permision().
feed_import_base_settings_form Settings form
feed_import_base_settings_form_validate Settings form validate
_feed_import_base_compare_image_field Field compare callback for image:image.
_feed_import_base_compare_other_fields Field compare any.
_feed_import_base_compare_taxonomy_term_reference_field Field compare callback for taxonomy:taxonomy_term_reference.
_feed_import_base_cron_in_time Helper for cron.
_feed_import_base_feed_enabled Checks if a feed is enabled.
_feed_import_base_get_error_table Returns a table containig error messages.
_feed_import_base_get_filters_dir Returns curent filters dir.
_feed_import_base_process_feed Imports a feed.
_feed_import_base_remove_running Removes a feed from running array.
_feed_import_base_save_report Saves report in log.
_feed_import_base_sort_feeds Sort callback for feeds.
_feed_import_base_variable_get Gets a variable directly from table.