You can create as many Feeds importer configurations as you would like to. Each can have a distinct purpose like letting your users aggregate RSS feeds or importing a CSV file for content migration. Here are a couple of things that are important to understand in order to get started with Feeds:
' . feeds_ui_edit_help() . '
';
unset($active_container['actions']);
break;
case 'fetcher':
case 'parser':
case 'processor':
$active_container['title'] = t('Select a !plugin_type', array('!plugin_type' => $active));
$active_container['body'] = drupal_get_form('feeds_ui_plugin_form', $importer, $active);
break;
case 'settings':
drupal_add_js(drupal_get_path('module', 'ctools') . '/js/dependent.js');
ctools_include('dependent');
if (empty($plugin_key)) {
$active_container['title'] = t('Basic settings');
$active_container['body'] = feeds_get_form($importer, 'configForm');
}
// feeds_plugin() returns a correct result because feed has been
// instantiated previously.
elseif (in_array($plugin_key, array_keys($plugins)) && $plugin = feeds_plugin($plugin_key, $importer->id)) {
$active_container['title'] = t('Settings for !plugin', array('!plugin' => $plugins[$plugin_key]['name']));
$active_container['body'] = feeds_get_form($plugin, 'configForm');
}
break;
case 'mapping':
$processor_name = isset($plugins[$config['processor']['plugin_key']]['name']) ? $plugins[$config['processor']['plugin_key']]['name'] : $plugins['FeedsMissingPlugin']['name'];
$active_container['title'] = t('Mapping for @processor', array('@processor' => $processor_name));
$active_container['body'] = drupal_get_form('feeds_ui_mapping_form', $importer);
break;
}
// Build config info.
$config_info = $info = array();
$info['class'] = array('config-set');
// Basic information.
$items = array();
$items[] = t('Attached to: @type', array('@type' => $importer->config['content_type'] ? node_type_get_name($importer->config['content_type']) : t('[none]')));
if ($importer->config['import_period'] == FEEDS_SCHEDULE_NEVER) {
$import_period = t('off');
}
elseif ($importer->config['import_period'] == 0) {
$import_period = t('as often as possible');
}
else {
$import_period = t('every !interval', array('!interval' => format_interval($importer->config['import_period'])));
}
$items[] = t('Periodic import: !import_period', array('!import_period' => $import_period));
$items[] = $importer->config['import_on_create'] ? t('Import on submission') : t('Do not import on submission');
$info['title'] = t('Basic settings');
$info['body'] = array(
array(
'body' => theme('item_list', array('items' => $items)),
'actions' => array(l(t('Settings'), $path . '/settings')),
),
);
$config_info[] = $info;
// Fetcher.
$fetcher = isset($plugins[$config['fetcher']['plugin_key']]) ? $plugins[$config['fetcher']['plugin_key']] : $plugins['FeedsMissingPlugin'];
$actions = array();
if ($importer->fetcher->hasConfigForm()) {
$actions = array(l(t('Settings'), $path . '/settings/' . $config['fetcher']['plugin_key']));
}
$info['title'] = t('Fetcher');
$info['body'] = array(
array(
'title' => $fetcher['name'],
'body' => $fetcher['description'],
'actions' => $actions,
),
);
$info['actions'] = array(l(t('Change'), $path . '/fetcher'));
$config_info[] = $info;
// Parser.
$parser = isset($plugins[$config['parser']['plugin_key']]) ? $plugins[$config['parser']['plugin_key']] : $plugins['FeedsMissingPlugin'];
$actions = array();
if ($importer->parser->hasConfigForm()) {
$actions = array(l(t('Settings'), $path . '/settings/' . $config['parser']['plugin_key']));
}
$info['title'] = t('Parser');
$info['body'] = array(
array(
'title' => $parser['name'],
'body' => $parser['description'],
'actions' => $actions,
)
);
$info['actions'] = array(l(t('Change'), $path . '/parser'));
$config_info[] = $info;
// Processor.
$processor = isset($plugins[$config['processor']['plugin_key']]) ? $plugins[$config['processor']['plugin_key']] : $plugins['FeedsMissingPlugin'];
$actions = array();
if ($importer->processor->hasConfigForm()) {
$actions[] = l(t('Settings'), $path . '/settings/' . $config['processor']['plugin_key']);
}
$actions[] = l(t('Mapping'), $path . '/mapping');
$info['title'] = t('Processor');
$info['body'] = array(
array(
'title' => $processor['name'],
'body' => $processor['description'],
'actions' => $actions,
)
);
$info['actions'] = array(l(t('Change'), $path . '/processor'));
$config_info[] = $info;
return theme('feeds_ui_edit_page', array(
'info' => $config_info,
'active' => $active_container,
));
}
/**
* Build a form of plugins to pick from.
*
* @param $form_state
* Form API form state array.
* @param $importer
* FeedsImporter object.
* @param $type
* Plugin type. One of 'fetcher', 'parser', 'processor'.
*
* @return
* A Form API form definition.
*/
function feeds_ui_plugin_form($form, &$form_state, $importer, $type) {
$plugins = FeedsPlugin::byType($type);
$form['#importer'] = $importer->id;
$form['#plugin_type'] = $type;
$importer_key = $importer->config[$type]['plugin_key'];
foreach ($plugins as $key => $plugin) {
$form['plugin_key'][$key] = array(
'#type' => 'radio',
'#parents' => array('plugin_key'),
'#title' => check_plain($plugin['name']),
'#description' => filter_xss(isset($plugin['help']) ? $plugin['help'] : $plugin['description']),
'#return_value' => $key,
'#default_value' => ($key == $importer_key) ? $key : '',
);
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
return $form;
}
/**
* Submit handler for feeds_ui_plugin_form().
*/
function feeds_ui_plugin_form_submit($form, &$form_state) {
// Set the plugin and save feed.
$importer = feeds_importer($form['#importer']);
$importer->setPlugin($form_state['values']['plugin_key']);
$importer->save();
drupal_set_message(t('Changed @type plugin.', array('@type' => $form['#plugin_type'])));
}
/**
* Theme feeds_ui_plugin_form().
*/
function theme_feeds_ui_plugin_form($variables) {
$form = $variables['form'];
$output = '';
foreach (element_children($form['plugin_key']) as $key) {
// Assemble container, render form elements.
$container = array(
'title' => $form['plugin_key'][$key]['#title'],
'body' => isset($form['plugin_key'][$key]['#description']) ? $form['plugin_key'][$key]['#description'] : '',
);
$form['plugin_key'][$key]['#title'] = t('Select');
$form['plugin_key'][$key]['#attributes']['class'] = array('feeds-ui-radio-link');
unset($form['plugin_key'][$key]['#description']);
$container['actions'] = array(drupal_render($form['plugin_key'][$key]));
$output .= theme('feeds_ui_container', array('container' => $container));
}
$output .= drupal_render_children($form);
return $output;
}
/**
* Edit mapping.
*
* @todo Completely merge this into config form handling. This is just a
* shared form of configuration, most of the common functionality can live in
* FeedsProcessor, a flag can tell whether mapping is supported or not.
*/
function feeds_ui_mapping_form($form, &$form_state, $importer) {
$form['#importer'] = $importer->id;
$form['#mappings'] = $mappings = $importer->processor->getMappings();
$form['help']['#markup'] = feeds_ui_mapping_help();
$form['#prefix'] = '';
return $output;
}
/**
* Render a simple container. A container can have a title, a description and
* one or more actions. Recursive.
*
* @todo Replace with theme_fieldset or a wrapper to theme_fieldset?
*
* @param $variables
* An array containing an array at 'container'.
* A 'container' array may contain one or more of the following keys:
* array(
* 'title' => 'the title',
* 'body' => 'the body of the container, may also be an array of more
* containers or a renderable array.',
* 'class' => array('the class of the container.'),
* 'id' => 'the id of the container',
* );
*/
function theme_feeds_ui_container($variables) {
$container = $variables['container'];
$class = array_merge(array('feeds-container'), empty($container['class']) ? array('plain') : $container['class']);
$id = empty($container['id']) ? '': ' id="' . $container['id'] . '"';
$output = '
';
if (isset($container['actions']) && count($container['actions'])) {
$output .= '
';
foreach ($container['actions'] as $action) {
$output .= '- ' . $action . '
';
}
$output .= '
';
}
if (!empty($container['title'])) {
$output .= '
';
$output .= $container['title'];
$output .= '
';
}
if (!empty($container['body'])) {
$output .= '
';
if (is_array($container['body'])) {
if (isset($container['body']['#type'])) {
$output .= drupal_render($container['body']);
}
else {
foreach ($container['body'] as $c) {
$output .= theme('feeds_ui_container', array('container' => $c));
}
}
}
else {
$output .= $container['body'];
}
$output .= '
';
}
$output .= '
';
return $output;
}
/**
* Theme function for feeds_ui_mapping_form().
*/
function theme_feeds_ui_mapping_form($variables) {
$form = $variables['form'];
$targets = feeds_importer($form['#importer'])->processor->getMappingTargets();
$targets = _feeds_ui_format_options($targets, TRUE);
$sources = feeds_importer($form['#importer'])->parser->getMappingSources();
// Some parsers do not define source options.
$sources = $sources ? _feeds_ui_format_options($sources, TRUE) : array();
// Build the actual mapping table.
$header = array(
t('Source'),
t('Target'),
t('Target configuration'),
' ',
t('Weight'),
);
$rows = array();
if (is_array($form['#mappings'])) {
foreach ($form['#mappings'] as $i => $mapping) {
$source = isset($sources[$mapping['source']]) ? check_plain($sources[$mapping['source']]) : check_plain($mapping['source']);
$target = isset($targets[$mapping['target']]) ? check_plain($targets[$mapping['target']]) : '
' . t('Missing') . '';
// Add indicator to target if target configuration changed.
if (isset($form['#mapping_settings'][$i])) {
$target .= '
*';
}
$rows[] = array(
'data' => array(
$source,
$target,
drupal_render($form['config'][$i]),
drupal_render($form['remove_flags'][$i]),
drupal_render($form['mapping_weight'][$i]),
),
'class' => array('draggable', 'tabledrag-leaf'),
);
}
}
if (!count($rows)) {
$rows[] = array(
array(
'colspan' => 5,
'data' => t('No mappings defined.'),
),
);
}
$rows[] = array(
drupal_render($form['source']),
drupal_render($form['target']),
'',
drupal_render($form['add']),
'',
);
$output = '';
if (!empty($form['changed'])) {
// This form element is only available if target configuration changed.
$output .= drupal_render($form['changed']);
}
$output .= '
' . drupal_render($form['help']) . '
';
$output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'feeds-ui-mapping-overview')));
// Build the help table that explains available sources.
$legend = '';
$rows = array();
foreach (element_children($form['legendset']['legend']['sources']) as $k) {
$rows[] = array(
check_plain(drupal_render($form['legendset']['legend']['sources'][$k]['name'])),
check_plain(drupal_render($form['legendset']['legend']['sources'][$k]['description'])),
);
}
if (count($rows)) {
$legend .= '
' . t('Sources') . '
';
$legend .= theme('table', array('header' => array(t('Name'), t('Description')), 'rows' => $rows));
}
// Build the help table that explains available targets.
$rows = array();
foreach (element_children($form['legendset']['legend']['targets']) as $k) {
$rows[] = array(
check_plain(drupal_render($form['legendset']['legend']['targets'][$k]['name']) . ' (' . $k . ')'),
check_plain(drupal_render($form['legendset']['legend']['targets'][$k]['description'])),
);
}
$legend .= '
' . t('Targets') . '
';
$legend .= theme('table', array('header' => array(t('Name'), t('Description')), 'rows' => $rows));
// Stick tables into collapsible fieldset.
$form['legendset']['legend'] = array(
'#markup' => '
' . $legend . '
',
);
$output .= drupal_render($form['legendset']);
$output .= drupal_render_children($form);
drupal_add_tabledrag('feeds-ui-mapping-overview', 'order', 'sibling', 'feeds-ui-mapping-weight');
return $output;
}
/**
* Page callback to import a Feeds importer.
*/
function feeds_ui_importer_import($form, &$form_state) {
$form['id'] = array(
'#type' => 'textfield',
'#title' => t('Importer id'),
'#description' => t('Enter the id to use for this importer if it is different from the source importer. Leave blank to use the id of the importer.'),
);
$form['id_override'] = array(
'#type' => 'checkbox',
'#title' => t('Replace an existing importer if one exists with the same id.'),
);
$form['bypass_validation'] = array(
'#type' => 'checkbox',
'#title' => t('Bypass importer validation'),
'#description' => t('Bypass the validation of plugins when importing.'),
);
$form['importer'] = array(
'#type' => 'textarea',
'#rows' => 10,
);
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Import'),
);
return $form;
}
/**
* Form validation handler for feeds_ui_importer_import().
*
* @see feeds_ui_importer_import_submit()
*/
function feeds_ui_importer_import_validate($form, &$form_state) {
$form_state['values']['importer'] = trim($form_state['values']['importer']);
$form_state['values']['id'] = trim($form_state['values']['id']);
if (!empty($form_state['values']['id']) && preg_match('/[^a-zA-Z0-9_]/', $form_state['values']['id'])) {
form_error($form['id'], t('Feeds importer id must be alphanumeric with underscores only.'));
}
if (substr($form_state['values']['importer'], 0, 5) == 'api_version) || $feeds_importer->api_version < 1) {
form_error($form['importer'], t('The importer is not compatible with this version of Feeds.'));
}
elseif (version_compare($feeds_importer->api_version, feeds_api_version(), '>')) {
form_error($form['importer'], t('That importer is created for the version %import_version of Feeds, but you only have version %api_version.', array(
'%import_version' => $feeds_importer->api_version,
'%api_version' => feeds_api_version())));
}
// Change to user-supplied id.
if ($form_state['values']['id']) {
$feeds_importer->id = $form_state['values']['id'];
}
$exists = feeds_ui_importer_machine_name_exists($feeds_importer->id);
if ($exists && !$form_state['values']['id_override']) {
if (feeds_importer($feeds_importer->id)->export_type != EXPORT_IN_CODE) {
return form_error($form['id'], t('Feeds importer already exists with that id.'));
}
}
if (!$form_state['values']['bypass_validation']) {
foreach (array('fetcher', 'parser', 'processor') as $type) {
$plugin = feeds_plugin($feeds_importer->config[$type]['plugin_key'], $feeds_importer->id);
if (get_class($plugin) == 'FeedsMissingPlugin') {
form_error($form['importer'], t('The plugin %plugin is unavailable.', array('%plugin' => $feeds_importer->config[$type]['plugin_key'])));
}
}
}
$form_state['importer'] = $feeds_importer;
}
/**
* Form submission handler for feeds_ui_importer_import().
*
* @see feeds_ui_importer_import_validate()
*/
function feeds_ui_importer_import_submit($form, &$form_state) {
$importer = $form_state['importer'];
// Create a copy of the importer to preserve config.
$save = feeds_importer($importer->id);
$save->setConfig($importer->config);
foreach (array('fetcher', 'parser', 'processor') as $type) {
$save->setPlugin($importer->config[$type]['plugin_key']);
$save->$type->setConfig($importer->config[$type]['config']);
}
$save->save();
drupal_set_message(t('Successfully imported the %id feeds importer.', array('%id' => $importer->id)));
$form_state['redirect'] = 'admin/structure/feeds';
}