Newer
Older
Alex Barth
committed
Alex Barth
committed
* Feeds - basic API functions and hook implementations.
*/
// Common request time, use as point of reference and to avoid calls to time().
Alex Barth
committed
define('FEEDS_REQUEST_TIME', time());
// Do not schedule a feed for refresh.
define('FEEDS_SCHEDULE_NEVER', -1);
// Never expire feed items.
define('FEEDS_EXPIRE_NEVER', -1);
// An object that is not persistent. Compare EXPORT_IN_DATABASE, EXPORT_IN_CODE.
Alex Barth
committed
define('FEEDS_EXPORT_NONE', 0x0);
define('FEEDS_BATCH_COMPLETE', 1.0);
define('FEEDS_BATCH_ACTIVE', 0.0);
Alex Barth
committed
/**
Alex Barth
committed
* @{
/**
* Implements hook_hook_info().
*/
function feeds_hook_info() {
$hooks = array(
'feeds_plugins',
'feeds_after_parse',
'feeds_before_import',
'feeds_before_update',
megachriz
committed
'feeds_prevalidate',
'feeds_presave',
'feeds_after_import',
'feeds_after_clear',
'feeds_processor_targets',
'feeds_processor_targets_alter',
'feeds_parser_sources_alter',
'feeds_config_defaults',
'feeds_fetcher_config_defaults',
'feeds_parser_config_defaults',
'feeds_processor_config_defaults',
);
return array_fill_keys($hooks, array('group' => 'feeds'));
}
*/
function feeds_cron() {
// Expire old log entries.
db_delete('feeds_log')
->condition('request_time', REQUEST_TIME - 604800, '<')
->execute();
// Find importers that need to be rescheduled.
$importers = feeds_reschedule();
if ($importers) {
// @todo Maybe we should queue this somehow as well. This could be potentially
// very long.
$sources = db_query("SELECT feed_nid, id FROM {feeds_source} WHERE id IN (:ids)", array(':ids' => $importers));
foreach ($sources as $source) {
feeds_source($source->id, $source->feed_nid)->schedule();
}
feeds_reschedule(FALSE);
}
// Sync the files in the cache directory with entries in the cache every now
// and then. By default: every six hours.
$last_check = variable_get('feeds_sync_cache_feeds_http_last_check');
$interval = variable_get('feeds_sync_cache_feeds_http_interval', 21600);
if ($last_check < (REQUEST_TIME - $interval)) {
// Check first if the task isn't already queued.
$queue = DrupalQueue::get('feeds_sync_cache_feeds_http');
if ($queue->numberOfItems() < 1) {
// Queue sync task.
FeedsHTTPCache::getInstance('cache_feeds_http')->startSync();
}
variable_set('feeds_sync_cache_feeds_http_last_check', REQUEST_TIME);
}
Alex Barth
committed
}
/**
* Implements hook_cron_job_scheduler_info().
* Compare queue names with key names in feeds_cron_queue_info().
*/
function feeds_cron_job_scheduler_info() {
$info = array();
$info['feeds_source_import'] = array(
'queue name' => 'feeds_source_import',
);
// feeds_source_clear never gets called, since we now use the queue directly.
// This is left in case any background jobs are still running after an
// upgrade.
$info['feeds_source_clear'] = array(
'queue name' => 'feeds_source_clear',
);
$info['feeds_source_expire'] = array(
'queue name' => 'feeds_source_expire',
$info['feeds_push_unsubscribe'] = array(
'queue name' => 'feeds_push_unsubscribe',
);
return $info;
}
/**
* Implements hook_cron_queue_info().
Alex Barth
committed
*/
function feeds_cron_queue_info() {
$queues = array();
Alex Barth
committed
$queues['feeds_source_import'] = array(
'worker callback' => 'feeds_source_import',
klausi
committed
'time' => 60,
Alex Barth
committed
);
$queues['feeds_source_clear'] = array(
'worker callback' => 'feeds_source_clear',
);
$queues['feeds_source_expire'] = array(
'worker callback' => 'feeds_source_expire',
Alex Barth
committed
);
$queues['feeds_push_unsubscribe'] = array(
'worker callback' => 'feeds_push_unsubscribe',
);
$queues['feeds_sync_cache_feeds_http'] = array(
'worker callback' => 'feeds_sync_cache_feeds_http',
);
Alex Barth
committed
return $queues;
Alex Barth
committed
/**
* Scheduler callback for importing from a source.
*
* @param array $job
* A job definition, which consists of at least the following elements:
* - type (string)
* The importer ID.
* - id (int)
* The Feed node ID if the importer is attached to a content type. Otherwise
* 0.
Alex Barth
committed
*/
function feeds_source_import(array $job) {
$source = _feeds_queue_worker_helper($job, 'import');
megachriz
committed
if ($source->doesExist()) {
$source->scheduleImport();
}
}
/**
* Scheduler callback for deleting all items from a source.
*
* @param array $job
* A job definition, which consists of at least the following elements:
* - type (string)
* The importer ID.
* - id (int)
* The Feed node ID if the importer is attached to a content type. Otherwise
* 0.
function feeds_source_clear(array $job) {
$source = _feeds_queue_worker_helper($job, 'clear');
megachriz
committed
if ($source->doesExist()) {
$source->scheduleClear();
}
Alex Barth
committed
}
/**
* Scheduler callback for expiring content.
*
* @param array $job
* A job definition, which consists of at least the following elements:
* - type (string)
* The importer ID.
* - id (int)
* The Feed node ID if the importer is attached to a content type. Otherwise
* 0.
Alex Barth
committed
*/
function feeds_source_expire(array $job) {
$source = _feeds_queue_worker_helper($job, 'expire');
megachriz
committed
if ($source->doesExist()) {
$source->scheduleExpire();
}
}
/**
* Executes a method on a feed source.
*
* @param array $job
* A job definition, which consists of at least the following elements:
* - type (string)
* The importer ID.
* - id (int)
* The Feed node ID if the importer is attached to a content type. Otherwise
* 0.
* @param string $method
* The method to execute.
*/
function _feeds_queue_worker_helper(array $job, $method) {
$source = feeds_source($job['type'], $job['id']);
Alex Barth
committed
try {
$source->existing()->$method();
Alex Barth
committed
}
catch (FeedsNotExistingException $e) {
megachriz
committed
// Eventually remove the job from job scheduler.
JobScheduler::get('feeds_source_import')->remove($job);
Alex Barth
committed
catch (Exception $e) {
$source->log($method, $e->getMessage(), array(), WATCHDOG_ERROR);
Alex Barth
committed
}
return $source;
/**
* Scheduler callback for keeping the cache dir and cache entries in sync.
*
* This makes sure that files that appear in the Feeds cache directory that are
* no longer referenced in the cache_feeds_http bin, are cleaned up.
* The entries saved in the cache_feeds_http bin and the actual cached files
* saved on the file system can get out of sync when:
* - Truncating the cache_feeds_http table manually.
* - When using an alternative cache class for the cache_feeds_http bin
* (other than 'FeedsHTTPCache').
*/
function feeds_sync_cache_feeds_http(array $job) {
FeedsHTTPCache::getInstance('cache_feeds_http')->sync($job['files']);
}
/**
* Scheduler callback for unsubscribing from PuSH hubs.
*
* @param array $job
* A job definition, which consists of at least the following elements:
* - type (string)
* The importer ID.
* - id (int)
* The Feed node ID if the importer is attached to a content type. Otherwise
* 0.
*/
function feeds_push_unsubscribe($job) {
$source = feeds_source($job['type'], $job['id']);
/** @var FeedsFetcher $fetcher */
$fetcher = feeds_plugin('FeedsHTTPFetcher', $source->importer->id);
$fetcher->unsubscribe($source);
}
/**
* Batch API worker callback. Used by FeedsSource::startBatchAPIJob().
*
* @todo Harmonize Job Scheduler API callbacks with Batch API callbacks?
*
* @param string $method
* Method to execute on importer; one of 'import' or 'clear'.
* @param string $importer_id
* Identifier of a FeedsImporter object.
* @param int $feed_nid
* If importer is attached to content type, feed node id identifying the
* source to be imported.
* @param array $context
*
* @see FeedsSource::startBatchAPIJob()
*/
function feeds_batch($method, $importer_id, $feed_nid = 0, &$context) {
$context['finished'] = FEEDS_BATCH_COMPLETE;
try {
$context['finished'] = feeds_source($importer_id, $feed_nid)->$method();
}
catch (Exception $e) {
drupal_set_message($e->getMessage(), 'error');
}
Alex Barth
committed
}
/**
* Reschedule one or all importers.
*
* @param string|bool|null $importer_id
* If TRUE, all importers will be rescheduled, if FALSE, no importers will
* be rescheduled, if an importer id, only importer of that id will be
* rescheduled.
*
* @return string[]
* A list of importer ids that need rescheduling.
*/
function feeds_reschedule($importer_id = NULL) {
$reschedule = variable_get('feeds_reschedule', FALSE);
if ($importer_id === TRUE || $importer_id === FALSE) {
$reschedule = $importer_id;
Alex Barth
committed
}
elseif (is_string($importer_id) && $reschedule !== TRUE) {
$reschedule = array_filter((array) $reschedule);
$reschedule[$importer_id] = $importer_id;
}
if (isset($importer_id)) {
variable_set('feeds_reschedule', $reschedule);
}
if ($reschedule === TRUE) {
return feeds_enabled_importers();
Alex Barth
committed
}
elseif ($reschedule === FALSE) {
return array();
}
Alex Barth
committed
return $reschedule;
}
* Implements feeds_permission().
function feeds_permission() {
$perms = array(
'administer feeds' => array(
'title' => t('Administer Feeds'),
'description' => t('Create, update, delete importers, execute import and delete tasks on any importer.'),
Alex Barth
committed
foreach (feeds_importer_load_all() as $importer) {
$perms["import $importer->id feeds"] = array(
'title' => t('Import @name feeds', array('@name' => $importer->config['name'])),
$perms["clear $importer->id feeds"] = array(
'title' => t('Delete items from @name feeds', array('@name' => $importer->config['name'])),
$perms["unlock $importer->id feeds"] = array(
'title' => t('Unlock imports from @name feeds', array('@name' => $importer->config['name'])),
'description' => t('If a feed importation breaks for some reason, users with this permission can unlock them.'),
Alex Barth
committed
}
return $perms;
}
/**
*
* Declare form callbacks for all known classes derived from FeedsConfigurable.
Alex Barth
committed
*/
function feeds_forms($form_id, $args) {
// Check if the requested form is a Feeds form.
if (!stripos($form_id, '_feeds_form')) {
return;
}
Alex Barth
committed
$forms = array();
$forms['FeedsImporter_feeds_form']['callback'] = 'feeds_form';
Alex Barth
committed
$plugins = FeedsPlugin::all();
Alex Barth
committed
foreach ($plugins as $plugin) {
$forms[$plugin['handler']['class'] . '_feeds_form']['callback'] = 'feeds_form';
Alex Barth
committed
}
return $forms;
}
/**
Alex Barth
committed
*/
function feeds_menu() {
$items = array();
$items['import'] = array(
'title' => 'Import',
'page callback' => 'feeds_page',
'access callback' => 'feeds_page_access',
'file' => 'feeds.pages.inc',
);
Chris Leppanen
committed
$items['import/%feeds_importer'] = array(
'title callback' => 'feeds_importer_title',
'title arguments' => array(1),
'page callback' => 'drupal_get_form',
'page arguments' => array('feeds_import_form', 1),
'access callback' => 'feeds_access',
'access arguments' => array('import', 1),
'file' => 'feeds.pages.inc',
);
Chris Leppanen
committed
$items['import/%feeds_importer/import'] = array(
'title' => 'Import',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
Chris Leppanen
committed
$items['import/%feeds_importer/delete-items'] = array(
'title' => 'Delete items',
'page callback' => 'drupal_get_form',
'page arguments' => array('feeds_delete_tab_form', 1),
'access callback' => 'feeds_access',
'access arguments' => array('clear', 1),
'file' => 'feeds.pages.inc',
'type' => MENU_LOCAL_TASK,
);
Chris Leppanen
committed
$items['import/%feeds_importer/unlock'] = array(
'title' => 'Unlock',
'page callback' => 'drupal_get_form',
'page arguments' => array('feeds_unlock_tab_form', 1),
'access callback' => 'feeds_access',
'access arguments' => array('unlock', 1),
'file' => 'feeds.pages.inc',
'type' => MENU_LOCAL_TASK,
);
Chris Leppanen
committed
$items['import/%feeds_importer/template'] = array(
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
'page callback' => 'feeds_importer_template',
'page arguments' => array(1),
'access callback' => 'feeds_access',
'access arguments' => array('import', 1),
'file' => 'feeds.pages.inc',
'type' => MENU_CALLBACK,
);
$items['node/%node/import'] = array(
'title' => 'Import',
'page callback' => 'drupal_get_form',
'page arguments' => array('feeds_import_tab_form', 1),
'access callback' => 'feeds_access',
'access arguments' => array('import', 1),
'file' => 'feeds.pages.inc',
'type' => MENU_LOCAL_TASK,
'weight' => 10,
);
$items['node/%node/delete-items'] = array(
'title' => 'Delete items',
'page callback' => 'drupal_get_form',
'page arguments' => array('feeds_delete_tab_form', NULL, 1),
'access callback' => 'feeds_access',
'access arguments' => array('clear', 1),
'file' => 'feeds.pages.inc',
'type' => MENU_LOCAL_TASK,
'weight' => 11,
);
$items['node/%node/unlock'] = array(
'title' => 'Unlock',
'page callback' => 'drupal_get_form',
'page arguments' => array('feeds_unlock_tab_form', NULL, 1),
'access callback' => 'feeds_access',
'access arguments' => array('unlock', 1),
'file' => 'feeds.pages.inc',
'type' => MENU_LOCAL_TASK,
'weight' => 11,
);
// @todo Eliminate this step and thus eliminate clearing menu cache when
// manipulating importers.
Alex Barth
committed
foreach (feeds_importer_load_all() as $importer) {
Alex Barth
committed
}
return $items;
}
/**
* Implements hook_admin_paths().
*/
function feeds_admin_paths() {
$paths = array(
'import' => TRUE,
'import/*' => TRUE,
'node/*/import' => TRUE,
'node/*/delete-items' => TRUE,
'node/*/log' => TRUE,
);
return $paths;
}
Alex Barth
committed
/**
* Menu loader callback.
*
* @param string $id
* The ID of the importer to load.
*
* @return FeedsImporter|false
* A FeedsImporter instance if found. False otherwise.
Alex Barth
committed
*/
function feeds_importer_load($id) {
Chris Leppanen
committed
try {
wmostrey
committed
$importer = feeds_importer($id);
if ($importer->doesExist()) {
return $importer;
}
Chris Leppanen
committed
}
catch (InvalidArgumentException $e) {
}
return FALSE;
Alex Barth
committed
}
*
* @param FeedsImporter $importer
* The importer to return the page title for.
*
* @return string
* A page title.
Chris Leppanen
committed
function feeds_importer_title(FeedsImporter $importer) {
Alex Barth
committed
/**
Alex Barth
committed
*/
function feeds_theme() {
return array(
'feeds_upload' => array(
'file' => 'feeds.pages.inc',
Alex Barth
committed
),
Alex Barth
committed
'feeds_source_status' => array(
'file' => 'feeds.pages.inc',
'variables' => array(
'progress_importing' => NULL,
'progress_clearing' => NULL,
'imported' => NULL,
'count' => NULL,
),
),
Alex Barth
committed
);
}
/**
* Menu access callback.
*
* @param string $action
* The action to be performed. Possible values are:
* - import;
* - clear;
* - unlock.
* @param object|string $param
Alex Barth
committed
* Node object or FeedsImporter id.
*
* @return bool
* True if access is granted. False otherwise.
Alex Barth
committed
*/
function feeds_access($action, $param) {
if (!in_array($action, array('import', 'clear', 'unlock'))) {
// If $action is not one of the supported actions, we return access denied.
return FALSE;
}
Chris Leppanen
committed
$importer_id = FALSE;
Alex Barth
committed
if (is_string($param)) {
$importer_id = $param;
}
Chris Leppanen
committed
elseif ($param instanceof FeedsImporter) {
$importer_id = $param->id;
}
Alex Barth
committed
elseif ($param->type) {
$importer_id = feeds_get_importer_id($param->type);
Alex Barth
committed
}
// Check for permissions if feed id is present, otherwise return FALSE.
if ($importer_id) {
if (user_access('administer feeds') || user_access("{$action} {$importer_id} feeds")) {
Alex Barth
committed
return TRUE;
}
}
return FALSE;
}
worldfallz
committed
/**
* Access callback to determine if the user can import Feeds importers.
*
* Feeds imports require an additional access check because they are PHP
* code and PHP is more locked down than administer feeds.
*
* @return bool
* True if access is granted. False otherwise.
worldfallz
committed
*/
function feeds_importer_import_access() {
return user_access('administer feeds') && user_access('use PHP for settings');
}
Alex Barth
committed
/**
* Menu access callback.
*
* @return bool
* True if access is granted. False otherwise.
Alex Barth
committed
*/
function feeds_page_access() {
if (user_access('administer feeds')) {
return TRUE;
}
foreach (feeds_enabled_importers() as $id) {
Alex Barth
committed
return TRUE;
}
}
return FALSE;
}
/**
* Implements hook_exit().
*/
function feeds_exit() {
elliotttf
committed
// Process any pending PuSH subscriptions.
$jobs = feeds_get_subscription_jobs();
foreach ($jobs as $job) {
if (!isset($job['fetcher']) || !isset($job['source'])) {
continue;
}
elliotttf
committed
$job['fetcher']->subscribe($job['source']);
}
stevekessler
committed
watchdog('feeds', 'Feeds reported errors, visit the Feeds log for details.', array(), WATCHDOG_ERROR, l(t('view'), 'admin/reports/feeds'));
Alex Barth
committed
/**
* Implements hook_views_api().
Alex Barth
committed
*/
function feeds_views_api() {
return array(
'api' => 3,
'path' => drupal_get_path('module', 'feeds') . '/views',
Alex Barth
committed
);
}
/**
* Implements hook_ctools_plugin_api().
Alex Barth
committed
*/
function feeds_ctools_plugin_api($owner, $api) {
if ($owner == 'feeds' && $api == 'plugins') {
return array('version' => 1);
}
}
Alex Barth
committed
/**
* Implements hook_ctools_plugin_type().
Alex Barth
committed
*/
function feeds_ctools_plugin_type() {
Alex Barth
committed
return array(
'plugins' => array(
'cache' => TRUE,
'use hooks' => TRUE,
'classes' => array('handler'),
),
Alex Barth
committed
);
}
Alex Barth
committed
/**
* Implements hook_feeds_plugins().
Alex Barth
committed
*/
function feeds_feeds_plugins() {
module_load_include('inc', 'feeds', 'feeds.plugins');
return _feeds_feeds_plugins();
}
/**
Chris Leppanen
committed
* Gets the feed_nid for a single entity.
*
* @param int $entity_id
* The entity id.
* @param string $entity_type
* The type of entity.
*
* @return int|bool
* The feed_nid of the entity, or FALSE if the entity doesn't belong to a
* feed.
*/
Chris Leppanen
committed
function feeds_get_feed_nid($entity_id, $entity_type) {
return db_query("SELECT feed_nid FROM {feeds_item} WHERE entity_type = :type AND entity_id = :id", array(':type' => $entity_type, ':id' => $entity_id))->fetchField();
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
}
/**
* Implements hook_entity_insert().
*/
function feeds_entity_insert($entity, $type) {
list($id) = entity_extract_ids($type, $entity);
feeds_item_info_insert($entity, $id);
}
/**
* Implements hook_entity_update().
*/
function feeds_entity_update($entity, $type) {
list($id) = entity_extract_ids($type, $entity);
feeds_item_info_save($entity, $id);
}
/**
* Implements hook_entity_delete().
*/
function feeds_entity_delete($entity, $type) {
list($id) = entity_extract_ids($type, $entity);
// Delete any imported items produced by the source.
db_delete('feeds_item')
->condition('entity_type', $type)
->condition('entity_id', $id)
->execute();
}
/**
* Implements hook_node_validate().
*/
function feeds_node_validate($node, $form, &$form_state) {
if (!$importer_id = feeds_get_importer_id($node->type)) {
return;
}
// Keep a copy of the title for subsequent node creation stages.
// @todo: revisit whether $node still looses all of its properties
// between validate and insert stage.
$last_title = &drupal_static('feeds_node_last_title');
$last_feeds = &drupal_static('feeds_node_last_feeds');
Alex Barth
committed
// On validation stage we are working with a FeedsSource object that is
// not tied to a nid - when creating a new node there is no
// $node->nid at this stage.
$source = feeds_source($importer_id);
Alex Barth
committed
Alex Barth
committed
// Node module magically moved $form['feeds'] to $node->feeds :P.
// configFormValidate may modify $last_feed, smuggle it to update/insert stage
// through a static variable.
$last_feeds = $node->feeds;
$source->configFormValidate($last_feeds);
megachriz
committed
// Check if title form element is hidden.
$title_hidden = (isset($form['title']['#access']) && !$form['title']['#access']);
// If the node title is empty and the title form element wasn't hidden, try to
// retrieve the title from the feed.
megachriz
committed
if (isset($node->title) && trim($node->title) == '' && !$title_hidden) {
try {
$source->addConfig($last_feeds);
Alex Barth
committed
if (!$last_title = $source->preview()->title) {
throw new Exception();
}
}
catch (Exception $e) {
drupal_set_message($e->getMessage(), 'error');
Dave Reid
committed
form_set_error('title', t('Could not retrieve title from feed.'));
Alex Barth
committed
}
}
}
Alex Barth
committed
/**
* Implements hook_node_presave().
*/
function feeds_node_presave($node) {
// Populate $node->title and $node->feed from result of validation phase.
$last_title = &drupal_static('feeds_node_last_title');
$last_feeds = &drupal_static('feeds_node_last_feeds');
if (empty($node->title) && !empty($last_title)) {
$node->title = $last_title;
}
if (!empty($last_feeds)) {
$node->feeds = $last_feeds;
}
$last_title = NULL;
$last_feeds = NULL;
megachriz
committed
// Update "changed" value if there was mapped to that.
if (isset($node->feeds_item->node_changed)) {
$node->changed = $node->feeds_item->node_changed;
}
Alex Barth
committed
}
Alex Barth
committed
/**
Alex Barth
committed
*/
function feeds_node_insert($node) {
if (isset($node->feeds) && $importer_id = feeds_get_importer_id($node->type)) {
$source = feeds_source($importer_id, $node->nid);
$source->addConfig($node->feeds);
$source->save();
Alex Barth
committed
// Start import if requested.
if (feeds_importer($importer_id)->config['import_on_create'] && !isset($node->feeds['suppress_import'])) {
Alex Barth
committed
}
// Schedule the source.
Alex Barth
committed
}
}
/**
* Implements hook_node_update().
*/
function feeds_node_update($node) {
if (isset($node->feeds) && $importer_id = feeds_get_importer_id($node->type)) {
$source = feeds_source($importer_id, $node->nid);
$source->addConfig($node->feeds);
$source->save();
$source->ensureSchedule();
}
}
/**
* Implements hook_node_delete().
*/
function feeds_node_delete($node) {
// Make sure we don't leave any orphans behind: Do not use
// feeds_get_importer_id() to determine importer id as the importer may have
// been deleted.
if ($importer_id = db_query("SELECT id FROM {feeds_source} WHERE feed_nid = :nid", array(':nid' => $node->nid))->fetchField()) {
feeds_source($importer_id, $node->nid)->delete();
}
}
* Implements hook_form_BASE_FORM_ID_alter().
*/
function feeds_form_node_form_alter(&$form, $form_state) {
if ($importer_id = feeds_get_importer_id($form['#node']->type)) {
// Enable uploads.
$form['#attributes']['enctype'] = 'multipart/form-data';
// Build form.
$source = feeds_source($importer_id, empty($form['#node']->nid) ? NULL : $form['#node']->nid);
$form['feeds'] = array(
'#type' => 'fieldset',
'#title' => t('Feed'),
'#tree' => TRUE,
Dave Reid
committed
'#weight' => 0,
);
$form['feeds'] += $source->configForm($form_state);
$form['#feed_id'] = $importer_id;
// If the parser has support for delivering a source title, set node title
// to not required and try to retrieve it from the source if the node title
// is left empty by the user.
// @see feeds_node_validate()
if (isset($form['title']) && $source->importer()->parser->providesSourceTitle()) {
$form['title']['#required'] = FALSE;
}
Alex Barth
committed
}
}
Dave Reid
committed
/**
* Implements hook_field_extra_fields().
*/
function feeds_field_extra_fields() {
$extras = array();
foreach (node_type_get_names() as $type => $name) {
if (feeds_get_importer_id($type)) {
$extras['node'][$type]['form']['feeds'] = array(
'label' => t('Feed'),
'description' => t('Feeds module form elements'),
'weight' => 0,
);
}
}
return $extras;
}
megachriz
committed
/**
* Implements hook_features_pipe_COMPONENT_alter() for 'feeds_importer'.
megachriz
committed
*
* Automatically adds dependencies when a Feed importer is selected in Features.
*/
function feeds_features_pipe_feeds_importer_alter(&$pipe, $data, &$export) {
foreach ($data as $importer_id) {
if ($importer = feeds_importer_load($importer_id)) {
$export['dependencies'] = array_merge($export['dependencies'], $importer->dependencies());
}
}
}
Chris Leppanen
committed
/**
* Implements hook_system_info_alter().
*
* Goes through a list of all modules that provide Feeds plugins and makes them
* required if there are any importers using those plugins.
*/
function feeds_system_info_alter(array &$info, $file, $type) {
if ($type !== 'module' || !module_exists($file->name) || !module_hook($file->name, 'feeds_plugins')) {
Chris Leppanen
committed
return;
}
// Don't make Feeds require itself, otherwise you can't disable Feeds until
// all importers are deleted.
if ($file->name === 'feeds' || !function_exists('ctools_include')) {
return;
}
// Get the plugins that belong to the current module.
ctools_include('plugins');
$module_plugins = array();
foreach (ctools_get_plugins('feeds', 'plugins') as $plugin_id => $plugin) {
if ($file->name === $plugin['module']) {
$module_plugins[$plugin_id] = TRUE;
}
}
// Check if any importers are using any plugins from the current module.
foreach (feeds_importer_load_all(TRUE) as $importer) {
// Skip importers that are defined in code and are provided by the current
// module. This ensures that modules that define both an importer and a
// plugin can still be disabled.
if ($importer->export_type == EXPORT_IN_CODE) {
$configs = ctools_export_load_object('feeds_importer', 'names', array($importer->id));
if (isset($configs[$importer->id]) && $configs[$importer->id]->export_module === $file->name) {
continue;
}
}
Chris Leppanen
committed
$configuration = $importer->getConfig();
foreach (array('fetcher', 'parser', 'processor') as $plugin_type) {
$plugin_key = $configuration[$plugin_type]['plugin_key'];
if (isset($module_plugins[$plugin_key])) {
$info['required'] = TRUE;
break 2;
}
}
}
if (empty($info['required'])) {
return;
}
if (module_exists('feeds_ui') && user_access('administer feeds')) {
$info['explanation'] = t('Feeds is currently using this module for one or more <a href="@link">importers</a>', array('@link' => url('admin/structure/feeds')));
Chris Leppanen
committed
}
else {
$info['explanation'] = t('Feeds is currently using this module for one or more importers');
Chris Leppanen
committed
}
}
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
/**
* Implements hook_module_implements_alter().
*/
function feeds_module_implements_alter(array &$implementations, $hook) {
if ($hook === 'feeds_processor_targets_alter') {
// We need two implementations of this hook, so we add one that gets
// called first, and move the normal one to last.
$implementations = array('_feeds' => FALSE) + $implementations;
// Move normal implementation to last.
$group = $implementations['feeds'];
unset($implementations['feeds']);
$implementations['feeds'] = $group;
}
}
/**
* Implements hook_feeds_processor_targets_alter().
*
* @see feeds_feeds_processor_targets()
* @see feeds_feeds_processor_targets_alter()
*/
function _feeds_feeds_processor_targets_alter(array &$targets, $entity_type, $bundle) {
// If hook_feeds_processor_targets() hasn't been called, for instance, by
// older processors, invoke it ourself.
if (!drupal_static('feeds_feeds_processor_targets', FALSE)) {
$targets += module_invoke_all('feeds_processor_targets', $entity_type, $bundle);
}
}
/**
* Implements hook_flush_caches().
*/
function feeds_flush_caches() {
// The update to add the table needs to have run. Taken from
// https://www.drupal.org/node/2511858
include_once DRUPAL_ROOT . '/includes/install.inc';
if (drupal_get_installed_schema_version('feeds') >= 7212) {
return array('cache_feeds_http');
}
return array();
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
* Implements hook_admin_menu_output_build().
*
* Shows available importers in the "Content" section of the admin menu.
* Requires the "admin_menu" module to be enabled.
*/
function feeds_admin_menu_output_build(array &$content) {
// Add new top-level item to the menu.
if (!isset($content['menu'])) {
return;
}
$access = FALSE;
foreach (feeds_enabled_importers() as $importer_id) {
if (user_access('administer feeds') || user_access("import $importer_id feeds")) {
$access = TRUE;
break;
}
}
if (!$access) {
return;
}
$content['menu']['admin/content']['admin/content/feeds_import'] = array(
'#title' => t('Import'),
'#href' => 'import',
);
foreach (feeds_importer_load_all() as $importer) {
$content['menu']['admin/content']['admin/content/feeds_import'][$importer->id] = array(