Skip to content

Instantly share code, notes, and snippets.

@Renrhaf
Last active July 19, 2016 11:22
Show Gist options
  • Save Renrhaf/f3d6b50efc9800dff058464bdfe92d54 to your computer and use it in GitHub Desktop.
Save Renrhaf/f3d6b50efc9800dff058464bdfe92d54 to your computer and use it in GitHub Desktop.
INFO-1041
diff --git a/foundation.make b/foundation.make
index 9ac33e4..7a9eae2 100755
--- a/foundation.make
+++ b/foundation.make
@@ -353,6 +353,28 @@ projects[migrate_extras][subdir] = "contrib"
; Add support for bean migrate; see http://drupal.org/node/1977058
projects[migrate_extras][patch][] = "http://drupal.org/files/migrate_extras_entity_api_entity_keys_name.patch"
+projects[job_scheduler][version] = 2.0-alpha3
+projects[job_scheduler][subdir] = "contrib"
+
+projects[feeds][version] = 2.0-beta1
+projects[feeds][subdir] = "contrib"
+
+projects[feeds_fetcher_directory][version] = 2.0-beta5
+projects[feeds_fetcher_directory][subdir] = "contrib"
+projects[feeds_fetcher_directory][patch][] = "https://www.drupal.org/files/issues/order-files-2085629-1.patch"
+
+projects[feeds_ftp_fetcher][version] = 1.x-dev
+projects[feeds_ftp_fetcher][subdir] = "contrib"
+
+projects[scald_feeds][download][type] = git
+projects[scald_feeds][download][revision] = 775fd31375c494c528a6bbb0f574f62f9d6e2f96
+projects[scald_feeds][download][branch] = 7.x-1.x
+projects[scald_feeds][subdir] = "contrib"
+; Check if an image already exists in an atom when importing (https://www.drupal.org/node/2285753)
+projects[scald_feeds][patch][] = "https://www.drupal.org/files/issues/combine_existing_atoms-2285753-12.patch"
+; Empty data caused errors, improve empty string detection (https://www.drupal.org/node/2539286)
+projects[scald_feeds][patch][] = "https://www.drupal.org/files/issues/empty_array_detection-2539286-1_0.patch"
+
projects[field_rename][version] = 1.0-beta1
projects[field_rename][subdir] = "contrib"
diff --git a/modules/custom/arte_afp/arte_afp.features.field_base.inc b/modules/custom/arte_afp/arte_afp.features.field_base.inc
new file mode 100644
index 0000000..d1ae6c7
--- /dev/null
+++ b/modules/custom/arte_afp/arte_afp.features.field_base.inc
@@ -0,0 +1,269 @@
+<?php
+/**
+ * @file
+ * arte_afp.features.field_base.inc
+ */
+
+/**
+ * Implements hook_field_default_field_bases().
+ */
+function arte_afp_field_default_field_bases() {
+ $field_bases = array();
+
+ // Exported field_base: 'field_afp_story_author'.
+ $field_bases['field_afp_story_author'] = array(
+ 'active' => 1,
+ 'cardinality' => 1,
+ 'deleted' => 0,
+ 'entity_types' => array(),
+ 'field_name' => 'field_afp_story_author',
+ 'indexes' => array(
+ 'format' => array(
+ 0 => 'format',
+ ),
+ ),
+ 'locked' => 0,
+ 'module' => 'text',
+ 'settings' => array(
+ 'entity_translation_sync' => FALSE,
+ 'max_length' => 255,
+ ),
+ 'translatable' => 0,
+ 'type' => 'text',
+ );
+
+ // Exported field_base: 'field_afp_story_categories'.
+ $field_bases['field_afp_story_categories'] = array(
+ 'active' => 1,
+ 'cardinality' => 1,
+ 'deleted' => 0,
+ 'entity_types' => array(),
+ 'field_name' => 'field_afp_story_categories',
+ 'indexes' => array(
+ 'target_id' => array(
+ 0 => 'target_id',
+ ),
+ ),
+ 'locked' => 0,
+ 'module' => 'entityreference',
+ 'settings' => array(
+ 'entity_translation_sync' => FALSE,
+ 'handler' => 'base',
+ 'handler_settings' => array(
+ 'autocomplete_items_limit' => 10,
+ 'behaviors' => array(
+ 'views-select-list' => array(
+ 'status' => 0,
+ ),
+ ),
+ 'sort' => array(
+ 'direction' => 'ASC',
+ 'property' => 'name',
+ 'type' => 'property',
+ ),
+ 'target_bundles' => array(
+ 'afp_tags' => 'afp_tags',
+ ),
+ ),
+ 'target_type' => 'taxonomy_term',
+ ),
+ 'translatable' => 0,
+ 'type' => 'entityreference',
+ );
+
+ // Exported field_base: 'field_afp_story_copyright'.
+ $field_bases['field_afp_story_copyright'] = array(
+ 'active' => 1,
+ 'cardinality' => 1,
+ 'deleted' => 0,
+ 'entity_types' => array(),
+ 'field_name' => 'field_afp_story_copyright',
+ 'indexes' => array(
+ 'format' => array(
+ 0 => 'format',
+ ),
+ ),
+ 'locked' => 0,
+ 'module' => 'text',
+ 'settings' => array(
+ 'entity_translation_sync' => FALSE,
+ 'max_length' => 255,
+ ),
+ 'translatable' => 0,
+ 'type' => 'text',
+ );
+
+ // Exported field_base: 'field_afp_story_creation_date'.
+ $field_bases['field_afp_story_creation_date'] = array(
+ 'active' => 1,
+ 'cardinality' => 1,
+ 'deleted' => 0,
+ 'entity_types' => array(),
+ 'field_name' => 'field_afp_story_creation_date',
+ 'indexes' => array(),
+ 'locked' => 0,
+ 'module' => 'date',
+ 'settings' => array(
+ 'cache_count' => 4,
+ 'cache_enabled' => 0,
+ 'entity_translation_sync' => FALSE,
+ 'granularity' => array(
+ 'day' => 'day',
+ 'hour' => 'hour',
+ 'minute' => 'minute',
+ 'month' => 'month',
+ 'second' => 0,
+ 'year' => 'year',
+ ),
+ 'timezone_db' => 'UTC',
+ 'todate' => '',
+ 'tz_handling' => 'site',
+ ),
+ 'translatable' => 0,
+ 'type' => 'datetime',
+ );
+
+ // Exported field_base: 'field_afp_story_images'.
+ $field_bases['field_afp_story_images'] = array(
+ 'active' => 1,
+ 'cardinality' => -1,
+ 'deleted' => 0,
+ 'entity_types' => array(),
+ 'field_name' => 'field_afp_story_images',
+ 'indexes' => array(
+ 'sid' => array(
+ 0 => 'sid',
+ ),
+ ),
+ 'locked' => 0,
+ 'module' => 'atom_reference',
+ 'settings' => array(
+ 'entity_translation_sync' => FALSE,
+ ),
+ 'translatable' => 0,
+ 'type' => 'atom_reference',
+ );
+
+ // Exported field_base: 'field_afp_story_item_id'.
+ $field_bases['field_afp_story_item_id'] = array(
+ 'active' => 1,
+ 'cardinality' => 1,
+ 'deleted' => 0,
+ 'entity_types' => array(),
+ 'field_name' => 'field_afp_story_item_id',
+ 'indexes' => array(
+ 'format' => array(
+ 0 => 'format',
+ ),
+ ),
+ 'locked' => 0,
+ 'module' => 'text',
+ 'settings' => array(
+ 'entity_translation_sync' => FALSE,
+ 'max_length' => 255,
+ ),
+ 'translatable' => 0,
+ 'type' => 'text',
+ );
+
+ // Exported field_base: 'field_afp_story_place'.
+ $field_bases['field_afp_story_place'] = array(
+ 'active' => 1,
+ 'cardinality' => 1,
+ 'deleted' => 0,
+ 'entity_types' => array(),
+ 'field_name' => 'field_afp_story_place',
+ 'indexes' => array(
+ 'format' => array(
+ 0 => 'format',
+ ),
+ ),
+ 'locked' => 0,
+ 'module' => 'text',
+ 'settings' => array(
+ 'entity_translation_sync' => FALSE,
+ 'max_length' => 255,
+ ),
+ 'translatable' => 0,
+ 'type' => 'text',
+ );
+
+ // Exported field_base: 'field_afp_story_revision_id'.
+ $field_bases['field_afp_story_revision_id'] = array(
+ 'active' => 1,
+ 'cardinality' => 1,
+ 'deleted' => 0,
+ 'entity_types' => array(),
+ 'field_name' => 'field_afp_story_revision_id',
+ 'indexes' => array(),
+ 'locked' => 0,
+ 'module' => 'number',
+ 'settings' => array(
+ 'entity_translation_sync' => FALSE,
+ ),
+ 'translatable' => 0,
+ 'type' => 'number_integer',
+ );
+
+ // Exported field_base: 'field_afp_story_unpublish_date'.
+ $field_bases['field_afp_story_unpublish_date'] = array(
+ 'active' => 1,
+ 'cardinality' => 1,
+ 'deleted' => 0,
+ 'entity_types' => array(),
+ 'field_name' => 'field_afp_story_unpublish_date',
+ 'indexes' => array(),
+ 'locked' => 0,
+ 'module' => 'date',
+ 'settings' => array(
+ 'cache_count' => 4,
+ 'cache_enabled' => 0,
+ 'entity_translation_sync' => FALSE,
+ 'granularity' => array(
+ 'day' => 'day',
+ 'hour' => 'hour',
+ 'minute' => 'minute',
+ 'month' => 'month',
+ 'second' => 0,
+ 'year' => 'year',
+ ),
+ 'timezone_db' => 'UTC',
+ 'todate' => '',
+ 'tz_handling' => 'site',
+ ),
+ 'translatable' => 0,
+ 'type' => 'datetime',
+ );
+
+ // Exported field_base: 'field_afp_story_update_date'.
+ $field_bases['field_afp_story_update_date'] = array(
+ 'active' => 1,
+ 'cardinality' => 1,
+ 'deleted' => 0,
+ 'entity_types' => array(),
+ 'field_name' => 'field_afp_story_update_date',
+ 'indexes' => array(),
+ 'locked' => 0,
+ 'module' => 'date',
+ 'settings' => array(
+ 'cache_count' => 4,
+ 'cache_enabled' => 0,
+ 'entity_translation_sync' => FALSE,
+ 'granularity' => array(
+ 'day' => 'day',
+ 'hour' => 'hour',
+ 'minute' => 'minute',
+ 'month' => 'month',
+ 'second' => 0,
+ 'year' => 'year',
+ ),
+ 'timezone_db' => 'UTC',
+ 'todate' => '',
+ 'tz_handling' => 'site',
+ ),
+ 'translatable' => 0,
+ 'type' => 'datetime',
+ );
+
+ return $field_bases;
+}
diff --git a/modules/custom/arte_afp/arte_afp.features.inc b/modules/custom/arte_afp/arte_afp.features.inc
new file mode 100644
index 0000000..9159f97
--- /dev/null
+++ b/modules/custom/arte_afp/arte_afp.features.inc
@@ -0,0 +1,43 @@
+<?php
+/**
+ * @file
+ * arte_afp.features.inc
+ */
+
+/**
+ * Implements hook_ctools_plugin_api().
+ */
+function arte_afp_ctools_plugin_api($module = NULL, $api = NULL) {
+ if ($module == "feeds" && $api == "feeds_importer_default") {
+ return array("version" => "1");
+ }
+ if ($module == "strongarm" && $api == "strongarm") {
+ return array("version" => "1");
+ }
+}
+
+/**
+ * Implements hook_node_info().
+ */
+function arte_afp_node_info() {
+ $items = array(
+ 'afp_feed' => array(
+ 'name' => t('AFP Feed'),
+ 'base' => 'node_content',
+ 'description' => t('Subscribes to AFP Feeds importer. Creates "AFP Story" nodes from feed contents.'),
+ 'has_title' => '1',
+ 'title_label' => t('Titre'),
+ 'help' => '',
+ ),
+ 'afp_story' => array(
+ 'name' => t('AFP Story'),
+ 'base' => 'node_content',
+ 'description' => t('AFP Story, meant to be automatically created by an AFP Feed.'),
+ 'has_title' => '1',
+ 'title_label' => t('Titre'),
+ 'help' => '',
+ ),
+ );
+ drupal_alter('node_info', $items);
+ return $items;
+}
diff --git a/modules/custom/arte_afp/arte_afp.features.taxonomy.inc b/modules/custom/arte_afp/arte_afp.features.taxonomy.inc
new file mode 100644
index 0000000..4221ccc
--- /dev/null
+++ b/modules/custom/arte_afp/arte_afp.features.taxonomy.inc
@@ -0,0 +1,38 @@
+<?php
+/**
+ * @file
+ * arte_afp.features.taxonomy.inc
+ */
+
+/**
+ * Implements hook_taxonomy_default_vocabularies().
+ */
+function arte_afp_taxonomy_default_vocabularies() {
+ return array(
+ 'afp_tags' => array(
+ 'name' => 'AFP Tags',
+ 'machine_name' => 'afp_tags',
+ 'description' => '',
+ 'hierarchy' => 0,
+ 'module' => 'taxonomy',
+ 'weight' => 0,
+ 'language' => 'und',
+ 'i18n_mode' => 4,
+ 'rdf_mapping' => array(
+ 'rdftype' => array(
+ 0 => 'skos:ConceptScheme',
+ ),
+ 'name' => array(
+ 'predicates' => array(
+ 0 => 'dc:title',
+ ),
+ ),
+ 'description' => array(
+ 'predicates' => array(
+ 0 => 'rdfs:comment',
+ ),
+ ),
+ ),
+ ),
+ );
+}
diff --git a/modules/custom/arte_afp/arte_afp.feeds_importer_default.inc b/modules/custom/arte_afp/arte_afp.feeds_importer_default.inc
new file mode 100644
index 0000000..a15c977
--- /dev/null
+++ b/modules/custom/arte_afp/arte_afp.feeds_importer_default.inc
@@ -0,0 +1,124 @@
+<?php
+/**
+ * @file
+ * arte_afp.feeds_importer_default.inc
+ */
+
+/**
+ * Implements hook_feeds_importer_default().
+ */
+function arte_afp_feeds_importer_default() {
+ $export = array();
+
+ $feeds_importer = new stdClass();
+ $feeds_importer->disabled = FALSE; /* Edit this to true to make a default feeds_importer disabled initially */
+ $feeds_importer->api_version = 1;
+ $feeds_importer->id = 'afp_feeds';
+ $feeds_importer->config = array(
+ 'name' => 'AFP Journal Internet Standard',
+ 'description' => '',
+ 'fetcher' => array(
+ 'plugin_key' => 'FeedsAFPFetcher',
+ 'config' => array(
+ 'server' => 'ftp-clients.afp.com',
+ 'port' => 21,
+ 'directories' => '/pub/francais/journal/, /pub/deutsch/spezial/',
+ 'username' => 'arte-',
+ 'password' => '',
+ ),
+ ),
+ 'parser' => array(
+ 'plugin_key' => 'FeedsAFPParser',
+ 'config' => array(),
+ ),
+ 'processor' => array(
+ 'plugin_key' => 'FeedsNodeProcessor',
+ 'config' => array(
+ 'expire' => '604800',
+ 'author' => 0,
+ 'authorize' => 0,
+ 'mappings' => array(
+ 0 => array(
+ 'source' => 'NewsItemId',
+ 'target' => 'guid',
+ 'unique' => 1,
+ ),
+ 1 => array(
+ 'source' => 'body',
+ 'target' => 'body',
+ 'unique' => FALSE,
+ ),
+ 2 => array(
+ 'source' => 'HeadLine',
+ 'target' => 'title',
+ 'unique' => FALSE,
+ ),
+ 3 => array(
+ 'source' => 'Language',
+ 'target' => 'language',
+ 'unique' => FALSE,
+ ),
+ 4 => array(
+ 'source' => 'ByLine',
+ 'target' => 'field_afp_story_author',
+ 'unique' => FALSE,
+ ),
+ 5 => array(
+ 'source' => 'DateLine',
+ 'target' => 'field_afp_story_place',
+ 'unique' => FALSE,
+ ),
+ 6 => array(
+ 'source' => 'categories',
+ 'target' => 'field_afp_story_categories:tid',
+ 'unique' => FALSE,
+ ),
+ 7 => array(
+ 'source' => 'CopyrightLine',
+ 'target' => 'field_afp_story_copyright',
+ 'unique' => FALSE,
+ ),
+ 8 => array(
+ 'source' => 'photoHrefphoto0',
+ 'target' => 'field_afp_story_images',
+ 'unique' => FALSE,
+ ),
+ 9 => array(
+ 'source' => 'FirstCreated',
+ 'target' => 'field_afp_story_creation_date:start',
+ 'unique' => FALSE,
+ ),
+ 10 => array(
+ 'source' => 'ThisRevisionCreated',
+ 'target' => 'field_afp_story_update_date:start',
+ 'unique' => FALSE,
+ ),
+ 11 => array(
+ 'source' => 'NewsItemId',
+ 'target' => 'field_afp_story_item_id',
+ 'unique' => FALSE,
+ ),
+ 12 => array(
+ 'source' => 'RevisionId',
+ 'target' => 'field_afp_story_revision_id',
+ 'unique' => FALSE,
+ ),
+ ),
+ 'update_existing' => '2',
+ 'update_non_existent' => 'skip',
+ 'input_format' => 'full_html',
+ 'skip_hash_check' => 0,
+ 'bundle' => 'afp_story',
+ ),
+ ),
+ 'content_type' => 'afp_feed',
+ 'update' => 0,
+ 'import_period' => '0',
+ 'expire_period' => 3600,
+ 'import_on_create' => 1,
+ 'process_in_background' => 0,
+ );
+ $export['afp_feeds'] = $feeds_importer;
+
+ return $export;
+}
diff --git a/modules/custom/arte_afp/arte_afp.info b/modules/custom/arte_afp/arte_afp.info
new file mode 100644
index 0000000..145c6f6
--- /dev/null
+++ b/modules/custom/arte_afp/arte_afp.info
@@ -0,0 +1,80 @@
+name = ARTE AFP
+description = Provides an AFP story fetcher that grabs AFP feeds frequently
+core = 7.x
+package = ARTE Foundation
+version = 7.x-1.0
+dependencies[] = arte_feeds_afp
+dependencies[] = atom_reference
+dependencies[] = ctools
+dependencies[] = date
+dependencies[] = entityreference
+dependencies[] = features
+dependencies[] = feeds
+dependencies[] = i18n_taxonomy
+dependencies[] = number
+dependencies[] = strongarm
+dependencies[] = taxonomy
+dependencies[] = text
+dependencies[] = scald_feeds
+features[ctools][] = feeds:feeds_importer_default:1
+features[ctools][] = strongarm:strongarm:1
+features[features_api][] = api:2
+features[feeds_importer][] = afp_feeds
+features[field_base][] = field_afp_story_author
+features[field_base][] = field_afp_story_categories
+features[field_base][] = field_afp_story_copyright
+features[field_base][] = field_afp_story_creation_date
+features[field_base][] = field_afp_story_images
+features[field_base][] = field_afp_story_item_id
+features[field_base][] = field_afp_story_place
+features[field_base][] = field_afp_story_revision_id
+features[field_base][] = field_afp_story_unpublish_date
+features[field_base][] = field_afp_story_update_date
+features[node][] = afp_feed
+features[node][] = afp_story
+features[taxonomy][] = afp_tags
+features[variable][] = field_bundle_settings_node__afp_story
+features[variable][] = i18n_node_extended_afp_feed
+features[variable][] = i18n_node_extended_afp_story
+features[variable][] = i18n_node_options_afp_feed
+features[variable][] = i18n_node_options_afp_story
+features[variable][] = language_content_type_afp_feed
+features[variable][] = language_content_type_afp_story
+features[variable][] = menu_options_afp_feed
+features[variable][] = menu_options_afp_story
+features[variable][] = menu_parent_afp_feed
+features[variable][] = menu_parent_afp_story
+features[variable][] = node_options_afp_feed
+features[variable][] = node_options_afp_story
+features[variable][] = node_preview_afp_feed
+features[variable][] = node_preview_afp_story
+features[variable][] = node_submitted_afp_feed
+features[variable][] = node_submitted_afp_story
+features[variable][] = pathauto_node_afp_feed_pattern
+features[variable][] = pathauto_node_afp_story_de_pattern
+features[variable][] = pathauto_node_afp_story_en_pattern
+features[variable][] = pathauto_node_afp_story_fr_pattern
+features[variable][] = pathauto_node_afp_story_pattern
+features[variable][] = pathauto_node_afp_story_und_pattern
+features[variable][] = workbench_moderation_default_state_afp_feed
+features[variable][] = workbench_moderation_default_state_afp_story
+features[variable][] = xmlsitemap_settings_node_afp_feed
+features[variable][] = xmlsitemap_settings_node_afp_story
+features[variable][] = xmlsitemap_settings_taxonomy_term_afp_tags
+features_exclude[field_instance][node-afp_feed-body] = node-afp_feed-body
+features_exclude[field_instance][node-afp_feed-field_xiti_chapters] = node-afp_feed-field_xiti_chapters
+features_exclude[field_instance][node-afp_story-body] = node-afp_story-body
+features_exclude[field_instance][node-afp_story-field_afp_story_author] = node-afp_story-field_afp_story_author
+features_exclude[field_instance][node-afp_story-field_afp_story_categories] = node-afp_story-field_afp_story_categories
+features_exclude[field_instance][node-afp_story-field_afp_story_copyright] = node-afp_story-field_afp_story_copyright
+features_exclude[field_instance][node-afp_story-field_afp_story_creation_date] = node-afp_story-field_afp_story_creation_date
+features_exclude[field_instance][node-afp_story-field_afp_story_images] = node-afp_story-field_afp_story_images
+features_exclude[field_instance][node-afp_story-field_afp_story_item_id] = node-afp_story-field_afp_story_item_id
+features_exclude[field_instance][node-afp_story-field_afp_story_place] = node-afp_story-field_afp_story_place
+features_exclude[field_instance][node-afp_story-field_afp_story_revision_id] = node-afp_story-field_afp_story_revision_id
+features_exclude[field_instance][node-afp_story-field_afp_story_unpublish_date] = node-afp_story-field_afp_story_unpublish_date
+features_exclude[field_instance][node-afp_story-field_afp_story_update_date] = node-afp_story-field_afp_story_update_date
+features_exclude[field_instance][node-afp_story-field_xiti_chapters] = node-afp_story-field_xiti_chapters
+features_exclude[field_base][field_xiti_chapters] = field_xiti_chapters
+features_exclude[taxonomy][xiti_chapters] = xiti_chapters
+project path = profiles/foundation/modules/custom
diff --git a/modules/custom/arte_afp/arte_afp.module b/modules/custom/arte_afp/arte_afp.module
new file mode 100644
index 0000000..c4cc67a
--- /dev/null
+++ b/modules/custom/arte_afp/arte_afp.module
@@ -0,0 +1,7 @@
+<?php
+/**
+ * @file
+ * Code for the ARTE AFP feature.
+ */
+
+include_once 'arte_afp.features.inc';
diff --git a/modules/custom/arte_afp/arte_afp.strongarm.inc b/modules/custom/arte_afp/arte_afp.strongarm.inc
new file mode 100644
index 0000000..3d3b436
--- /dev/null
+++ b/modules/custom/arte_afp/arte_afp.strongarm.inc
@@ -0,0 +1,324 @@
+<?php
+/**
+ * @file
+ * arte_afp.strongarm.inc
+ */
+
+/**
+ * Implements hook_strongarm().
+ */
+function arte_afp_strongarm() {
+ $export = array();
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'field_bundle_settings_node__afp_story';
+ $strongarm->value = array(
+ 'view_modes' => array(),
+ 'extra_fields' => array(
+ 'form' => array(
+ 'language' => array(
+ 'weight' => '1',
+ ),
+ 'metatags' => array(
+ 'weight' => '18',
+ ),
+ 'title' => array(
+ 'weight' => '0',
+ ),
+ 'path' => array(
+ 'weight' => '15',
+ ),
+ 'redirect' => array(
+ 'weight' => '17',
+ ),
+ 'xmlsitemap' => array(
+ 'weight' => '16',
+ ),
+ ),
+ 'display' => array(),
+ ),
+ );
+ $export['field_bundle_settings_node__afp_story'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'i18n_node_extended_afp_feed';
+ $strongarm->value = 1;
+ $export['i18n_node_extended_afp_feed'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'i18n_node_extended_afp_story';
+ $strongarm->value = '1';
+ $export['i18n_node_extended_afp_story'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'i18n_node_options_afp_feed';
+ $strongarm->value = array();
+ $export['i18n_node_options_afp_feed'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'i18n_node_options_afp_story';
+ $strongarm->value = array();
+ $export['i18n_node_options_afp_story'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'language_content_type_afp_feed';
+ $strongarm->value = '0';
+ $export['language_content_type_afp_feed'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'language_content_type_afp_story';
+ $strongarm->value = '1';
+ $export['language_content_type_afp_story'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'menu_options_afp_feed';
+ $strongarm->value = array();
+ $export['menu_options_afp_feed'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'menu_options_afp_story';
+ $strongarm->value = array(
+ 0 => 'main-menu',
+ );
+ $export['menu_options_afp_story'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'menu_parent_afp_feed';
+ $strongarm->value = 'main-menu:0';
+ $export['menu_parent_afp_feed'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'menu_parent_afp_story';
+ $strongarm->value = 'main-menu:0';
+ $export['menu_parent_afp_story'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'node_options_afp_feed';
+ $strongarm->value = array(
+ 0 => 'status',
+ );
+ $export['node_options_afp_feed'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'node_options_afp_story';
+ $strongarm->value = array(
+ 0 => 'status',
+ );
+ $export['node_options_afp_story'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'node_preview_afp_feed';
+ $strongarm->value = '0';
+ $export['node_preview_afp_feed'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'node_preview_afp_story';
+ $strongarm->value = '1';
+ $export['node_preview_afp_story'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'node_submitted_afp_feed';
+ $strongarm->value = 1;
+ $export['node_submitted_afp_feed'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'node_submitted_afp_story';
+ $strongarm->value = 0;
+ $export['node_submitted_afp_story'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'pathauto_node_afp_feed_pattern';
+ $strongarm->value = 'afp/feed/[node:title]';
+ $export['pathauto_node_afp_feed_pattern'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'pathauto_node_afp_story_de_pattern';
+ $strongarm->value = 'afp/Neuigkeiten/[node:title]';
+ $export['pathauto_node_afp_story_de_pattern'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'pathauto_node_afp_story_en_pattern';
+ $strongarm->value = 'afp/story/[node:title]';
+ $export['pathauto_node_afp_story_en_pattern'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'pathauto_node_afp_story_fr_pattern';
+ $strongarm->value = 'afp/actualites/[node:title]';
+ $export['pathauto_node_afp_story_fr_pattern'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'pathauto_node_afp_story_pattern';
+ $strongarm->value = 'afp/story/[node:title]';
+ $export['pathauto_node_afp_story_pattern'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'pathauto_node_afp_story_und_pattern';
+ $strongarm->value = 'afp/story/[node:title]';
+ $export['pathauto_node_afp_story_und_pattern'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'workbench_moderation_default_state_afp_feed';
+ $strongarm->value = 'draft';
+ $export['workbench_moderation_default_state_afp_feed'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'workbench_moderation_default_state_afp_story';
+ $strongarm->value = 'draft';
+ $export['workbench_moderation_default_state_afp_story'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'xmlsitemap_settings_node_afp_feed';
+ $strongarm->value = array(
+ 'status' => '0',
+ 'priority' => '0.5',
+ );
+ $export['xmlsitemap_settings_node_afp_feed'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'xmlsitemap_settings_node_afp_story';
+ $strongarm->value = array(
+ 'entity' => 'node',
+ 'bundle' => 'afp_news_story',
+ 'info' => array(
+ 'label' => 'AFP News Story',
+ 'admin' => array(
+ 'path' => 'admin/structure/types/manage/%node_type',
+ 'real path' => 'admin/structure/types/manage/afp-news-story',
+ 'bundle argument' => 4,
+ 'access arguments' => array(
+ 0 => 'administer content types',
+ ),
+ ),
+ 'rdf_mapping' => array(
+ 'rdftype' => array(
+ 0 => 'sioc:Item',
+ 1 => 'foaf:Document',
+ ),
+ 'title' => array(
+ 'predicates' => array(
+ 0 => 'dc:title',
+ ),
+ ),
+ 'created' => array(
+ 'predicates' => array(
+ 0 => 'dc:date',
+ 1 => 'dc:created',
+ ),
+ 'datatype' => 'xsd:dateTime',
+ 'callback' => 'date_iso8601',
+ ),
+ 'changed' => array(
+ 'predicates' => array(
+ 0 => 'dc:modified',
+ ),
+ 'datatype' => 'xsd:dateTime',
+ 'callback' => 'date_iso8601',
+ ),
+ 'body' => array(
+ 'predicates' => array(
+ 0 => 'content:encoded',
+ ),
+ ),
+ 'uid' => array(
+ 'predicates' => array(
+ 0 => 'sioc:has_creator',
+ ),
+ 'type' => 'rel',
+ ),
+ 'name' => array(
+ 'predicates' => array(
+ 0 => 'foaf:name',
+ ),
+ ),
+ 'comment_count' => array(
+ 'predicates' => array(
+ 0 => 'sioc:num_replies',
+ ),
+ 'datatype' => 'xsd:integer',
+ ),
+ 'last_activity' => array(
+ 'predicates' => array(
+ 0 => 'sioc:last_activity_date',
+ ),
+ 'datatype' => 'xsd:dateTime',
+ 'callback' => 'date_iso8601',
+ ),
+ ),
+ 'xmlsitemap' => array(
+ 'entity' => 'node',
+ 'bundle' => 'afp_news_story',
+ 'status' => '0',
+ 'priority' => '0.5',
+ ),
+ ),
+ 'status' => '0',
+ 'priority' => '0.5',
+ );
+ $export['xmlsitemap_settings_node_afp_story'] = $strongarm;
+
+ $strongarm = new stdClass();
+ $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+ $strongarm->api_version = 1;
+ $strongarm->name = 'xmlsitemap_settings_taxonomy_term_afp_tags';
+ $strongarm->value = array(
+ 'status' => '0',
+ 'priority' => '0.5',
+ );
+ $export['xmlsitemap_settings_taxonomy_term_afp_tags'] = $strongarm;
+
+ return $export;
+}
diff --git a/modules/custom/arte_afp/modules/arte_afp_instances/arte_afp_instances.features.field_instance.inc b/modules/custom/arte_afp/modules/arte_afp_instances/arte_afp_instances.features.field_instance.inc
new file mode 100644
index 0000000..08238b7
--- /dev/null
+++ b/modules/custom/arte_afp/modules/arte_afp_instances/arte_afp_instances.features.field_instance.inc
@@ -0,0 +1,662 @@
+<?php
+/**
+ * @file
+ * arte_afp_instances.features.field_instance.inc
+ */
+
+/**
+ * Implements hook_field_default_field_instances().
+ */
+function arte_afp_instances_field_default_field_instances() {
+ $field_instances = array();
+
+ // Exported field_instance: 'node-afp_feed-body'.
+ $field_instances['node-afp_feed-body'] = array(
+ 'bundle' => 'afp_feed',
+ 'default_value' => NULL,
+ 'deleted' => 0,
+ 'description' => '',
+ 'display' => array(
+ 'default' => array(
+ 'label' => 'hidden',
+ 'module' => 'text',
+ 'settings' => array(
+ 'semantic_field_format' => 0,
+ ),
+ 'type' => 'text_default',
+ 'weight' => 1,
+ ),
+ 'teaser' => array(
+ 'label' => 'hidden',
+ 'module' => 'text',
+ 'settings' => array(
+ 'semantic_field_format' => 0,
+ 'trim_length' => 600,
+ ),
+ 'type' => 'text_summary_or_trimmed',
+ 'weight' => 1,
+ ),
+ ),
+ 'entity_type' => 'node',
+ 'field_name' => 'body',
+ 'label' => 'Body',
+ 'required' => FALSE,
+ 'settings' => array(
+ 'context' => '',
+ 'display_summary' => TRUE,
+ 'dnd_enabled' => 0,
+ 'entity_translation_sync' => FALSE,
+ 'mee_enabled' => 0,
+ 'text_processing' => 1,
+ 'user_register_form' => FALSE,
+ ),
+ 'widget' => array(
+ 'module' => 'text',
+ 'settings' => array(
+ 'rows' => 20,
+ 'summary_rows' => 5,
+ ),
+ 'type' => 'text_textarea_with_summary',
+ 'weight' => 31,
+ ),
+ );
+
+ // Exported field_instance: 'node-afp_story-body'.
+ $field_instances['node-afp_story-body'] = array(
+ 'bundle' => 'afp_story',
+ 'default_value' => NULL,
+ 'deleted' => 0,
+ 'description' => 'XML: DataContent',
+ 'display' => array(
+ 'default' => array(
+ 'label' => 'hidden',
+ 'module' => 'text',
+ 'settings' => array(
+ 'semantic_field_format' => 0,
+ ),
+ 'type' => 'text_default',
+ 'weight' => 1,
+ ),
+ 'teaser' => array(
+ 'label' => 'hidden',
+ 'module' => 'text',
+ 'settings' => array(
+ 'semantic_field_format' => 0,
+ 'trim_length' => 600,
+ ),
+ 'type' => 'text_summary_or_trimmed',
+ 'weight' => 1,
+ ),
+ ),
+ 'entity_type' => 'node',
+ 'field_name' => 'body',
+ 'label' => 'Body',
+ 'required' => 0,
+ 'settings' => array(
+ 'context' => 'full',
+ 'context_default' => 'sdl_editor_representation',
+ 'display_summary' => 1,
+ 'dnd_enabled' => 1,
+ 'entity_translation_sync' => FALSE,
+ 'mee_enabled' => 1,
+ 'semantic_field_format' => '',
+ 'text_processing' => 1,
+ 'user_register_form' => FALSE,
+ ),
+ 'widget' => array(
+ 'active' => 1,
+ 'module' => 'text',
+ 'settings' => array(
+ 'rows' => 20,
+ 'summary_rows' => 5,
+ ),
+ 'type' => 'text_textarea_with_summary',
+ 'weight' => 7,
+ ),
+ );
+
+ // Exported field_instance: 'node-afp_story-field_afp_story_author'.
+ $field_instances['node-afp_story-field_afp_story_author'] = array(
+ 'bundle' => 'afp_story',
+ 'default_value' => NULL,
+ 'deleted' => 0,
+ 'description' => 'XML: NewsLines:ByLine',
+ 'display' => array(
+ 'default' => array(
+ 'label' => 'above',
+ 'module' => 'text',
+ 'settings' => array(
+ 'semantic_field_format' => 0,
+ ),
+ 'type' => 'text_default',
+ 'weight' => 2,
+ ),
+ 'teaser' => array(
+ 'label' => 'above',
+ 'settings' => array(),
+ 'type' => 'hidden',
+ 'weight' => 0,
+ ),
+ ),
+ 'entity_type' => 'node',
+ 'field_name' => 'field_afp_story_author',
+ 'label' => 'Author',
+ 'required' => 0,
+ 'settings' => array(
+ 'context' => 'title',
+ 'context_default' => 'sdl_editor_representation',
+ 'dnd_enabled' => 0,
+ 'entity_translation_sync' => FALSE,
+ 'mee_enabled' => 0,
+ 'semantic_field_format' => '',
+ 'text_processing' => 0,
+ 'user_register_form' => FALSE,
+ ),
+ 'widget' => array(
+ 'active' => 1,
+ 'module' => 'text',
+ 'settings' => array(
+ 'size' => 60,
+ ),
+ 'type' => 'text_textfield',
+ 'weight' => 2,
+ ),
+ );
+
+ // Exported field_instance: 'node-afp_story-field_afp_story_categories'.
+ $field_instances['node-afp_story-field_afp_story_categories'] = array(
+ 'bundle' => 'afp_story',
+ 'default_value' => NULL,
+ 'deleted' => 0,
+ 'description' => '',
+ 'display' => array(
+ 'default' => array(
+ 'label' => 'above',
+ 'module' => 'entityreference',
+ 'settings' => array(
+ 'link' => FALSE,
+ 'semantic_field_format' => 0,
+ ),
+ 'type' => 'entityreference_label',
+ 'weight' => 12,
+ ),
+ 'teaser' => array(
+ 'label' => 'above',
+ 'settings' => array(),
+ 'type' => 'hidden',
+ 'weight' => 0,
+ ),
+ ),
+ 'entity_type' => 'node',
+ 'field_name' => 'field_afp_story_categories',
+ 'label' => 'Categories',
+ 'required' => 0,
+ 'settings' => array(
+ 'behaviors' => array(
+ 'taxonomy-index' => array(
+ 'status' => TRUE,
+ ),
+ ),
+ 'entity_translation_sync' => FALSE,
+ 'semantic_field_format' => '',
+ 'user_register_form' => FALSE,
+ ),
+ 'widget' => array(
+ 'active' => 1,
+ 'module' => 'entityreference',
+ 'settings' => array(
+ 'match_operator' => 'CONTAINS',
+ 'path' => '',
+ 'size' => 60,
+ ),
+ 'type' => 'entityreference_autocomplete_tags',
+ 'weight' => 6,
+ ),
+ );
+
+ // Exported field_instance: 'node-afp_story-field_afp_story_copyright'.
+ $field_instances['node-afp_story-field_afp_story_copyright'] = array(
+ 'bundle' => 'afp_story',
+ 'default_value' => NULL,
+ 'deleted' => 0,
+ 'description' => 'XML: NewsLines:CopyrightLine',
+ 'display' => array(
+ 'default' => array(
+ 'label' => 'above',
+ 'module' => 'text',
+ 'settings' => array(
+ 'semantic_field_format' => 0,
+ ),
+ 'type' => 'text_default',
+ 'weight' => 4,
+ ),
+ 'teaser' => array(
+ 'label' => 'above',
+ 'settings' => array(),
+ 'type' => 'hidden',
+ 'weight' => 0,
+ ),
+ ),
+ 'entity_type' => 'node',
+ 'field_name' => 'field_afp_story_copyright',
+ 'label' => 'Copyright',
+ 'required' => 0,
+ 'settings' => array(
+ 'context' => 'title',
+ 'context_default' => 'sdl_editor_representation',
+ 'dnd_enabled' => 0,
+ 'entity_translation_sync' => FALSE,
+ 'mee_enabled' => 0,
+ 'semantic_field_format' => '',
+ 'text_processing' => 0,
+ 'user_register_form' => FALSE,
+ ),
+ 'widget' => array(
+ 'active' => 1,
+ 'module' => 'text',
+ 'settings' => array(
+ 'size' => 60,
+ ),
+ 'type' => 'text_textfield',
+ 'weight' => 4,
+ ),
+ );
+
+ // Exported field_instance: 'node-afp_story-field_afp_story_creation_date'.
+ $field_instances['node-afp_story-field_afp_story_creation_date'] = array(
+ 'bundle' => 'afp_story',
+ 'default_value' => array(),
+ 'deleted' => 0,
+ 'description' => 'XML: FirstCreated',
+ 'display' => array(
+ 'default' => array(
+ 'label' => 'above',
+ 'module' => 'date',
+ 'settings' => array(
+ 'format_type' => 'long',
+ 'fromto' => 'both',
+ 'multiple_from' => '',
+ 'multiple_number' => '',
+ 'multiple_to' => '',
+ 'semantic_field_format' => 0,
+ 'show_remaining_days' => FALSE,
+ ),
+ 'type' => 'date_default',
+ 'weight' => 6,
+ ),
+ 'teaser' => array(
+ 'label' => 'above',
+ 'settings' => array(),
+ 'type' => 'hidden',
+ 'weight' => 0,
+ ),
+ ),
+ 'entity_type' => 'node',
+ 'field_name' => 'field_afp_story_creation_date',
+ 'label' => 'Creation date',
+ 'required' => 0,
+ 'settings' => array(
+ 'default_value' => 'now',
+ 'default_value2' => 'same',
+ 'default_value_code' => '',
+ 'default_value_code2' => '',
+ 'entity_translation_sync' => FALSE,
+ 'semantic_field_format' => '',
+ 'user_register_form' => FALSE,
+ ),
+ 'widget' => array(
+ 'active' => 1,
+ 'module' => 'date',
+ 'settings' => array(
+ 'increment' => 15,
+ 'input_format' => 'd/m/Y - H:i:s',
+ 'input_format_custom' => '',
+ 'label_position' => 'above',
+ 'no_fieldset' => 0,
+ 'text_parts' => array(),
+ 'year_range' => '-3:+3',
+ ),
+ 'type' => 'date_popup',
+ 'weight' => 9,
+ ),
+ );
+
+ // Exported field_instance: 'node-afp_story-field_afp_story_images'.
+ $field_instances['node-afp_story-field_afp_story_images'] = array(
+ 'bundle' => 'afp_story',
+ 'default_value' => NULL,
+ 'deleted' => 0,
+ 'description' => 'XML: ContentItem in HighDef',
+ 'display' => array(
+ 'default' => array(
+ 'label' => 'above',
+ 'module' => 'atom_reference',
+ 'settings' => array(
+ 'link' => 0,
+ 'override' => 0,
+ 'semantic_field_format' => 0,
+ ),
+ 'type' => 'title',
+ 'weight' => 5,
+ ),
+ 'teaser' => array(
+ 'label' => 'above',
+ 'settings' => array(),
+ 'type' => 'hidden',
+ 'weight' => 0,
+ ),
+ ),
+ 'entity_type' => 'node',
+ 'field_name' => 'field_afp_story_images',
+ 'label' => 'Images',
+ 'required' => 0,
+ 'settings' => array(
+ 'allow_override' => 0,
+ 'entity_translation_sync' => FALSE,
+ 'referencable_types' => array(
+ 'gallery' => 0,
+ 'iframe' => 0,
+ 'image' => 'image',
+ 'video' => 0,
+ ),
+ 'semantic_field_format' => '',
+ 'user_register_form' => FALSE,
+ ),
+ 'widget' => array(
+ 'active' => 1,
+ 'module' => 'atom_reference',
+ 'settings' => array(
+ 'context' => 'sdl_editor_representation',
+ ),
+ 'type' => 'atom_reference_textfield',
+ 'weight' => 8,
+ ),
+ );
+
+ // Exported field_instance: 'node-afp_story-field_afp_story_item_id'.
+ $field_instances['node-afp_story-field_afp_story_item_id'] = array(
+ 'bundle' => 'afp_story',
+ 'default_value' => NULL,
+ 'deleted' => 0,
+ 'description' => 'XML: NewsItemId',
+ 'display' => array(
+ 'default' => array(
+ 'label' => 'above',
+ 'module' => 'text',
+ 'settings' => array(
+ 'semantic_field_format' => 0,
+ ),
+ 'type' => 'text_default',
+ 'weight' => 11,
+ ),
+ 'teaser' => array(
+ 'label' => 'above',
+ 'settings' => array(),
+ 'type' => 'hidden',
+ 'weight' => 0,
+ ),
+ ),
+ 'entity_type' => 'node',
+ 'field_name' => 'field_afp_story_item_id',
+ 'label' => 'Item ID',
+ 'required' => 0,
+ 'settings' => array(
+ 'context' => 'title',
+ 'context_default' => 'sdl_editor_representation',
+ 'dnd_enabled' => 0,
+ 'entity_translation_sync' => FALSE,
+ 'mee_enabled' => 0,
+ 'semantic_field_format' => '',
+ 'text_processing' => 0,
+ 'user_register_form' => FALSE,
+ ),
+ 'widget' => array(
+ 'active' => 1,
+ 'module' => 'text',
+ 'settings' => array(
+ 'size' => 60,
+ ),
+ 'type' => 'text_textfield',
+ 'weight' => 12,
+ ),
+ );
+
+ // Exported field_instance: 'node-afp_story-field_afp_story_place'.
+ $field_instances['node-afp_story-field_afp_story_place'] = array(
+ 'bundle' => 'afp_story',
+ 'default_value' => NULL,
+ 'deleted' => 0,
+ 'description' => 'XML: NewsLines:DateLine',
+ 'display' => array(
+ 'default' => array(
+ 'label' => 'above',
+ 'module' => 'text',
+ 'settings' => array(
+ 'semantic_field_format' => 0,
+ ),
+ 'type' => 'text_default',
+ 'weight' => 3,
+ ),
+ 'teaser' => array(
+ 'label' => 'above',
+ 'settings' => array(),
+ 'type' => 'hidden',
+ 'weight' => 0,
+ ),
+ ),
+ 'entity_type' => 'node',
+ 'field_name' => 'field_afp_story_place',
+ 'label' => 'Place',
+ 'required' => 0,
+ 'settings' => array(
+ 'context' => 'title',
+ 'context_default' => 'sdl_editor_representation',
+ 'dnd_enabled' => 0,
+ 'entity_translation_sync' => FALSE,
+ 'mee_enabled' => 0,
+ 'semantic_field_format' => '',
+ 'text_processing' => 0,
+ 'user_register_form' => FALSE,
+ ),
+ 'widget' => array(
+ 'active' => 1,
+ 'module' => 'text',
+ 'settings' => array(
+ 'size' => 60,
+ ),
+ 'type' => 'text_textfield',
+ 'weight' => 3,
+ ),
+ );
+
+ // Exported field_instance: 'node-afp_story-field_afp_story_revision_id'.
+ $field_instances['node-afp_story-field_afp_story_revision_id'] = array(
+ 'bundle' => 'afp_story',
+ 'default_value' => NULL,
+ 'deleted' => 0,
+ 'description' => 'XML: RevisionId',
+ 'display' => array(
+ 'default' => array(
+ 'label' => 'above',
+ 'module' => 'number',
+ 'settings' => array(
+ 'decimal_separator' => '.',
+ 'prefix_suffix' => TRUE,
+ 'scale' => 0,
+ 'semantic_field_format' => 0,
+ 'thousand_separator' => '',
+ ),
+ 'type' => 'number_integer',
+ 'weight' => 8,
+ ),
+ 'teaser' => array(
+ 'label' => 'above',
+ 'settings' => array(),
+ 'type' => 'hidden',
+ 'weight' => 0,
+ ),
+ ),
+ 'entity_type' => 'node',
+ 'field_name' => 'field_afp_story_revision_id',
+ 'label' => 'Revision ID',
+ 'required' => 0,
+ 'settings' => array(
+ 'entity_translation_sync' => FALSE,
+ 'max' => '',
+ 'min' => '',
+ 'prefix' => '',
+ 'semantic_field_format' => '',
+ 'suffix' => '',
+ 'user_register_form' => FALSE,
+ ),
+ 'widget' => array(
+ 'active' => 0,
+ 'module' => 'number',
+ 'settings' => array(),
+ 'type' => 'number',
+ 'weight' => 13,
+ ),
+ );
+
+ // Exported field_instance: 'node-afp_story-field_afp_story_unpublish_date'.
+ $field_instances['node-afp_story-field_afp_story_unpublish_date'] = array(
+ 'bundle' => 'afp_story',
+ 'deleted' => 0,
+ 'description' => 'Using scheduler: creation date +7 days',
+ 'display' => array(
+ 'default' => array(
+ 'label' => 'above',
+ 'module' => 'date',
+ 'settings' => array(
+ 'format_type' => 'long',
+ 'fromto' => 'both',
+ 'multiple_from' => '',
+ 'multiple_number' => '',
+ 'multiple_to' => '',
+ 'semantic_field_format' => 0,
+ 'show_remaining_days' => FALSE,
+ ),
+ 'type' => 'date_default',
+ 'weight' => 10,
+ ),
+ 'teaser' => array(
+ 'label' => 'above',
+ 'settings' => array(),
+ 'type' => 'hidden',
+ 'weight' => 0,
+ ),
+ ),
+ 'entity_type' => 'node',
+ 'field_name' => 'field_afp_story_unpublish_date',
+ 'label' => 'Unpublish date',
+ 'required' => 0,
+ 'settings' => array(
+ 'default_value' => 'now',
+ 'default_value2' => 'same',
+ 'default_value_code' => '',
+ 'default_value_code2' => '',
+ 'entity_translation_sync' => FALSE,
+ 'semantic_field_format' => '',
+ 'user_register_form' => FALSE,
+ ),
+ 'widget' => array(
+ 'active' => 1,
+ 'module' => 'date',
+ 'settings' => array(
+ 'increment' => 15,
+ 'input_format' => 'd/m/Y - H:i:s',
+ 'input_format_custom' => '',
+ 'label_position' => 'above',
+ 'no_fieldset' => 0,
+ 'text_parts' => array(),
+ 'year_range' => '-3:+3',
+ ),
+ 'type' => 'date_popup',
+ 'weight' => 11,
+ ),
+ );
+
+ // Exported field_instance: 'node-afp_story-field_afp_story_update_date'.
+ $field_instances['node-afp_story-field_afp_story_update_date'] = array(
+ 'bundle' => 'afp_story',
+ 'deleted' => 0,
+ 'description' => 'XML: ThisRevisionCreated',
+ 'display' => array(
+ 'default' => array(
+ 'label' => 'above',
+ 'module' => 'date',
+ 'settings' => array(
+ 'format_type' => 'long',
+ 'fromto' => 'both',
+ 'multiple_from' => '',
+ 'multiple_number' => '',
+ 'multiple_to' => '',
+ 'semantic_field_format' => 0,
+ 'show_remaining_days' => FALSE,
+ ),
+ 'type' => 'date_default',
+ 'weight' => 7,
+ ),
+ 'teaser' => array(
+ 'label' => 'above',
+ 'settings' => array(),
+ 'type' => 'hidden',
+ 'weight' => 0,
+ ),
+ ),
+ 'entity_type' => 'node',
+ 'field_name' => 'field_afp_story_update_date',
+ 'label' => 'Update date',
+ 'required' => 0,
+ 'settings' => array(
+ 'default_value' => 'now',
+ 'default_value2' => 'same',
+ 'default_value_code' => '',
+ 'default_value_code2' => '',
+ 'entity_translation_sync' => FALSE,
+ 'semantic_field_format' => '',
+ 'user_register_form' => FALSE,
+ ),
+ 'widget' => array(
+ 'active' => 1,
+ 'module' => 'date',
+ 'settings' => array(
+ 'increment' => 15,
+ 'input_format' => 'd/m/Y - H:i:s',
+ 'input_format_custom' => '',
+ 'label_position' => 'above',
+ 'no_fieldset' => 0,
+ 'text_parts' => array(),
+ 'year_range' => '-3:+3',
+ ),
+ 'type' => 'date_popup',
+ 'weight' => 10,
+ ),
+ );
+
+ // Translatables
+ // Included for use with string extractors like potx.
+ t('Author');
+ t('Body');
+ t('Categories');
+ t('Copyright');
+ t('Creation date');
+ t('Images');
+ t('Item ID');
+ t('Place');
+ t('Revision ID');
+ t('Unpublish date');
+ t('Update date');
+ t('Using scheduler: creation date +7 days');
+ t('XML: ContentItem in HighDef');
+ t('XML: DataContent');
+ t('XML: FirstCreated');
+ t('XML: NewsItemId');
+ t('XML: NewsLines:ByLine');
+ t('XML: NewsLines:CopyrightLine');
+ t('XML: NewsLines:DateLine');
+ t('XML: RevisionId');
+ t('XML: ThisRevisionCreated');
+
+ return $field_instances;
+}
diff --git a/modules/custom/arte_afp/modules/arte_afp_instances/arte_afp_instances.info b/modules/custom/arte_afp/modules/arte_afp_instances/arte_afp_instances.info
new file mode 100644
index 0000000..0d8841b
--- /dev/null
+++ b/modules/custom/arte_afp/modules/arte_afp_instances/arte_afp_instances.info
@@ -0,0 +1,26 @@
+name = ARTE AFP Instances
+description = Field instances for ARTE AFP
+core = 7.x
+package = ARTE Foundation
+version = 7.x-1.0
+dependencies[] = arte_afp
+dependencies[] = atom_reference
+dependencies[] = date
+dependencies[] = entityreference
+dependencies[] = features
+dependencies[] = number
+dependencies[] = text
+features[features_api][] = api:2
+features[field_instance][] = node-afp_feed-body
+features[field_instance][] = node-afp_story-body
+features[field_instance][] = node-afp_story-field_afp_story_author
+features[field_instance][] = node-afp_story-field_afp_story_categories
+features[field_instance][] = node-afp_story-field_afp_story_copyright
+features[field_instance][] = node-afp_story-field_afp_story_creation_date
+features[field_instance][] = node-afp_story-field_afp_story_images
+features[field_instance][] = node-afp_story-field_afp_story_item_id
+features[field_instance][] = node-afp_story-field_afp_story_place
+features[field_instance][] = node-afp_story-field_afp_story_revision_id
+features[field_instance][] = node-afp_story-field_afp_story_unpublish_date
+features[field_instance][] = node-afp_story-field_afp_story_update_date
+project path = profiles/foundation/modules/custom/arte_afp/modules
diff --git a/modules/custom/arte_afp/modules/arte_afp_instances/arte_afp_instances.module b/modules/custom/arte_afp/modules/arte_afp_instances/arte_afp_instances.module
new file mode 100644
index 0000000..44788b0
--- /dev/null
+++ b/modules/custom/arte_afp/modules/arte_afp_instances/arte_afp_instances.module
@@ -0,0 +1,5 @@
+<?php
+/**
+ * @file
+ * Drupal needs this blank file.
+ */
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/README.md b/modules/custom/arte_afp/modules/arte_feeds_afp/README.md
new file mode 100644
index 0000000..5cd9124
--- /dev/null
+++ b/modules/custom/arte_afp/modules/arte_feeds_afp/README.md
@@ -0,0 +1,16 @@
+Arte AFP Feeds Plugins
+======================
+
+This module adds an AFP feed to fetch content from the AFP FTP server.
+On Future, the whole import was very long - nearly 30 minutes - because there are many files to fetch.
+
+Beware, as only one connection is allowed at a time to the AFP FTP server.
+The import may fail if an other connection is opened.
+
+First, all images are fetched and transferred on the server.
+Then, XML are parsed and nodes created, images are added as scald nodes.
+
+For testing purposes, a function was created to fetch test data located in the repertory "datas"
+of this module. These files are fetched with the drush command "drush afp" on site install.
+Then, behat can test the presence of imported test nodes and their content to make sure the
+parser is working properly.
\ No newline at end of file
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/arte_feeds_afp.cleanup.inc b/modules/custom/arte_afp/modules/arte_feeds_afp/arte_feeds_afp.cleanup.inc
new file mode 100644
index 0000000..e64ca52
--- /dev/null
+++ b/modules/custom/arte_afp/modules/arte_feeds_afp/arte_feeds_afp.cleanup.inc
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * @param int $timestamp
+ */
+function _arte_feeds_afp_delete_old_stories($timestamp) {
+ $old_stories_titles = _arte_feeds_afp_get_old_stories_titles($timestamp);
+ node_delete_multiple(array_keys($old_stories_titles));
+}
+
+/**
+ * @param int $timestamp
+ *
+ * @return string[]
+ * Format: $[$nid] = $title
+ */
+function _arte_feeds_afp_get_old_stories_titles($timestamp) {
+
+ $afp_uid = _arte_feeds_afp_get_afp_uid();
+ if (!$afp_uid) {
+ // @todo Show warning.
+ return array();
+ }
+
+ $q = db_select('node');
+ $q->fields('node', array('nid', 'title'));
+ $q->condition('node.type', 'afp_story');
+ $q->condition('node.uid', $afp_uid);
+ $q->condition('node.created', $timestamp, '<=');
+
+ return $q->execute()->fetchAllKeyed();
+}
+
+/**
+ * Gets all atoms that were created before the given timestamp.
+ *
+ * @param int $timestamp
+ *
+ * @return string[]
+ * Format: $[$sid] = $title
+ */
+function _arte_feeds_afp_get_old_atoms_titles($timestamp) {
+
+ $afp_uid = _arte_feeds_afp_get_afp_uid();
+ if (!$afp_uid) {
+ // @todo Show warning.
+ return array();
+ }
+
+ $q = db_select('scald_atoms', 'atom');
+ $q->fields('atom', array('sid', 'title'));
+ $q->condition('atom.publisher', $afp_uid);
+ $q->condition('atom.created', $timestamp, '<=');
+
+ return $q->execute()->fetchAllKeyed();
+}
+
+/**
+ * Gets the uid of the AFP user.
+ *
+ * @return int|null
+ */
+function _arte_feeds_afp_get_afp_uid() {
+ $q = db_select('users');
+ $q->fields('users', array('uid'));
+ $q->condition('users.name', 'AFP');
+ $afp_uid = $q->execute()->fetchField();
+ if (!$afp_uid) {
+ return NULL;
+ }
+ return $afp_uid;
+}
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/arte_feeds_afp.drush.inc b/modules/custom/arte_afp/modules/arte_feeds_afp/arte_feeds_afp.drush.inc
new file mode 100644
index 0000000..dad61fb
--- /dev/null
+++ b/modules/custom/arte_afp/modules/arte_feeds_afp/arte_feeds_afp.drush.inc
@@ -0,0 +1,139 @@
+<?php
+
+/**
+ * Implements hook_drush_command().
+ */
+function arte_feeds_afp_drush_command() {
+ $items = array();
+
+ $items['afp-test'] = array(
+ 'description' => 'Test the AFP import by parsing default files in the module.',
+ 'aliases' => array('afp'),
+ );
+
+ $items['afp-import'] = array(
+ 'description' => 'Import AFP feed data.',
+ 'aliases' => array('afp-i'),
+ );
+
+ $time_arg_desc = 'The time limit.'
+ . ' Can be any string understood by PHP strtotime().'
+ . ' Defaults to "7 days ago".';
+
+ $items['afp-list-old-stories'] = array(
+ 'description' => 'List AFP stories created before a given date/time.',
+ 'aliases' => array('afp-los'),
+ 'arguments' => array(
+ 'time' => $time_arg_desc,
+ ),
+ );
+
+ $items['afp-list-old-atoms'] = array(
+ 'description' => 'List AFP atoms created before a given date/time.',
+ 'aliases' => array('afp-loa'),
+ 'arguments' => array(
+ 'time' => $time_arg_desc,
+ ),
+ );
+
+ $items['afp-cleanup'] = array(
+ 'description' => 'Delete AFP stories and atoms created before a given date/time.',
+ 'aliases' => array('afp-c'),
+ 'arguments' => array(
+ 'time' => $time_arg_desc,
+ ),
+ );
+
+ return $items;
+}
+
+/**
+ * @param string $time_str
+ */
+function drush_arte_feeds_afp_afp_list_old_stories($time_str = '7 days ago') {
+ require_once __DIR__ . '/arte_feeds_afp.cleanup.inc';
+ $timestamp = strtotime($time_str);
+ $news_titles = _arte_feeds_afp_get_old_stories_titles($timestamp);
+ drush_print_r($news_titles);
+ drush_print('Above you see the AFP stories imported before ' . date('Y-m-d H:i:s', $timestamp));
+}
+
+/**
+ * @param string $time_str
+ */
+function drush_arte_feeds_afp_afp_list_old_atoms($time_str = '7 days ago') {
+ require_once __DIR__ . '/arte_feeds_afp.cleanup.inc';
+ $timestamp = strtotime($time_str);
+ $news_titles = _arte_feeds_afp_get_old_atoms_titles($timestamp);
+ drush_print_r($news_titles);
+ drush_print('Above you see the AFP atoms imported before ' . date('Y-m-d H:i:s', $timestamp));
+}
+
+/**
+ * @param string $time_str
+ *
+ * @throws \Exception
+ */
+function drush_arte_feeds_afp_afp_cleanup($time_str = '7 days ago') {
+
+ require_once __DIR__ . '/arte_feeds_afp.cleanup.inc';
+ require_once __DIR__ . '/arte_feeds_afp.cleanup.inc';
+
+ $timestamp = strtotime($time_str);
+ drush_log('Going to delete AFP stories and atoms imported before ' . date('Y-m-d H:i:s', $timestamp), 'ok');
+
+ $news_titles = _arte_feeds_afp_get_old_stories_titles($timestamp);
+ node_delete_multiple(array_keys($news_titles));
+ drush_log('Deleted ' . count($news_titles) . ' stories', 'ok');
+
+ $atom_titles = _arte_feeds_afp_get_old_atoms_titles($timestamp);
+ scald_atom_delete_multiple(array_keys($atom_titles));
+ drush_log('Deleted ' . count($atom_titles) . ' atoms', 'ok');
+}
+
+/**
+ * Drush afp-test callback.
+ */
+function drush_arte_feeds_afp_afp_test() {
+ arte_feeds_afp_import_test_data();
+}
+
+/**
+ * Drush afp-import callback.
+ *
+ * Import the AFP Feed from a drush command.
+ */
+function drush_arte_feeds_afp_afp_import() {
+ global $user;
+
+ drush_print(dt('Importing AFP feed...'));
+
+ // Log in the admin user.
+ $user = user_load(1);
+ $destination = 'public://feeds';
+
+ // Load the AFP feed node.
+ $nid = db_query("SELECT nid FROM {node} WHERE type = :type LIMIT 1", array(':type' => "afp_feed"))->fetchField();
+ if ($nid === FALSE) {
+ drush_print(dt('The AFP import node was not found. Creating one...'), 'warning');
+ $node = _arte_feeds_afp_create_import_node();
+ $nid = $node->nid;
+ }
+
+ // Make sure the directory exists.
+ file_prepare_directory($destination, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
+
+ // Create the batch.
+ $batch = array(
+ 'title' => t('AFP Feed import'),
+ 'operations' => array(
+ array('feeds_batch', array('import', 'afp_feeds', $nid)),
+ ),
+ 'progress_message' => '',
+ 'progressive' => FALSE
+ );
+ batch_set($batch);
+
+ // Throw batch.
+ drush_backend_batch_process();
+}
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/arte_feeds_afp.info b/modules/custom/arte_afp/modules/arte_feeds_afp/arte_feeds_afp.info
new file mode 100644
index 0000000..b02d55f
--- /dev/null
+++ b/modules/custom/arte_afp/modules/arte_feeds_afp/arte_feeds_afp.info
@@ -0,0 +1,14 @@
+name = ARTE AFP Feeds
+Description = This module extends Feeds to allow XML files importation from AFP
+package = "ARTE Foundation"
+core = 7.x
+version = 7.x-1.0
+
+dependencies[] = feeds
+dependencies[] = feeds_ftp_fetcher
+dependencies[] = feeds_fetcher_directory
+dependencies[] = xautoload
+
+files[] = arte_feeds_afp.install
+files[] = plugins/FeedsAFPParser.inc
+files[] = plugins/FeedsAFPFetcher.inc
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/arte_feeds_afp.install b/modules/custom/arte_afp/modules/arte_feeds_afp/arte_feeds_afp.install
new file mode 100644
index 0000000..5ae7995
--- /dev/null
+++ b/modules/custom/arte_afp/modules/arte_feeds_afp/arte_feeds_afp.install
@@ -0,0 +1,11 @@
+<?php
+
+/**
+ * Implements hook_enable().
+ *
+ * Adds our AFP feed node to be able to import elements.
+ */
+function arte_feeds_afp_enable() {
+ // Create the import node if necessary.
+ _arte_feeds_afp_create_import_node();
+}
\ No newline at end of file
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/arte_feeds_afp.module b/modules/custom/arte_afp/modules/arte_feeds_afp/arte_feeds_afp.module
new file mode 100644
index 0000000..f2ec9b3
--- /dev/null
+++ b/modules/custom/arte_afp/modules/arte_feeds_afp/arte_feeds_afp.module
@@ -0,0 +1,331 @@
+<?php
+
+/**
+ * Implements hook_feeds_plugins().
+ *
+ * Create a custom Feeds parser.
+ * Create a custom Feeds fetcher.
+ */
+function arte_feeds_afp_feeds_plugins() {
+ $info = array();
+
+ $info['FeedsAFPParser'] = array(
+ 'name' => 'AFP parser',
+ 'description' => 'Parses AFP xml files.',
+ 'handler' => array(
+ 'parent' => 'FeedsParser',
+ 'class' => 'FeedsAFPParser',
+ 'file' => 'FeedsAFPParser.inc',
+ 'path' => drupal_get_path('module', 'arte_feeds_afp') . '/plugins/',
+ ),
+ );
+
+ $info['FeedsAFPFetcher'] = array(
+ 'name' => 'AFP Fetcher',
+ 'description' => 'Download content from the AFP server',
+ 'help' => 'Fetches AFP files',
+ 'handler' => array(
+ 'parent' => 'FeedsFetcher',
+ 'class' => 'FeedsAFPFetcher',
+ 'file' => 'FeedsAFPFetcher.inc',
+ 'path' => drupal_get_path('module', 'arte_feeds_afp') . '/plugins/',
+ ),
+ );
+
+ return $info;
+}
+
+/**
+ * Implements hook_feeds_processor_targets_alter().
+ */
+function arte_feeds_afp_feeds_processor_targets_alter(&$targets, $type, $bundle) {
+ if ($type == 'node' && $bundle == 'afp_story') {
+ $targets['field_afp_story_categories:tid'] = array(
+ 'name' => t('AFP Feed: categories'),
+ 'description' => t('Extract a list of categories from AFP feed items.'),
+ 'callback' => 'arte_feeds_afp_set_categories',
+ 'real_target' => 'field_afp_story_categories',
+ );
+ }
+}
+
+/**
+ * Callback for mapping AFP Feed category terms.
+ * Uses the "afp_tags" vocabulary.
+ */
+function arte_feeds_afp_set_categories(FeedsSource $source, $entity, $target, array $value) {
+ $language = entity_language('node', $entity);
+ $vocabulary = taxonomy_vocabulary_machine_name_load('afp_tags');
+
+ $query = new EntityFieldQuery();
+ $query->entityCondition('entity_type', 'taxonomy_term')
+ ->entityCondition('bundle', 'afp_tags')
+ ->entityCondition('language', $language)
+ ->range(0, 1);
+
+ // Get existing data.
+ $field = field_get_items('node', $entity, $target);
+ if (!$field) { $field = array('und' => array()); }
+
+ foreach ($value as $term) {
+ $term = trim($term);
+ $name_query = clone $query;
+
+ $tid = FALSE;
+ if (strlen($term) > 0) {
+ // Try to get one existing term.
+ if ($tids = $name_query->propertyCondition('name', $term)->execute()) {
+ foreach ($tids['taxonomy_term'] as $possible_term) {
+ $tid = $possible_term->tid;
+ break;
+ }
+ }
+
+ // Create one if none found.
+ if (!$tid) {
+ $term = (object) array(
+ 'name' => drupal_substr($term, 0, 255),
+ 'vid' => $vocabulary->vid,
+ 'vocabulary_machine_name' => $vocabulary->machine_name,
+ 'language' => $language
+ );
+ taxonomy_term_save($term);
+ $tid = $term->tid;
+ }
+ }
+
+ if ($tid) {
+ $field['und'][] = array('target_id' => $tid);
+ }
+ }
+
+ $entity->field_afp_story_categories = $field;
+}
+
+/**
+ * Implements hook_feeds_presave().
+ */
+function arte_feeds_afp_feeds_presave(FeedsSource $source, $entity, $item) {
+ if ($entity->type == 'afp_story') {
+ // Set author to the one created on site install.
+ $author = variable_get('arte_feeds_afp_user', 0);
+ $entity->uid = $author;
+
+ // If not set, set unpublishing date.
+ // Thus, we allow editors to change the unpublishing date for some nodes.
+ $unpublish = field_get_items('node', $entity, 'field_afp_story_unpublish_date');
+ if (empty($unpublish)) {
+ // Set unpublishing date = creation date + 7 days.
+ $date = new \DateTime('now');
+ $created = field_get_items('node', $entity, 'field_afp_story_creation_date');
+ if (is_array($created) && count($created) == 1) {
+ $date = \DateTime::createFromFormat('Y-m-d G:i:s', $created[0]['value'], new \DateTimeZone($created[0]['timezone']));
+ if (!$date) {
+ $date = new \DateTime('now');
+ }
+ }
+
+ $date->modify('+7 days');
+ $date->setTime(0, 0, 0);
+
+ $entity->field_afp_story_unpublish_date[LANGUAGE_NONE][0]['value'] = $date->format('Y-m-d G:i:s');
+ $entity->field_afp_story_unpublish_date[LANGUAGE_NONE][0]['timezone'] = 'Europe\Paris';
+ $entity->field_afp_story_unpublish_date[LANGUAGE_NONE][0]['timezone_db'] = 'UTC';
+ $entity->field_afp_story_unpublish_date[LANGUAGE_NONE][0]['date_type'] = 'datetime';
+ }
+
+ // Add 'field_image_copyright' and 'field_image_legend' data to the atom.
+ $atoms = field_get_items('node', $entity, 'field_afp_story_images');
+ if (is_array($atoms) && count($atoms) > 0) {
+ foreach ($atoms as $i => $atom) {
+ $atom = scald_atom_load($atom['sid']);
+ if ($atom) {
+ $atom->publisher = $author;
+ $atom->title = substr($item['HeadLinephoto' . $i], 0, 255);
+ $atom->field_image_copyright[$atom->language][0]['value'] = $item['ByLinephoto' . $i] . ' - ' . $item['CreditLinephoto' . $i];
+ $atom->field_image_legend[$atom->language][0]['value'] = $item['DataContentphoto' . $i];
+
+ scald_atom_save($atom);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Implements hook_entity_delete().
+ */
+function arte_feeds_afp_entity_delete($entity, $type) {
+ // If we try to delete an AFP news with a scald entity linked to it, we delete the scald atom too.
+ if ($type == 'node' && $entity->type == 'afp_story') {
+ $images = field_get_items($type, $entity, 'field_afp_story_images');
+ if (is_array($images) && count($images) > 0) {
+ $sids = array();
+ foreach ($images as $key => $value) {
+ $sids[] = $value['sid'];
+ }
+ scald_atom_delete_multiple($sids);
+ }
+ }
+}
+
+/**
+ * Implements hook_entity_presave().
+ */
+function arte_feeds_afp_entity_presave($entity, $type) {
+ if ($type == 'node' && $entity->type == 'afp_story') {
+ // Force changed date to the one given by AFP feeds, only if there is one provided.
+ $date = FALSE;
+ if (!empty($entity->field_afp_story_update_date[LANGUAGE_NONE][0]['value'])) {
+ $date = strtotime($entity->field_afp_story_update_date[LANGUAGE_NONE][0]['value']);
+ }
+ // If update date could not be retrieved we try to catch the creation date.
+ if (!$date && !empty($entity->field_afp_story_creation_date[LANGUAGE_NONE][0]['value'])) {
+ $date = strtotime($entity->field_afp_story_creation_date[LANGUAGE_NONE][0]['value']);
+ }
+
+ // If a date could be retrieve and properly converted to timestamp
+ // we force it as last update date, otherwise we let drupal set it.
+ if ($date) {
+ $entity->changed = $date;
+ }
+
+ // Disable Disqus comment by defaults.
+ if ($entity->is_new) {
+ $entity->disqus_status = FALSE;
+ }
+ }
+}
+
+/**
+ * Creates a testing feeds importer and throw import.
+ */
+function arte_feeds_afp_import_test_data() {
+ global $user;
+ $user = user_load(1);
+
+ // Load includes and clear cache.
+ module_load_include('inc', 'arte_afp', 'arte_afp.feeds_importer_default');
+
+ // The testing fetcher configuration.
+ $fetcher = array(
+ 'plugin_key' => 'feeds_fetcher_directory_fetcher',
+ 'config' => array(
+ 'recursive' => 1,
+ 'directory' => drupal_get_path('module', 'arte_feeds_afp') . '/datas',
+ 'filemask' => '/\\.(xml|jpg)$/',
+ 'updated_files' => 1,
+ ),
+ );
+
+ // Get the feed source and clone importer.
+ $default = arte_afp_feeds_importer_default();
+ $default = array_shift($default);
+ $default->id = 'arte_feeds_afp_testing_importer';
+ $default->config['fetcher'] = $fetcher;
+
+ // Create the test importer.
+ $importer = feeds_importer($default->id);
+ $importer->setConfig($default->config);
+ foreach (array('fetcher', 'parser', 'processor') as $type) {
+ $importer->setPlugin($default->config[$type]['plugin_key']);
+ $importer->$type->setConfig($default->config[$type]['config']);
+ }
+
+ // Save the importer.
+ $importer->save();
+
+ // Import data.
+ $myFeed = feeds_source($importer->id);
+ $config = array($fetcher['plugin_key'] => array('source' => $fetcher['config']['directory']));
+ $myFeed->addConfig($config);
+ while (FEEDS_BATCH_COMPLETE != $myFeed->import()) {}
+
+ // Remove importer.
+ $importer->delete();
+ feeds_cache_clear();
+}
+
+/**
+ * Adds a afp_feed node if no one exists yet.
+ */
+function _arte_feeds_afp_create_import_node() {
+ $nodes = node_load_multiple(array(), array(
+ 'title' => 'AFP Node Feed',
+ 'type' => 'afp_feed'
+ ));
+
+ if (!is_array($nodes) or empty($nodes)) {
+ $user = _arte_feeds_afp_create_import_user();
+
+ $node = new stdClass();
+ $node->title = "AFP Node Feed";
+ $node->type = "afp_feed";
+ $node->language = LANGUAGE_NONE;
+ $node->uid = $user->uid;
+ $node->status = 1;
+ $node->promote = 0;
+ $node->comment = 0;
+
+ // Sets some defaults.
+ // Invokes hook_prepare() and hook_node_prepare().
+ node_object_prepare($node);
+
+ // Prepare node for saving.
+ $node = node_submit($node);
+
+ // Save node.
+ node_save($node);
+ } else {
+ $node = array_shift($nodes);
+ }
+
+ return $node;
+}
+
+/**
+ * Create the import user if necessary.
+ */
+function _arte_feeds_afp_create_import_user() {
+ $user = null;
+
+ // Try to load defined user.
+ $uid = variable_get('arte_feeds_afp_user', NULL);
+ if (!is_null($uid)) {
+ $user = user_load($uid);
+ }
+
+ // Create AFP user to be used for the content creation.
+ if (is_null($uid) || !isset($user->uid)) {
+ $user = user_load_by_name('AFP');
+ if ($user === FALSE) {
+ $password = user_password(8);
+
+ // Set up the user fields
+ $fields = array(
+ 'name' => 'AFP',
+ 'pass' => $password,
+ 'status' => 1,
+ 'roles' => array(
+ DRUPAL_AUTHENTICATED_RID => 'authenticated user',
+ ),
+ );
+ $account = user_save('', $fields);
+ variable_set('arte_feeds_afp_user', $account->uid);
+ }
+ }
+
+ return $user;
+}
+
+/**
+ * Implements hook_foundation_xiti_tracking_xtpage_alter().
+ *
+ * All AFP content should have a specific xiti tagging: AFP::NODE_TITLE
+ */
+function arte_feeds_afp_foundation_xiti_tracking_xtpage_alter(&$pageName, $variables) {
+ $node = menu_get_object('node');
+ if (isset($node->type) && $node->type === 'afp_story') {
+ $pageName = 'AFP::' . $node->title;
+ }
+}
\ No newline at end of file
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/1501d2f14c069680518edb03ac4e5342d284ada2.jpg b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/1501d2f14c069680518edb03ac4e5342d284ada2.jpg
new file mode 100644
index 0000000..77dc7bb
Binary files /dev/null and b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/1501d2f14c069680518edb03ac4e5342d284ada2.jpg differ
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/1b1ed0b8f4f644faf026f897ec56d6f40e8cd5b2.jpg b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/1b1ed0b8f4f644faf026f897ec56d6f40e8cd5b2.jpg
new file mode 100644
index 0000000..cef9574
Binary files /dev/null and b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/1b1ed0b8f4f644faf026f897ec56d6f40e8cd5b2.jpg differ
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/231bed086a163efd2e341d2884a148dc02a90d23.jpg b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/231bed086a163efd2e341d2884a148dc02a90d23.jpg
new file mode 100644
index 0000000..a7be08b
Binary files /dev/null and b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/231bed086a163efd2e341d2884a148dc02a90d23.jpg differ
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/39f947236afda4fe5b7ff80257bcab975c2493ed.jpg b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/39f947236afda4fe5b7ff80257bcab975c2493ed.jpg
new file mode 100644
index 0000000..4f53d84
Binary files /dev/null and b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/39f947236afda4fe5b7ff80257bcab975c2493ed.jpg differ
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/5411b70307caeec0a940451dcc3f53abd664e2e1.jpg b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/5411b70307caeec0a940451dcc3f53abd664e2e1.jpg
new file mode 100644
index 0000000..27e0bf9
Binary files /dev/null and b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/5411b70307caeec0a940451dcc3f53abd664e2e1.jpg differ
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/5a7b41e96fda2fb10b0c1a0c8137f67dc98b6bf8.jpg b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/5a7b41e96fda2fb10b0c1a0c8137f67dc98b6bf8.jpg
new file mode 100644
index 0000000..a868d7b
Binary files /dev/null and b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/5a7b41e96fda2fb10b0c1a0c8137f67dc98b6bf8.jpg differ
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/7212d3214c744dbea46ff7cf1c6562aa2df6fbd8.jpg b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/7212d3214c744dbea46ff7cf1c6562aa2df6fbd8.jpg
new file mode 100644
index 0000000..595bffc
Binary files /dev/null and b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/7212d3214c744dbea46ff7cf1c6562aa2df6fbd8.jpg differ
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/72b9c32db509b6aea6ce1a6b39ba85a0dac5dba2.jpg b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/72b9c32db509b6aea6ce1a6b39ba85a0dac5dba2.jpg
new file mode 100644
index 0000000..5e9b733
Binary files /dev/null and b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/72b9c32db509b6aea6ce1a6b39ba85a0dac5dba2.jpg differ
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/774ae7f0380bd281b4a1f6925d58034cf571b684.jpg b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/774ae7f0380bd281b4a1f6925d58034cf571b684.jpg
new file mode 100644
index 0000000..222aa68
Binary files /dev/null and b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/774ae7f0380bd281b4a1f6925d58034cf571b684.jpg differ
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/a249dc6c0fa8ba62e4da8272ca85c77d45d645f0.jpg b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/a249dc6c0fa8ba62e4da8272ca85c77d45d645f0.jpg
new file mode 100644
index 0000000..891a6ec
Binary files /dev/null and b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/a249dc6c0fa8ba62e4da8272ca85c77d45d645f0.jpg differ
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/index.xml b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/index.xml
new file mode 100644
index 0000000..ff66300
--- /dev/null
+++ b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/index.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<NewsML Version="1.2">
+ <Catalog Href="http://www.afp.com/dtd/AFPCatalog.xml"/>
+ <NewsEnvelope>
+ <DateAndTime>20130813T085328Z</DateAndTime>
+ </NewsEnvelope>
+ <NewsItem>
+ <Identification>
+ <NewsIdentifier>
+ <ProviderId>afp.com</ProviderId>
+ <DateId>20130813</DateId>
+ <NewsItemId>francais--journal--eco</NewsItemId>
+ <RevisionId PreviousRevision="0" Update="N">1</RevisionId>
+ <PublicIdentifier>urn:newsml:afp.com:20130813:francais--journal--eco:1</PublicIdentifier>
+ </NewsIdentifier>
+ <NameLabel>Finances</NameLabel>
+ </Identification>
+ <NewsManagement>
+ <NewsItemType FormalName="News"/>
+ <FirstCreated>20130813T085328Z</FirstCreated>
+ <ThisRevisionCreated>20130813T085328Z</ThisRevisionCreated>
+ <Status FormalName="Usable"/>
+ </NewsManagement>
+ <NewsComponent>
+ <AdministrativeMetadata>
+ <Provider>
+ <Party FormalName="afp.com"/>
+ </Provider>
+ </AdministrativeMetadata>
+ <DescriptiveMetadata>
+ <Language FormalName="fr"/>
+ </DescriptiveMetadata>
+ <NewsComponent>
+ <NewsLines>
+ <HeadLine>Auto-entrepreneurs: les "Poussins" observent un "premier signal positif"</HeadLine>
+ </NewsLines>
+ <NewsItemRef NewsItem="newsmlmmd.urn.newsml.afp.com.20130813.ace8d470.cd86.4d50.95be.7ec24b732e3d.xml"/>
+ </NewsComponent>
+ <NewsComponent>
+ <NewsLines>
+ <HeadLine>La Chine va devenir le premier importateur mondial de pétrole</HeadLine>
+ </NewsLines>
+ <NewsItemRef NewsItem="newsmlmmd.urn.newsml.afp.com.20130813.92672245.2138.41a4.b9b0.870e85b8971d.xml"/>
+ </NewsComponent>
+ <NewsComponent>
+ <NewsLines>
+ <HeadLine>Blanchiment: les vignobles concernés, appel à la vigilance</HeadLine>
+ </NewsLines>
+ <NewsItemRef NewsItem="newsmlmmd.urn.newsml.afp.com.20130813.22c51cff.6221.4895.8925.a651d63b9fdf.xml"/>
+ </NewsComponent>
+ <NewsComponent>
+ <NewsLines>
+ <HeadLine>Les jeux de construction Meccano rachetés par un groupe canadien</HeadLine>
+ </NewsLines>
+ <NewsItemRef NewsItem="newsmlmmd.urn.newsml.afp.com.20130813.45a3b4db.75ed.4da2.928f.200016929820.xml"/>
+ </NewsComponent>
+ <NewsComponent>
+ <NewsLines>
+ <HeadLine>"Hyperloop" promet un voyage supersonique en capsule</HeadLine>
+ </NewsLines>
+ <NewsItemRef NewsItem="newsmlmmd.urn.newsml.afp.com.20130812.cf986026.114b.452f.9d1e.b5d8f1ddee14.xml"/>
+ </NewsComponent>
+ <NewsComponent>
+ <NewsLines>
+ <HeadLine>Les prix des fruits et légumes flambent cet été</HeadLine>
+ </NewsLines>
+ <NewsItemRef NewsItem="newsmlmmd.urn.newsml.afp.com.20130812.48ddbff8.3547.4e60.aad4.f6afc001c17e.xml"/>
+ </NewsComponent>
+ <NewsComponent>
+ <NewsLines>
+ <HeadLine>Zone euro: sortie de récession en vue</HeadLine>
+ </NewsLines>
+ <NewsItemRef NewsItem="newsmlmmd.urn.newsml.afp.com.20130813.84aae127.2943.4c2c.945b.17c2239fadf2.xml"/>
+ </NewsComponent>
+ <NewsComponent>
+ <NewsLines>
+ <HeadLine>SocGen: Groupama sort du capital, touche 517 millions d'euros</HeadLine>
+ </NewsLines>
+ <NewsItemRef NewsItem="newsmlmmd.urn.newsml.afp.com.20130813.e8b1aac5.459d.4041.ad3b.7de93a3e2bc0.xml"/>
+ </NewsComponent>
+ <NewsComponent>
+ <NewsLines>
+ <HeadLine>La Bourse de Paris débute sur une note optimiste</HeadLine>
+ </NewsLines>
+ <NewsItemRef NewsItem="newsmlmmd.urn.newsml.afp.com.20130813.2fb5d772.391b.48cb.ad42.0971d16be481.xml"/>
+ </NewsComponent>
+ <NewsComponent>
+ <NewsLines>
+ <HeadLine>Wall Street clôture à l'équilibre, dans l'attente d'indicateurs</HeadLine>
+ </NewsLines>
+ <NewsItemRef NewsItem="newsmlmmd.urn.newsml.afp.com.20130812.3904592b.7bad.46c0.9de3.73cfedc51ffc.xml"/>
+ </NewsComponent>
+ </NewsComponent>
+ </NewsItem>
+</NewsML>
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/newsmlmmd.urn.newsml.afp.com.20130813.84aae127.2943.4c2c.945b.17c2239fadf2.xml b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/newsmlmmd.urn.newsml.afp.com.20130813.84aae127.2943.4c2c.945b.17c2239fadf2.xml
new file mode 100644
index 0000000..c7cb330
--- /dev/null
+++ b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/newsmlmmd.urn.newsml.afp.com.20130813.84aae127.2943.4c2c.945b.17c2239fadf2.xml
@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<NewsML Version="1.2">
+ <Catalog Href="http://www.afp.com/dtd/AFPCatalog.xml"/>
+ <NewsEnvelope>
+ <DateAndTime>20130813T084231Z</DateAndTime>
+ </NewsEnvelope>
+ <NewsItem>
+ <Identification>
+ <NewsIdentifier>
+ <ProviderId>afp.com</ProviderId>
+ <DateId>20130813</DateId>
+ <NewsItemId>84aae127-2943-4c2c-945b-17c2239fadf2</NewsItemId>
+ <RevisionId PreviousRevision="0" Update="N">1</RevisionId>
+ <PublicIdentifier>urn:newsml:afp.com:20130813:84aae127-2943-4c2c-945b-17c2239fadf2:1</PublicIdentifier>
+ </NewsIdentifier>
+ <NameLabel>UE-dette-finance-économie-croissance-indicateur</NameLabel>
+ </Identification>
+ <NewsManagement>
+ <NewsItemType FormalName="News"/>
+ <FirstCreated>20130813T084218Z</FirstCreated>
+ <ThisRevisionCreated>20130813T084218Z</ThisRevisionCreated>
+ <Status FormalName="Usable"/>
+ </NewsManagement>
+ <NewsComponent>
+ <NewsLines>
+ <HeadLine>Zone euro: sortie de récession en vue</HeadLine>
+ <ByLine>Par Aurélie MAYEMBO</ByLine>
+ <DateLine>Bruxelles (AFP)</DateLine>
+ <CopyrightLine>© 2013 AFP</CopyrightLine>
+ <SlugLine>UE-dette-finance-économie-croissance-indicateur</SlugLine>
+ </NewsLines>
+ <AdministrativeMetadata>
+ <Provider>
+ <Party FormalName="afp.com" />
+ </Provider>
+ </AdministrativeMetadata>
+ <DescriptiveMetadata>
+ <Language FormalName="fr" />
+ <OfInterestTo FormalName="francais--journal--eco"/>
+ <Location HowPresent="Origin">
+ <Property FormalName="Country" Value="BEL"/>
+ <Property FormalName="City" Value="Bruxelles"/>
+ <Property FormalName="Latitude" Value="50.85045"/>
+ <Property FormalName="Longitude" Value="4.34878"/>
+ </Location>
+ <SubjectCode>
+ <SubjectDetail FormalName="04008004"/>
+ </SubjectCode>
+ <SubjectCode>
+ <SubjectMatter FormalName="11014000"/>
+ </SubjectCode>
+ <SubjectCode>
+ <Subject FormalName="04000000"/>
+ </SubjectCode>
+ <SubjectCode>
+ <SubjectMatter FormalName="11014000"/>
+ </SubjectCode>
+ <SubjectCode>
+ <Subject FormalName="04000000"/>
+ </SubjectCode>
+ <SubjectCode>
+ <SubjectMatter FormalName="04008000"/>
+ </SubjectCode>
+ <SubjectCode>
+ <Subject FormalName="11000000"/>
+ </SubjectCode>
+ <SubjectCode>
+ <SubjectMatter FormalName="04017000"/>
+ </SubjectCode>
+ </DescriptiveMetadata>
+ <NewsComponent>
+ <ContentItem>
+ <MediaType FormalName="Text"/>
+ <Format FormalName="bcNITF2.5"/>
+ <DataContent><media media-type="image" style="leftSide"><media-reference data-location="#photo0" mime-type=""/></media><p>Si les prévisions des analystes se confirment mercredi, la zone euro devrait avoir renoué avec la croissance au deuxième trimestre et tourné le dos à sa plus longue phase de récession, mais la reprise s'annonce molle et la sortie de crise encore lointaine.</p><p>Après six trimestres consécutifs de repli, le produit intérieur brut (PIB) de la zone euro devrait progresser de 0,1% ou de 0,2% au deuxième trimestre, selon les analystes. Eurostat publiera les chiffres officiels mercredi en fin de matinée.</p><p>Bien que minime, cette hausse se traduira, si elle est confirmée, par une sortie de récession pour la zone euro. La croissance devrait ensuite s'accélérer au troisième trimestre.</p><p>Plusieurs indices vont dans ce sens: la récession s'est atténuée d'avril à juin en Italie (-0,2%) et en Espagne (-0,1%), les troisième et quatrième économies de la zone euro. </p><p>En Belgique, la croissance a très légèrement accéléré sur la période, avec une hausse de 0,1% du PIB après un début d'année au point mort (PIB stable). Très tournée vers le commerce avec le reste de l'UE, l'économie belge est souvent considérée comme un bon témoin de la conjoncture européenne.</p><p>Amélioration au printemps</p><p>En outre, récemment, "les indicateurs de confiance ont montré une amélioration, partant de niveaux bas, et confirmé le scénario d'une stabilisation de l'économie", a affirmé début août le patron de la Banque centrale européenne (BCE), Mario Draghi.</p><p>De la production industrielle aux ventes de détail, en passant par les indicateurs de confiance, toutes ces données ont montré des signes d'amélioration au printemps. </p><p>Enfin, pour la première fois en deux ans, le nombre de chômeurs a reculé en juin dans la zone euro même si le taux de chômage s'inscrit toujours à des niveaux record, rappelle Howard Archer, économiste chez IHS Global Insight.</p><p>Malgré tout, "il est encore trop tôt pour évoquer une fin de la crise de la dette en zone euro", avertissent Michala Marcussen et Brian Hilliard, analystes pour Société Générale CIB.</p><p>D'autant plus que le rebond de l'activité au printemps cache d'importantes disparités et dépend largement du moteur allemand. </p><p>Outre-Rhin, l'économie devrait progresser de 0,6% au deuxième trimestre, soutenu par la construction, après un premier trimestre marqué par des conditions météorologiques défavorables. Le PIB allemand est lui aussi attendu mercredi, tout comme celui de la France, des Pays-Bas et du Portugal.</p><p>En France, les dernières prévisions de l'Institut national de la statistique (Insee) tablent sur un PIB en hausse de seulement 0,2% au deuxième trimestre.</p><p>Autres difficultés en vue: les ajustements à l'oeuvre dans plusieurs pays de la zone euro, qui se traduisent par de grosses coupes budgétaires, "vont continuer à peser sur l'activité économique", a récemment averti M. Draghi, pour qui des "risques (continuent) à peser" sur celle-ci. Parmi eux, une évolution de la demande mondiale et en zone euro inférieure aux attentes.</p><p>"Les pays européens souffrent toujours d'une demande domestique atone, écrasée par le niveau du chômage, la faiblesse des hausses salariales et la pression fiscale croissante. Par conséquent, aucun support n'est à espérer en provenance du marché domestique qui devrait simplement cesser de se réduire à l'horizon 2014", développe François Duhen, analyste pour CM-CIC Securities.</p><p>"Les relais de croissance devront être trouvés dans le reste du monde et, sur ce point, la dégradation des perspectives de nombreux pays émergents ne sera pas sans conséquence", pointe-t-il.</p><p>Sur l'ensemble de l'année 2013, les prévisionnistes interrogés par la BCE estiment que la zone euro verra son activité se contracter de 0,6%. En 2014, ils attendent une hausse de 0,9%. Le FMI fait des prévisions similaires pour les deux années.</p><p>Moins pessimiste, la Commission européenne tablait au printemps sur une baisse du PIB de 0,4% cette année, avant une nette reprise en 2014 (+1,2%) pour les 17 pays de l'Union monétaire.</p></DataContent>
+ </ContentItem>
+ </NewsComponent>
+
+ <NewsComponent Duid="photo0">
+ <NewsLines>
+ <HeadLine>Le logo de l&apos;euro devant la Banque centrale européenne, à Francfort, le 7 février 2013</HeadLine>
+ <CreditLine>AFP/Archives</CreditLine>
+ <ByLine>Daniel Roland</ByLine>
+ </NewsLines>
+ <AdministrativeMetadata>
+ <Provider>
+ <Party FormalName="afp.com" />
+ </Provider>
+ <Creator>
+ <Party FormalName="Daniel Roland"/>
+ </Creator>
+ <Source>
+ <Party FormalName="AFP"/>
+ </Source>
+ </AdministrativeMetadata>
+ <NewsComponent>
+ <Role FormalName="Caption"/>
+ <ContentItem>
+ <MediaType FormalName="Text"/>
+ <Format FormalName="bcNITF2.5"/>
+ <DataContent><p>Si les prévisions des analystes se confirment mercredi, la zone euro devrait avoir renoué avec la croissance au deuxième trimestre et tourné le dos à sa plus longue phase de récession, mais la reprise s'annonce molle et la sortie de crise encore lointaine.</p></DataContent>
+ </ContentItem>
+ </NewsComponent>
+ <NewsComponent>
+ <Role FormalName="HighDef"/>
+ <ContentItem Href="1501d2f14c069680518edb03ac4e5342d284ada2.jpg">
+ <MediaType FormalName="Photo"/>
+ <Format FormalName="JPG"/>
+ <Characteristics>
+ <SizeInBytes>77300</SizeInBytes>
+ <Property FormalName="Width" Value="768"/>
+ <Property FormalName="Height" Value="509"/>
+ </Characteristics>
+ </ContentItem>
+ </NewsComponent>
+ <NewsComponent>
+ <Role FormalName="Preview"/>
+ <ContentItem Href="7212d3214c744dbea46ff7cf1c6562aa2df6fbd8.jpg">
+ <MediaType FormalName="Photo"/>
+ <Format FormalName="JPG"/>
+ <Characteristics>
+ <SizeInBytes>40240</SizeInBytes>
+ <Property FormalName="Width" Value="511"/>
+ <Property FormalName="Height" Value="339"/>
+ </Characteristics>
+ </ContentItem>
+ </NewsComponent>
+ <NewsComponent>
+ <Role FormalName="Quicklook"/>
+ <ContentItem Href="a249dc6c0fa8ba62e4da8272ca85c77d45d645f0.jpg">
+ <MediaType FormalName="Photo"/>
+ <Format FormalName="JPG"/>
+ <Characteristics>
+ <SizeInBytes>11544</SizeInBytes>
+ <Property FormalName="Width" Value="244"/>
+ <Property FormalName="Height" Value="162"/>
+ </Characteristics>
+ </ContentItem>
+ </NewsComponent>
+ <NewsComponent>
+ <Role FormalName="Thumbnail"/>
+ <ContentItem Href="5a7b41e96fda2fb10b0c1a0c8137f67dc98b6bf8.jpg">
+ <MediaType FormalName="Photo"/>
+ <Format FormalName="JPG"/>
+ <Characteristics>
+ <SizeInBytes>3317</SizeInBytes>
+ <Property FormalName="Width" Value="110"/>
+ <Property FormalName="Height" Value="73"/>
+ </Characteristics>
+ </ContentItem>
+ </NewsComponent>
+ <NewsComponent>
+ <Role FormalName="Original"/>
+ <ContentItem Href="72b9c32db509b6aea6ce1a6b39ba85a0dac5dba2.jpg">
+ <MediaType FormalName="Photo"/>
+ <Format FormalName="JPG"/>
+ <Characteristics>
+ <SizeInBytes>1841443</SizeInBytes>
+ <Property FormalName="Width" Value="3452"/>
+ <Property FormalName="Height" Value="2288"/>
+ </Characteristics>
+ </ContentItem>
+ </NewsComponent>
+ </NewsComponent>
+ </NewsComponent>
+ </NewsItem>
+</NewsML>
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/newsmlmmd.urn.newsml.afp.com.20130813.e8b1aac5.459d.4041.ad3b.7de93a3e2bc0.xml b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/newsmlmmd.urn.newsml.afp.com.20130813.e8b1aac5.459d.4041.ad3b.7de93a3e2bc0.xml
new file mode 100644
index 0000000..102513f
--- /dev/null
+++ b/modules/custom/arte_afp/modules/arte_feeds_afp/datas/francais/journal/eco/newsmlmmd.urn.newsml.afp.com.20130813.e8b1aac5.459d.4041.ad3b.7de93a3e2bc0.xml
@@ -0,0 +1,176 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<NewsML Version="1.2">
+ <Catalog Href="http://www.afp.com/dtd/AFPCatalog.xml"/>
+ <NewsEnvelope>
+ <DateAndTime>20130813T085328Z</DateAndTime>
+ </NewsEnvelope>
+ <NewsItem>
+ <Identification>
+ <NewsIdentifier>
+ <ProviderId>afp.com</ProviderId>
+ <DateId>20130813</DateId>
+ <NewsItemId>e8b1aac5-459d-4041-ad3b-7de93a3e2bc0</NewsItemId>
+ <RevisionId PreviousRevision="0" Update="N">1</RevisionId>
+ <PublicIdentifier>urn:newsml:afp.com:20130813:e8b1aac5-459d-4041-ad3b-7de93a3e2bc0:1</PublicIdentifier>
+ </NewsIdentifier>
+ <NameLabel>assurances-banque-acquisition-marchés-Bourse</NameLabel>
+ </Identification>
+ <NewsManagement>
+ <NewsItemType FormalName="News"/>
+ <FirstCreated>20130813T085305Z</FirstCreated>
+ <ThisRevisionCreated>20130813T085305Z</ThisRevisionCreated>
+ <Status FormalName="Usable"/>
+ </NewsManagement>
+ <NewsComponent>
+ <NewsLines>
+ <HeadLine>SocGen: Groupama sort du capital, touche 517 millions d&apos;euros</HeadLine>
+ <DateLine>Paris (AFP)</DateLine>
+ <CopyrightLine>© 2013 AFP</CopyrightLine>
+ <SlugLine>assurances-banque-acquisition-marchés-Bourse</SlugLine>
+ </NewsLines>
+ <AdministrativeMetadata>
+ <Provider>
+ <Party FormalName="afp.com" />
+ </Provider>
+ </AdministrativeMetadata>
+ <DescriptiveMetadata>
+ <Language FormalName="fr" />
+ <OfInterestTo FormalName="francais--journal--eco"/>
+ <Location HowPresent="Origin">
+ <Property FormalName="Country" Value="FRA"/>
+ <Property FormalName="City" Value="Paris"/>
+ <Property FormalName="Latitude" Value="48.85341"/>
+ <Property FormalName="Longitude" Value="2.34121"/>
+ </Location>
+ <SubjectCode>
+ <SubjectDetail FormalName="04006006"/>
+ </SubjectCode>
+ <SubjectCode>
+ <SubjectDetail FormalName="04006002"/>
+ </SubjectCode>
+ <SubjectCode>
+ <SubjectDetail FormalName="04016005"/>
+ </SubjectCode>
+ <SubjectCode>
+ <SubjectMatter FormalName="04009000"/>
+ </SubjectCode>
+ <SubjectCode>
+ <SubjectDetail FormalName="04009003"/>
+ </SubjectCode>
+ <SubjectCode>
+ <Subject FormalName="04000000"/>
+ </SubjectCode>
+ <SubjectCode>
+ <Subject FormalName="04000000"/>
+ </SubjectCode>
+ <SubjectCode>
+ <Subject FormalName="04000000"/>
+ </SubjectCode>
+ <SubjectCode>
+ <SubjectDetail FormalName="04016029"/>
+ </SubjectCode>
+ <SubjectCode>
+ <SubjectMatter FormalName="04006000"/>
+ </SubjectCode>
+ </DescriptiveMetadata>
+ <NewsComponent>
+ <ContentItem>
+ <MediaType FormalName="Text"/>
+ <Format FormalName="bcNITF2.5"/>
+ <DataContent><media media-type="image" style="leftSide"><media-reference data-location="#photo0" mime-type=""/></media><p>L'assureur mutualiste Groupama a récupéré 517 millions d'euros en soldant sa participation dans la Société Générale, qui représentait environ 1,9% du capital, a-t-il indiqué mardi dans un communiqué.</p><p>Cette opération, annoncée lundi soir, a fait chuter le titre Société Générale de près de 2% à l'ouverture de la séance à la Bourse de Paris. Vers 10H30, l'action perdait 1,60% à 35,03 euros.</p><p>"Dans un marché d'été", cette nouvelle "anime un peu la cote", mais cette baisse "n'est pas de nature à entraîner une révision de l'avis des investisseurs" sur la valeur, a estimé Remy Naturkrejt, un gérant de Meeschaert Gestion Privée, en rappelant que depuis le début de l'été le titre SocGen était passé de près de 26 euros à près de 36 euros.</p><p>"Cette opération relève du rééquilibrage du portefeuille des actifs de Groupama annoncé dès fin 2011, dans des conditions de marché qui le permettaient, et ne reflète en rien une appréciation négative de la stratégie ou de la gestion de la Société Générale", a souligné de son côté l'assureur.</p><p>Groupama a revendu en dehors du marché les 14.881.016 actions qu'elle détenait encore dans la banque française, auprès d’investisseurs institutionnels.</p><p>Le placement était dirigé par Goldman Sachs International et HSBC.</p><p>L'assureur mutualiste est redevenu bénéficiaire au premier semestre après deux exercices passés dans le rouge, marqués par des dépréciations qui l'ont contraint à céder des actifs et à réduire ses coûts.</p></DataContent>
+ </ContentItem>
+ </NewsComponent>
+
+ <NewsComponent Duid="photo0">
+ <NewsLines>
+ <HeadLine>Le logo de la Société Générale, le 29 avril 2012</HeadLine>
+ <CreditLine>AFP/Archives</CreditLine>
+ <ByLine>Loic Venance</ByLine>
+ <NewsLine>
+ <NewsLineType FormalName="AdvisoryLine" />
+ <NewsLineText>PICTURE TAKEN WITH A TILT AND SHIFT LENS</NewsLineText>
+ </NewsLine>
+ </NewsLines>
+ <AdministrativeMetadata>
+ <Provider>
+ <Party FormalName="afp.com" />
+ </Provider>
+ <Creator>
+ <Party FormalName="Loic Venance"/>
+ </Creator>
+ <Source>
+ <Party FormalName="AFP"/>
+ </Source>
+ </AdministrativeMetadata>
+ <NewsComponent>
+ <Role FormalName="Caption"/>
+ <ContentItem>
+ <MediaType FormalName="Text"/>
+ <Format FormalName="bcNITF2.5"/>
+ <DataContent><p>L'assureur mutualiste Groupama a récupéré 517 millions d'euros en soldant sa participation dans la Société Générale, qui représentait environ 1,9% du capital, a-t-il indiqué mardi dans un communiqué.</p></DataContent>
+ </ContentItem>
+ </NewsComponent>
+ <NewsComponent>
+ <Role FormalName="HighDef"/>
+ <ContentItem Href="39f947236afda4fe5b7ff80257bcab975c2493ed.jpg">
+ <MediaType FormalName="Photo"/>
+ <Format FormalName="JPG"/>
+ <Characteristics>
+ <SizeInBytes>35488</SizeInBytes>
+ <Property FormalName="Width" Value="768"/>
+ <Property FormalName="Height" Value="512"/>
+ </Characteristics>
+ </ContentItem>
+ </NewsComponent>
+ <NewsComponent>
+ <Role FormalName="Preview"/>
+ <ContentItem Href="5411b70307caeec0a940451dcc3f53abd664e2e1.jpg">
+ <MediaType FormalName="Photo"/>
+ <Format FormalName="JPG"/>
+ <Characteristics>
+ <SizeInBytes>20252</SizeInBytes>
+ <Property FormalName="Width" Value="512"/>
+ <Property FormalName="Height" Value="341"/>
+ </Characteristics>
+ </ContentItem>
+ </NewsComponent>
+ <NewsComponent>
+ <Role FormalName="Quicklook"/>
+ <ContentItem Href="774ae7f0380bd281b4a1f6925d58034cf571b684.jpg">
+ <MediaType FormalName="Photo"/>
+ <Format FormalName="JPG"/>
+ <Characteristics>
+ <SizeInBytes>7905</SizeInBytes>
+ <Property FormalName="Width" Value="245"/>
+ <Property FormalName="Height" Value="163"/>
+ </Characteristics>
+ </ContentItem>
+ </NewsComponent>
+ <NewsComponent>
+ <Role FormalName="Thumbnail"/>
+ <ContentItem Href="231bed086a163efd2e341d2884a148dc02a90d23.jpg">
+ <MediaType FormalName="Photo"/>
+ <Format FormalName="JPG"/>
+ <Characteristics>
+ <SizeInBytes>2887</SizeInBytes>
+ <Property FormalName="Width" Value="110"/>
+ <Property FormalName="Height" Value="73"/>
+ </Characteristics>
+ </ContentItem>
+ </NewsComponent>
+ <NewsComponent>
+ <Role FormalName="Original"/>
+ <ContentItem Href="1b1ed0b8f4f644faf026f897ec56d6f40e8cd5b2.jpg">
+ <MediaType FormalName="Photo"/>
+ <Format FormalName="JPG"/>
+ <Characteristics>
+ <SizeInBytes>1244657</SizeInBytes>
+ <Property FormalName="Width" Value="2667"/>
+ <Property FormalName="Height" Value="1778"/>
+ </Characteristics>
+ </ContentItem>
+ </NewsComponent>
+ </NewsComponent>
+ </NewsComponent>
+ </NewsItem>
+</NewsML>
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/plugins/FeedsAFPFetcher.inc b/modules/custom/arte_afp/modules/arte_feeds_afp/plugins/FeedsAFPFetcher.inc
new file mode 100644
index 0000000..edcd8f5
--- /dev/null
+++ b/modules/custom/arte_afp/modules/arte_feeds_afp/plugins/FeedsAFPFetcher.inc
@@ -0,0 +1,199 @@
+<?php
+/**
+ * @file
+ * Feeds FTP Fetcher classses and functions.
+ */
+
+/**
+ * Fetches data via AFP server.
+ */
+class FeedsAFPFetcher extends FeedsFetcher {
+
+ /**
+ * Implements FeedsFetcher::fetch().
+ */
+ public function fetch(FeedsSource $source) {
+ // Loads feeds ftp fetcher plugin.
+ ctools_include('FeedsFTPFetcher', 'feeds_ftp_fetcher', 'plugins');
+
+ $state = $source->state(FEEDS_FETCH);
+
+ // GET THE LIST OF FILES TO PROCESS.
+ // Check if we have a list of files to process.
+ if (!isset($state->files)) {
+ // Reset our ftp file list because we want to refresh it.
+ $ftp_files = array();
+ variable_del('afp_ftp_file_list');
+
+ // Make sure the directory exists.
+ $destination = 'public://feeds';
+ file_prepare_directory($destination, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
+
+ // Connect to FTP server.
+ $connect = feeds_ftp_fetcher_connect($this->config['server'], $this->config['username'], $this->config['password'], $this->config['port']);
+
+ // Get all directories to fetch.
+ $directories = explode(',', $this->config['directories']);
+ $files = array();
+ if (is_array($directories) && count($directories) > 0) {
+ foreach ($directories as $directory) {
+ $directory = trim($directory);
+
+ // Change to this directory.
+ feeds_ftp_fetcher_chdir($connect, $directory);
+
+ // List of files on this FTP server directory.
+ $dir_files = feeds_ftp_fetcher_list($connect, $directory);
+ if ($dir_files === FALSE) {
+ // If error, log and continue to next directory.
+ watchdog('AFP Feed Fetcher', 'Unable to list remote directory: %directory', array('%directory' => $directory), WATCHDOG_WARNING);
+ continue;
+ }
+
+ // Iterate on file and only keep XML.
+ if (!empty($dir_files)) {
+ // Store the files in global var to increase performances.
+ $ftp_files[$directory] = $dir_files;
+
+ foreach ($dir_files as $filename) {
+ $infos = pathinfo($filename);
+ if ($infos['extension'] == 'xml' && strpos($filename, 'index') === FALSE) {
+ $filename = $directory . '/' . $filename;
+ $files[] = $filename;
+ }
+ }
+ }
+ }
+ }
+
+ // Set state variables for later use.
+ $state->files = $files;
+ $state->total = count($files);
+
+ // Save the ftp file list to increase performance.
+ variable_set('afp_ftp_file_list', $ftp_files);
+
+ // Make sure that the FTP connection is closed.
+ @ftp_close($connect);
+ }
+
+ // DOWNLOAD FILES.
+ // Check if there are any files to download.
+ if (!empty($state->files)) {
+ foreach ($state->files as $i => $filename) {
+ $infos = pathinfo($filename);
+
+ // Connect to FTP server.
+ $connect = feeds_ftp_fetcher_connect($this->config['server'], $this->config['username'], $this->config['password'], $this->config['port']);
+
+ // Change to root directory.
+ feeds_ftp_fetcher_chdir($connect, $infos['dirname']);
+
+ // Download the remote file.
+ $temp_filename = feeds_ftp_fetcher_download($connect, $infos['basename']);
+ if (!$temp_filename) {
+ // If error, log and continue to next file.
+ watchdog('AFP Feed Fetcher', 'Failed to retrieve %filename from remote server.', array('%filename' => $filename), WATCHDOG_WARNING);
+ unset($state->files[$i]); // remove this file from the list of file to process - otherwise it will be stuck.
+ continue;
+ }
+
+ // Record the temp file details in the state.
+ $state->last_file = array(
+ 'name' => $filename,
+ 'size' => feeds_ftp_fetcher_file_size($connect, $filename),
+ 'temp' => $temp_filename,
+ );
+
+ // Make sure we close the ftp connection.
+ @ftp_close($connect);
+
+ // Remove the last file from the list of files that need to be downloaded
+ unset($state->files[$i]);
+ break;
+ }
+
+ // Retrieve the raw data from the temp file.
+ if (isset($state->last_file['temp'])) {
+ $raw = feeds_ftp_fetcher_get_data($state->last_file['temp']);
+ $state->progress($state->total, $state->total - count($state->files));
+ return new FeedsFetcherResult($raw);
+ }
+ }
+
+ $state->progress(0, 1);
+ return new FeedsFetcherResult('');
+ }
+
+ /**
+ * Override parent::configDefaults().
+ */
+ public function configDefaults() {
+ global $conf;
+
+ if (!empty($conf['afp_feeds_settings'])) {
+ return $conf['afp_feeds_settings'];
+ }
+ else {
+ return array(
+ 'server' => 'ftp-clients.afp.com',
+ 'port' => 21,
+ 'directories' => implode(', ', array(
+ '/pub/francais/journal/',
+ '/pub/deutsch/spezial/',
+ )),
+ 'username' => 'arte-',
+ 'password' => '',
+ );
+ }
+ }
+
+ /**
+ * Override parent::configForm().
+ */
+ public function configForm(&$form_state) {
+ $form = array();
+
+ $form['server'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Server'),
+ '#description' => t("Enter a server hostname. Do not include 'ftp://' prefix."),
+ '#default_value' => $this->config['server'],
+ '#required' => TRUE,
+ );
+
+ $form['port'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Port'),
+ '#description' => t('Enter a port to connect on.'),
+ '#default_value' => $this->config['port'],
+ '#required' => TRUE,
+ );
+
+ $form['username'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Username'),
+ '#description' => t('The username used to connect to the FTP server.'),
+ '#default_value' => $this->config['username'],
+ '#required' => TRUE,
+ );
+
+ $form['password'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Password'),
+ '#description' => t('The password used to connect to the FTP server.'),
+ '#default_value' => $this->config['password'],
+ '#required' => TRUE,
+ );
+
+ $form['directories'] = array(
+ '#type' => 'textarea',
+ '#title' => t('Directories'),
+ '#description' => t("The directories on the FTP server separated with a comma. E.g., '/pub/francais/journal/sci, /pub/francais/journal/hightech'"),
+ '#default_value' => $this->config['directories'],
+ '#required' => TRUE,
+ );
+
+ return $form;
+ }
+}
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/plugins/FeedsAFPParser.inc b/modules/custom/arte_afp/modules/arte_feeds_afp/plugins/FeedsAFPParser.inc
new file mode 100644
index 0000000..b716191
--- /dev/null
+++ b/modules/custom/arte_afp/modules/arte_feeds_afp/plugins/FeedsAFPParser.inc
@@ -0,0 +1,513 @@
+<?php
+
+/**
+ * @file
+ * Contains FeedsAFPParser dealing with AFP feed importing.
+ */
+use Drupal\arte_feeds_afp\FilesDir\FilesDirInterface;
+
+/**
+ * A parser for the AFP feed specification.
+ *
+ * @see http://www.afp.com/dtd/AFPCatalog.xml
+ */
+class FeedsAFPParser extends FeedsParser {
+
+ /**
+ * Implements FeedsParser::parse().
+ *
+ * @param \FeedsSource $source
+ * @param \FeedsFetcherResult $fetcher_result
+ *
+ * @return \FeedsParserResult
+ */
+ public function parse(FeedsSource $source, FeedsFetcherResult $fetcher_result) {
+
+ // The FeedsFTPFetcher class is autoloaded automatically, but the same file
+ // also contains some procedural code that we need here, such as
+ /* @see feeds_ftp_fetcher_connect() */
+ ctools_include('FeedsFTPFetcher', 'feeds_ftp_fetcher', 'plugins');
+
+ $item = $this->parseBuildItem($source, $fetcher_result);
+
+ if (NULL !== $item) {
+ return new FeedsParserResult(array($item));
+ }
+ else {
+ return new FeedsParserResult();
+ }
+ }
+
+ /**
+ * @param \FeedsSource $source
+ * @param \FeedsFetcherResult $fetcher_result
+ *
+ * @return array|null
+ */
+ private function parseBuildItem(FeedsSource $source, FeedsFetcherResult $fetcher_result) {
+
+ if ($fetcher_result instanceof FeedsFileFetcherResult) {
+ // Fetcher results coming from \feeds_fetcher_directory_fetcher are not
+ // filtered, but luckily we know the original file name.
+ $xmlFilePath = $fetcher_result->getFilePath();
+ }
+ else {
+ // $fetcher_result->getFilePath() would create a temporary file, which we
+ // should avoid. And the name of this temp file would not be meaningful.
+ // Instead, we use the file path from FeedsAFPFetcher.
+ $xmlFilePath = $source->state['fetch']->last_file['name'];
+ }
+
+ $fileInfo = pathinfo($xmlFilePath);
+ if (0
+ // Skip jpg and other non-xml files.
+ || !array_key_exists('extension', $fileInfo)
+ || 'xml' !== $fileInfo['extension']
+ // Skip index.xml.
+ || 'index.xml' === $fileInfo['basename']
+ ) {
+ return NULL;
+ }
+
+ try {
+ $raw = $fetcher_result->getRaw();
+ }
+ catch (Exception $e) {
+ // If an exception is raised, we don't have valid XML and we skip.
+ $this->logWarning(
+ 'Exception getting raw XML for %file: %message',
+ array(
+ '%file' => $xmlFilePath,
+ '%message' => $e->getMessage(),
+ ));
+ // Return the empty FeedsParserResult object.
+ return NULL;
+ }
+
+ try {
+ $xml = new SimpleXMLElement($raw);
+ }
+ catch (Exception $e) {
+ // If an exception is raised, we don't have valid XML and we skip.
+ $this->logWarning(
+ 'Exception parsing XML for %file: %message'.
+ array(
+ '%file' => $xmlFilePath,
+ '%message' => $e->getMessage(),
+ ));
+ // Return the empty FeedsParserResult object.
+ return NULL;
+ }
+
+ try {
+ $filesDir = $this->sourceGetFilesDir($source, dirname($xmlFilePath));
+ }
+ catch (\Exception $e) {
+ $this->logWarning(
+ 'Exception opening files directory for %file: %message',
+ array(
+ '%file' => $xmlFilePath,
+ '%message' => $e->getMessage(),
+ ));
+ return NULL;
+ }
+
+ try {
+ // Adds the item to the list.
+ $item = $this->xmlBuildItem($xml, $filesDir);
+ }
+ catch (Exception $e) {
+ $this->logWarning(
+ 'Exception interpreting XML data for %file: %message',
+ array(
+ '%file' => $xmlFilePath,
+ '%message' => $e->getMessage(),
+ ));
+ return NULL;
+ }
+
+ return $item;
+ }
+
+ /**
+ * @param \FeedsSource $source
+ *
+ * @return \Drupal\arte_feeds_afp\FilesDir\FilesDirInterface
+ *
+ * @throws \RuntimeException
+ */
+ private function sourceGetFilesDir(FeedsSource $source, $dirname) {
+
+ $fetcher = $source->importer->fetcher;
+
+ // The fetcher object needs to be a FeedsAFPFetcher, because further down
+ // we use the FTP login data from the fetcher, to fetch images attached to
+ // the news stories.
+ if ($fetcher instanceof FeedsAFPFetcher) {
+ $ftpSettings = new \Drupal\arte_feeds_afp\Ftp\FtpSettings($fetcher->config);
+ return new \Drupal\arte_feeds_afp\FilesDir\FtpFilesDir($ftpSettings, $dirname);
+ }
+ elseif ($fetcher instanceof \feeds_fetcher_directory_fetcher) {
+ return new \Drupal\arte_feeds_afp\FilesDir\LocalFilesDir($dirname);
+ }
+
+ throw new \RuntimeException('Unexpected fetcher type: ' . get_class($fetcher));
+ }
+
+ /**
+ * @param \SimpleXMLElement $xml
+ * @param \Drupal\arte_feeds_afp\FilesDir\FilesDirInterface $filesDir
+ *
+ * @return array
+ *
+ * @throws \Exception
+ */
+ private function xmlBuildItem(SimpleXMLElement $xml, FilesDirInterface $filesDir) {
+
+ $xmlNewsItem = $xml->NewsItem;
+ if (NULL === $xmlNewsItem) {
+ throw new \RuntimeException('XML Structure: $xml->NewsItem is missing.');
+ }
+ if (!$xmlNewsItem instanceof SimpleXMLElement) {
+ throw new \RuntimeException('XML Structure: $xml->NewsItem is not a SimpleXMLElement.');
+ }
+ return $this->xmlNewsItemBuildItem($xmlNewsItem, $filesDir);
+ }
+
+ /**
+ * @param \SimpleXMLElement $xmlNewsItem
+ * @param \Drupal\arte_feeds_afp\FilesDir\FilesDirInterface $filesDir
+ *
+ * @return array
+ *
+ * @throws \Exception
+ */
+ private function xmlNewsItemBuildItem(SimpleXMLElement $xmlNewsItem, FilesDirInterface $filesDir) {
+
+ // Set item values from XML.
+ $item = array();
+
+ $item['NewsItemId'] = (string) $xmlNewsItem->Identification->NewsIdentifier->NewsItemId[0];
+ $item['DateId'] = $xmlNewsItem->Identification->NewsIdentifier->DateId[0]->__toString();
+ $item['RevisionId'] = $xmlNewsItem->Identification->NewsIdentifier->RevisionId[0]->__toString();
+ $item['PublicIdentifier'] = $xmlNewsItem->Identification->NewsIdentifier->PublicIdentifier[0]->__toString();
+ $item['NameLabel'] = $xmlNewsItem->Identification->NameLabel[0]->__toString();
+ $item['FirstCreated'] = strtotime($xmlNewsItem->NewsManagement->FirstCreated[0]->__toString());
+ $item['ThisRevisionCreated'] = strtotime($xmlNewsItem->NewsManagement->ThisRevisionCreated[0]->__toString());
+ $item['HeadLine'] = (string) substr($xmlNewsItem->NewsComponent->NewsLines->HeadLine[0], 0, 255);
+ $item['ByLine'] = (string) $xmlNewsItem->NewsComponent->NewsLines->ByLine[0];
+ $item['DateLine'] = (string) $xmlNewsItem->NewsComponent->NewsLines->DateLine[0];
+ $item['CopyrightLine'] = (string) $xmlNewsItem->NewsComponent->NewsLines->CopyrightLine[0];
+ $item['SlugLine'] = (string) $xmlNewsItem->NewsComponent->NewsLines->SlugLine[0];
+
+ // Extract main category from parent folder and use tags as categories.
+ $categories = array_merge(array($this->mainCategoryExtract($filesDir)), explode('-', (string) $xmlNewsItem->Identification->NameLabel[0]->__toString()));
+ $item['categories'] = array_map(function($tag) {
+ return drupal_ucfirst($tag);
+ }, $categories);
+
+ if (isset($xmlNewsItem->NewsComponent->DescriptiveMetadata->Language[0])) {
+ $item['Language'] = (string) $xmlNewsItem->NewsComponent->DescriptiveMetadata->Language[0]->attributes()->FormalName[0];
+ }
+
+ if (isset($xmlNewsItem->NewsComponent->DescriptiveMetadata->OfInterestTo[0])) {
+ $item['OfInterestTo'] = (string) $xmlNewsItem->NewsComponent->DescriptiveMetadata->OfInterestTo[0]->attributes()->FormalName[0];
+ }
+
+ // Parse item body.
+ $item['body'] = '';
+ foreach ($xmlNewsItem->NewsComponent->NewsComponent as $NewsComponent) {
+ if (isset($NewsComponent->ContentItem[0]->MediaType) && (string) $NewsComponent->ContentItem[0]->MediaType->attributes()->FormalName[0] == 'Text') {
+ $texts = (array) $NewsComponent->ContentItem[0]->DataContent[0]->p;
+ foreach ($texts as $txt) {
+ $item['body'] .= '<p>' . $txt . '</p>';
+ }
+ }
+ }
+
+ // Parse item image.
+ $item_key = 'photo0';
+
+ // Try to get the photo component.
+ $photoComponentElements = $xmlNewsItem->NewsComponent->xpath('NewsComponent[@Duid="' . $item_key . '"]');
+ if (is_array($photoComponentElements) and count($photoComponentElements) == 1) {
+ $photoComponent = array_shift($photoComponentElements);
+ $photoData = $this->photoComponentExtractData($photoComponent, $filesDir);
+ }
+ else {
+ $photoData = array();
+ }
+
+ foreach (array('HeadLine', 'CreditLine', 'ByLine', 'photoHref', 'DataContent') as $key) {
+ $item[$key . $item_key] = array_key_exists($key, $photoData)
+ ? $photoData[$key]
+ : '';
+ }
+
+ // Try to find existing node and re-use it's atom.
+ if (isset($photoData['photoHref']) && empty($photoData['photoHref'])) {
+ $existing = $this->findExisting($item['NewsItemId']);
+ if (!is_null($existing)) {
+ $images = field_get_items('node', $existing, 'field_afp_story_images');
+ if (!empty($images)) {
+ $atom = scald_atom_load($images[0]['sid']);
+ if (!empty($atom) && isset($atom->file_source)) {
+ // Use existing atom file source to avoid image being removed.
+ $photoData['photoHref'] = $atom->file_source;
+ $this->logWarning('Node %item : used previously existing atom to prevent image deletion.', array(
+ '%item' => $existing->nid,
+ ));
+ }
+ }
+ }
+ }
+
+ return $item;
+ }
+
+ /**
+ * @param \SimpleXMLElement $photoComponent
+ * @param \Drupal\arte_feeds_afp\FilesDir\FilesDirInterface $filesDir
+ *
+ * @return array
+ */
+ private function photoComponentExtractData(SimpleXMLElement $photoComponent, FilesDirInterface $filesDir) {
+
+ $photos = $this->photoComponentExtractPhotos($photoComponent);
+
+ if (!is_array($photos) || count($photos) <= 1) {
+ return array();
+ }
+
+ $destination = $this->photosCreatePhoto($photos, $filesDir);
+
+ return $this->downloadedPhotoBuildData($photoComponent, $destination);
+ }
+
+ /**
+ * @param array $photos
+ * @param \Drupal\arte_feeds_afp\FilesDir\FilesDirInterface $filesDir
+ *
+ * @return array
+ * array($photoPath, $temp_filename) or array(NULL, NULL).
+ */
+ private function photosDownloadFirstAvailablePhoto(array $photos, FilesDirInterface $filesDir) {
+
+ foreach ($photos as $type => $photoPath) {
+ try {
+ $temp_filename = $filesDir->downloadFile($photoPath);
+ if ($temp_filename) {
+ // Close the FTP connection.
+ /* @see FtpFilesDir::__destruct() */
+ unset($filesDir);
+ return array($photoPath, $temp_filename);
+ }
+ }
+ catch (\Exception $e) {
+ $this->logWarning($e->getMessage());
+ continue;
+ }
+ }
+
+ $this->logWarning(
+ 'None of the photos exists in %directory: @photos',
+ array(
+ '%directory' => $filesDir->getLabel(),
+ '@photos' => var_export($photos, TRUE),
+ ));
+
+ // Close the FTP connection.
+ /* @see FtpFilesDir::__destruct() */
+ unset($filesDir);
+
+ return array(NULL, NULL);
+ }
+
+ /**
+ * Creates one
+ *
+ * @param array $photos
+ * @param \Drupal\arte_feeds_afp\FilesDir\FilesDirInterface $filesDir
+ *
+ * @return string|null
+ * The filename of the destination file, or NULL in case of failure.
+ */
+ private function photosCreatePhoto(array $photos, FilesDirInterface $filesDir) {
+
+ list($photoName, $temp_filename) = $this->photosDownloadFirstAvailablePhoto($photos, $filesDir);
+
+ if (!strlen($photoName) || !strlen($temp_filename)) {
+ return NULL;
+ }
+
+ // Copy the file to public feeds repertory.
+ $destination = 'public://feeds/' . $photoName;
+ file_unmanaged_copy($temp_filename, $destination, FILE_EXISTS_REPLACE);
+
+ if (!file_exists($destination)) {
+ $this->logWarning(
+ 'Failed to create photo at %file',
+ array('%file' => $destination));
+ return NULL;
+ }
+
+ return $destination;
+ }
+
+ /**
+ * @param \SimpleXMLElement $photoComponent
+ * @param $destination
+ *
+ * @return array
+ */
+ private function downloadedPhotoBuildData(SimpleXMLElement $photoComponent, $destination) {
+
+ $data = array();
+
+ // Fill item photo data.
+ $data['HeadLine'] = $photoComponent->NewsLines->HeadLine[0]->__toString();
+ $data['CreditLine'] = $photoComponent->NewsLines->CreditLine[0]->__toString();
+ $data['ByLine'] = $photoComponent->NewsLines->ByLine[0]->__toString();
+ $data['photoHref'] = $destination;
+
+ // Add photo body data.
+ $paragraphs = (array) $photoComponent->p;
+ foreach ($paragraphs as $p) {
+ $data['DataContent'] .= '<p>' . $p . '</p>';
+ }
+
+ return $data;
+ }
+
+ /**
+ * @param \SimpleXmlElement $photoComponent
+ * XML element that contains the photo information.
+ *
+ * @return string[]
+ * URLs of photos in different format/resolution.
+ */
+ private function photoComponentExtractPhotos(SimpleXmlElement $photoComponent) {
+
+ // Get all images information.
+ $photos = array();
+
+ $photoOriginal = $photoComponent->xpath('NewsComponent/Role[@FormalName="Original"]/parent::*/ContentItem');
+ if (is_array($photoOriginal) and count($photoOriginal) == 1) {
+ $photos['Original'] = (string) $photoOriginal[0]->attributes()->Href[0];
+ }
+
+ $photoHighDef = $photoComponent->xpath('NewsComponent/Role[@FormalName="HighDef"]/parent::*/ContentItem');
+ if (is_array($photoHighDef) and count($photoHighDef) == 1) {
+ $photos['HighDef'] = (string) $photoHighDef[0]->attributes()->Href[0];
+ }
+
+ $photoQuicklook = $photoComponent->xpath('NewsComponent/Role[@FormalName="Quicklook"]/parent::*/ContentItem');
+ if (is_array($photoQuicklook) and count($photoQuicklook) == 1) {
+ $photos['Quicklook'] = (string) $photoQuicklook[0]->attributes()->Href[0];
+ }
+
+ $photoThumbnail = $photoComponent->xpath('NewsComponent/Role[@FormalName="Thumbnail"]/parent::*/ContentItem');
+ if (is_array($photoThumbnail) and count($photoThumbnail) == 1) {
+ $photos['Thumbnail'] = (string) $photoThumbnail[0]->attributes()->Href[0];
+ }
+
+ return $photos;
+ }
+
+ /**
+ * Extracts the main category according to parent folder.
+ *
+ * @param \Drupal\arte_feeds_afp\FilesDir\FilesDirInterface $filesDir
+ * The file concerned.
+ *
+ * @return string
+ * The main category.
+ */
+ private function mainCategoryExtract(FilesDirInterface $filesDir) {
+ // Get parent folder name.
+ $folders = explode('/', $filesDir->getLabel());
+ $parent_folder = end($folders);
+
+ // Define a optional mapping.
+ // If not found in here, fallback to folder name.
+ $mapping = array(
+ 'medecine' => 'Médecine',
+ 'sci' => 'Science'
+ );
+
+ return isset($mapping[$parent_folder]) ? $mapping[$parent_folder] : ucfirst($parent_folder);
+ }
+
+ /**
+ * Implements FeedsParser::getMappingSources().
+ */
+ public function getMappingSources() {
+ $item = array();
+
+ $item['NewsItemId']['name'] = t('NewsItemId');
+ $item['DateId']['name'] = t('DateId');
+ $item['RevisionId']['name'] = t('RevisionId');
+ $item['PublicIdentifier']['name'] = t('PublicIdentifier');
+ $item['NameLabel']['name'] = t('NameLabel');
+ $item['FirstCreated']['name'] = t('FirstCreated');
+ $item['ThisRevisionCreated']['name'] = t('ThisRevisionCreated');
+ $item['HeadLine']['name'] = t('HeadLine');
+ $item['ByLine']['name'] = t('ByLine');
+ $item['DateLine']['name'] = t('DateLine');
+ $item['CopyrightLine']['name'] = t('CopyrightLine');
+ $item['SlugLine']['name'] = t('SlugLine');
+ $item['Language']['name'] = t('Language');
+ $item['body']['name'] = t('body');
+ $item['OfInterestTo']['name'] = t('OfInterestTo');
+ $item['categories']['name'] = t('Categories');
+
+ $item_key = 'photo0';
+ $item['HeadLine' . $item_key]['name'] = 'HeadLine' . $item_key;
+ $item['CreditLine' . $item_key]['name'] = 'CreditLine' . $item_key;
+ $item['ByLine' . $item_key]['name'] = 'ByLine' . $item_key;
+ $item['photoHref' . $item_key]['name'] = 'photoHref' . $item_key;
+ $item['DataContent' . $item_key]['name'] = 'DataContent' . $item_key;
+
+ return $item;
+ }
+
+ /**
+ * Sends a warning to the watchdog.
+ *
+ * @param string $message
+ * @param array $replacements
+ */
+ private function logWarning($message, array $replacements = array()) {
+ drupal_set_message(format_string($message, $replacements), 'warning');
+ watchdog('AFP Feed Parser', $message, $replacements);
+ }
+
+ /**
+ * Helper to find an existing item that could be
+ * already imported, using the AFP item id.
+ *
+ * @param string $newsItemId
+ * The AFP news item unique ID.
+ *
+ * @return object|NULL
+ * The node or NULL if none found.
+ */
+ private function findExisting($newsItemId) {
+ $afp_story = NULL;
+
+ $query = new \EntityFieldQuery();
+ $query->entityCondition('entity_type', 'node')
+ ->entityCondition('bundle', 'afp_story')
+ ->fieldCondition('field_afp_story_item_id', 'value', $newsItemId);
+
+ $result = $query->execute();
+ if (isset($result['node'])) {
+ $news_stories = entity_load('node', array_keys($result['node']));
+ if (!empty($news_stories)) {
+ $afp_story = array_shift($news_stories);
+ }
+ }
+
+ return $afp_story;
+ }
+}
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/src/FileException.php b/modules/custom/arte_afp/modules/arte_feeds_afp/src/FileException.php
new file mode 100644
index 0000000..e6734fc
--- /dev/null
+++ b/modules/custom/arte_afp/modules/arte_feeds_afp/src/FileException.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace Drupal\arte_feeds_afp;
+
+class FileException extends \Exception {
+
+}
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/src/FilesDir/FilesDirInterface.php b/modules/custom/arte_afp/modules/arte_feeds_afp/src/FilesDir/FilesDirInterface.php
new file mode 100644
index 0000000..a956c05
--- /dev/null
+++ b/modules/custom/arte_afp/modules/arte_feeds_afp/src/FilesDir/FilesDirInterface.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Drupal\arte_feeds_afp\FilesDir;
+
+interface FilesDirInterface {
+
+ /**
+ * @param string $relativeFilePath
+ * File path relative to the directory.
+ *
+ * @return string|false
+ * The file path to a local file, or FALSE on failure.
+ */
+ public function downloadFile($relativeFilePath);
+
+ /**
+ * @return string
+ */
+ public function getLabel();
+}
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/src/FilesDir/FtpFilesDir.php b/modules/custom/arte_afp/modules/arte_feeds_afp/src/FilesDir/FtpFilesDir.php
new file mode 100644
index 0000000..d0f6487
--- /dev/null
+++ b/modules/custom/arte_afp/modules/arte_feeds_afp/src/FilesDir/FtpFilesDir.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace Drupal\arte_feeds_afp\FilesDir;
+
+use Drupal\arte_feeds_afp\Ftp\FtpSettings;
+
+class FtpFilesDir implements FilesDirInterface {
+
+ /**
+ * @var \Drupal\arte_feeds_afp\Ftp\FtpConnection
+ */
+ private $ftpConnection;
+
+ /**
+ * @var string
+ */
+ private $ftpDirectory;
+
+ /**
+ * @var string
+ */
+ private $label;
+
+ /**
+ * List of files.
+ *
+ * @var true[]
+ */
+ private $files;
+
+ /**
+ * @param \Drupal\arte_feeds_afp\Ftp\FtpSettings $ftpSettings
+ * @param string $ftpDirectory
+ *
+ * @throws \Drupal\arte_feeds_afp\Ftp\FtpException
+ */
+ public function __construct(FtpSettings $ftpSettings, $ftpDirectory) {
+ $this->ftpConnection = $ftpSettings->connect()->chdir($ftpDirectory);
+ $this->ftpDirectory = $ftpDirectory;
+ $this->label = $ftpSettings->dirGetUri($ftpDirectory);
+ $this->files = $this->ftpConnection->ls($ftpDirectory);
+ }
+
+ /**
+ * The destructor.
+ *
+ * Closes the FTP connection.
+ */
+ public function __destruct() {
+ // Explicitly trigger the connection constructor to close the FTP connection.
+ // See http://www.hackingwithphp.com/6/10/3/deleting-objects
+ unset($this->ftpConnection);
+ }
+
+ /**
+ * @param string $relativeFilePath
+ * File path relative to the directory.
+ *
+ * @return string|false
+ * The file path to a local file, or FALSE if it does not exist.
+ *
+ * @throws \Drupal\arte_feeds_afp\FileException
+ * @throws \Drupal\arte_feeds_afp\Ftp\FtpException
+ */
+ public function downloadFile($relativeFilePath) {
+ if (!in_array($relativeFilePath, $this->files, TRUE)) {
+ return FALSE;
+ }
+ $tempFile = file_create_filename($relativeFilePath, file_directory_temp());
+ $this->ftpConnection->fileDownload($this->ftpDirectory . '/' . $relativeFilePath, $tempFile);
+ return $tempFile;
+ }
+
+ /**
+ * @return string
+ */
+ public function getLabel() {
+ return $this->label;
+ }
+
+}
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/src/FilesDir/LocalFilesDir.php b/modules/custom/arte_afp/modules/arte_feeds_afp/src/FilesDir/LocalFilesDir.php
new file mode 100644
index 0000000..16b8168
--- /dev/null
+++ b/modules/custom/arte_afp/modules/arte_feeds_afp/src/FilesDir/LocalFilesDir.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Drupal\arte_feeds_afp\FilesDir;
+
+class LocalFilesDir implements FilesDirInterface {
+
+ /**
+ * @var string
+ */
+ private $dir;
+
+ /**
+ * @param string $dir
+ */
+ function __construct($dir) {
+ if (!file_exists($dir)) {
+ throw new \InvalidArgumentException(format_string('Directory %dir does not exist.', array('%dir' => $dir)));
+ }
+ $this->dir = $dir;
+ }
+
+ /**
+ * @param string $relativeFilePath
+ * File path relative to the directory.
+ *
+ * @return string|false
+ * The file path to a local file, or FALSE on failure.
+ */
+ public function downloadFile($relativeFilePath) {
+ $file = $this->dir . '/' . $relativeFilePath;
+ if (!file_exists($file)) {
+ return FALSE;
+ }
+ return $file;
+ }
+
+ /**
+ * @return string
+ */
+ public function getLabel() {
+ return $this->dir;
+ }
+
+}
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/src/Ftp/FtpConnection.php b/modules/custom/arte_afp/modules/arte_feeds_afp/src/Ftp/FtpConnection.php
new file mode 100644
index 0000000..9517fe1
--- /dev/null
+++ b/modules/custom/arte_afp/modules/arte_feeds_afp/src/Ftp/FtpConnection.php
@@ -0,0 +1,232 @@
+<?php
+
+namespace Drupal\arte_feeds_afp\Ftp;
+
+use Drupal\arte_feeds_afp\FileException;
+
+class FtpConnection {
+
+ /**
+ * @var resource
+ */
+ private $connection;
+
+ /**
+ * @param string $host
+ * @param int $port
+ *
+ * @return static
+ */
+ static function open($host, $port) {
+ $connection = @ftp_connect($host, $port);
+ if (!$connection) {
+ throw new FtpException("Failed to connect to FTP");
+ }
+ return new static($connection);
+ }
+
+ /**
+ * @param array $config
+ *
+ * @return $this
+ */
+ static function openFromConfig(array $config) {
+ return static::open($config['server'], $config['port'])
+ ->login($config['username'], $config['password'])
+ ->enablePassiveMode();
+ }
+
+ /**
+ * @param resource $connection
+ */
+ function __construct($connection) {
+ if (empty($connection)) {
+ throw new \InvalidArgumentException('Not a valid FTP connection resource.');
+ }
+ $this->connection = $connection;
+ }
+
+ /**
+ * Closes the FTP connection.
+ */
+ function __destruct() {
+ ftp_close($this->connection);
+ }
+
+ /**
+ * @param string $username
+ * @param string $password
+ *
+ * @return $this
+ *
+ * @see \ftp_login()
+ */
+ function login($username, $password) {
+ if (!@ftp_login($this->connection, $username, $password)) {
+ throw new FtpException("FTP login failed.");
+ }
+ return $this;
+ }
+
+ /**
+ * Enables passive mode.
+ *
+ * @return $this
+ *
+ * @see \ftp_pasv()
+ */
+ function enablePassiveMode() {
+ if (!@ftp_pasv($this->connection, TRUE)) {
+ throw new FtpException("Failed to enable passive mode.");
+ }
+ return $this;
+ }
+
+ /**
+ * @param string $directory
+ *
+ * @return $this
+ */
+ function chdir($directory) {
+ if (!@ftp_chdir($this->connection, $directory)) {
+ throw new FtpException('Failed to change FTP directory.');
+ }
+ return $this;
+ }
+
+ /**
+ * Gets the current directory of the FTP connection.
+ *
+ * @return string
+ */
+ function pwd() {
+ $dir = ftp_pwd($this->connection);
+ if (FALSE === $dir) {
+ throw new FtpException('Failed to determine current directory on FTP.');
+ }
+ return $dir;
+ }
+
+ /**
+ * Lists all files and subfolders in a directory on FTP.
+ *
+ * @param string $directory
+ * A directory on FTP.
+ *
+ * @return string[]
+ * Array of file and subdirectory names, relative to $directory.
+ */
+ function ls($directory) {
+ $list = @ftp_nlist($this->connection, $directory);
+ if (FALSE === $list) {
+ throw new FtpException('Failed to list FTP directory contents.');
+ }
+ $names = array();
+ foreach ($list as $path) {
+ $names[] = basename($path);
+ }
+ return $names;
+ }
+
+ /**
+ * Lists all files and subfolders in a directory on FTP.
+ *
+ * @param string $directory
+ * A directory on FTP.
+ *
+ * @return string[]
+ * Format: $[$name] = $path
+ */
+ function lsAbsolute($directory) {
+ $list = @ftp_nlist($this->connection, $directory);
+ if (FALSE === $list) {
+ throw new FtpException('Failed to list FTP directory contents.');
+ }
+ $paths = array();
+ foreach ($list as $path) {
+ $paths[basename($path)] = $path;
+ }
+ return $paths;
+ }
+
+ /**
+ * Downloads a file from FTP to the temporary directory.
+ *
+ * @param string $pathOnFtp
+ * Filename on FTP.
+ * @param string $destination
+ * Destination file path.
+ *
+ * @return string
+ * Filename of a temporary file, that is a copy of the file from FTP.
+ *
+ * @throws \Drupal\arte_feeds_afp\FileException
+ * @throws \Drupal\arte_feeds_afp\Ftp\FtpException
+ */
+ function fileDownload($pathOnFtp, $destination) {
+
+ $fp = @fopen($destination, 'w');
+ if (!$fp) {
+ $pathinfo = pathinfo($destination);
+ if (!is_dir($pathinfo['dirname'])) {
+ throw new FileException(
+ format_string(
+ 'Directory "%directory" for destination file "%file" does not exist.',
+ array('%file' => $destination, '%directory' => $pathinfo['dirname'])));
+ }
+ throw new FileException(
+ format_string(
+ 'Unable to open destination file "%file" for writing.',
+ array('%file' => $destination)));
+ }
+
+ if (!@ftp_fget($this->connection, $fp, $pathOnFtp, FTP_BINARY)) {
+ throw new FtpException(
+ format_string(
+ 'Unable to download file from FTP : "%file"',
+ array('%file' => $pathOnFtp)));
+ }
+ @fclose($fp);
+
+ return $destination;
+ }
+
+ /**
+ * Checks if a path points to a non-empty file on FTP.
+ *
+ * @param string $path
+ * Path on FTP.
+ *
+ * @return bool
+ * TRUE, if $path is a non-empty file.
+ * FALSE, if $path does not exist or is a directory.
+ */
+ function pathIsNonEmptyFile($path) {
+ $size = @ftp_size($this->connection, $path);
+ if (-1 === $size || !is_numeric($size)) {
+ // Failed to determine file size, or $path is a directory.
+ return FALSE;
+ }
+ if ($size <= 0) {
+ // The file is empty.
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ /**
+ * @param string $filename
+ *
+ * @return int
+ *
+ * @throws FtpException
+ */
+ function fileGetSize($filename) {
+ $size = @ftp_size($this->connection, $filename);
+ if (-1 === $size || !is_numeric($size)) {
+ throw new FtpException('Failed to determine file size.');
+ }
+ return $size;
+ }
+
+}
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/src/Ftp/FtpException.php b/modules/custom/arte_afp/modules/arte_feeds_afp/src/Ftp/FtpException.php
new file mode 100644
index 0000000..ba282f1
--- /dev/null
+++ b/modules/custom/arte_afp/modules/arte_feeds_afp/src/Ftp/FtpException.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace Drupal\arte_feeds_afp\Ftp;
+
+class FtpException extends \RuntimeException {
+
+}
diff --git a/modules/custom/arte_afp/modules/arte_feeds_afp/src/Ftp/FtpSettings.php b/modules/custom/arte_afp/modules/arte_feeds_afp/src/Ftp/FtpSettings.php
new file mode 100644
index 0000000..0486e04
--- /dev/null
+++ b/modules/custom/arte_afp/modules/arte_feeds_afp/src/Ftp/FtpSettings.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace Drupal\arte_feeds_afp\Ftp;
+
+class FtpSettings {
+
+ /**
+ * FTP connection parameters.
+ *
+ * @var string[]
+ */
+ protected $config;
+
+ /**
+ * @param array $config
+ *
+ * @throws \InvalidArgumentException
+ */
+ function __construct(array $config) {
+ foreach (array('server', 'port', 'username', 'password') as $key) {
+ if (empty($config[$key])) {
+ throw new \InvalidArgumentException("Incomplete FTP settings. \$config['$key'] is missing or empty.");
+ }
+ }
+ $this->config = $config;
+ }
+
+ /**
+ * Opens a connection to an FTP server.
+ *
+ * @return \Drupal\arte_feeds_afp\Ftp\FtpConnection
+ * The FTP connection as defined in $this->config.
+ *
+ * @throws \Drupal\arte_feeds_afp\Ftp\FtpException
+ */
+ public function connect() {
+ return FtpConnection::open($this->config['server'], $this->config['port'])
+ ->login($this->config['username'], $this->config['password'])
+ ->enablePassiveMode();
+ }
+
+ /**
+ * @param string $ftpDirectory
+ *
+ * @return string
+ */
+ public function dirGetUri($ftpDirectory) {
+ return 'ftp://'
+ . $this->config['username']
+ . '@' . $this->config['server']
+ . ':' . $this->config['port']
+ . '/' . $ftpDirectory;
+ }
+
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment