diff --git a/.gitattributes b/.gitattributes
index 211e61395c0d05ef445eafabed63ae5e0f85969b..b300e1761dcb079c400584692c1d146673f33117 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -2,6 +2,8 @@
 action/odt2spip_importe.php -text
 formulaires/configurer_odt2spip.html -text
 formulaires/odt2spip.html -text
+formulaires/odt2spip.php -text
+inc/odt2spip.php -text
 inc/odt2spip.xsl -text
 inc/odt2spip_generer_sortie.php -text
 inc/odt2spip_retailler_img.php -text
@@ -27,6 +29,8 @@ lang/paquet-odt2spip_fr.php -text
 /odt2spip_pipelines.php -text
 /paquet.xml -text
 prive/squelettes/contenu/configurer_odt2spip.html -text
+prive/squelettes/inclure/document2spip.html -text
+prive/squelettes/inclure/odt2spip.html -text
 tests/content_math_objet1.xml -text
 tests/content_math_objet2.xml -text
 tests/content_math_objet3.xml -text
diff --git a/action/odt2spip_importe.php b/action/odt2spip_importe.php
index a0bd057930737808071e871985dafd4be2727ac6..d97aa9347f88fcf9a6d9bc5228538dc96e51ed39 100644
--- a/action/odt2spip_importe.php
+++ b/action/odt2spip_importe.php
@@ -36,150 +36,56 @@ if (!defined('_ECRIRE_INC_VERSION')) {
  * }}
  *
  */
-function action_odt2spip_importe() {
-	global $visiteur_session;
-
-	$id_auteur = $visiteur_session['id_auteur'];
-	$arg = _request('arg');
-	$args = explode(':', $arg);
+function action_odt2spip_importe($fichier = null, $arg = null) {
+	if (is_null($arg)) {
+		$arg = _request('arg');
+	}
 
-	// le 1er élément de _request('arg') est id_rubrique=XXX ou id_article=YYY
+	// arg : id_rubrique=XXX ou id_article=YYY
 	$id_article = $id_rubrique = false;
-	$Targs = explode('=', $args[0]);
-	$objet = $Targs[0];
-	$id_objet = intval($Targs[1]);
-	$objet == 'id_rubrique' ? $id_rubrique = $id_objet : $id_article = $id_objet;
-
-	$hash = _request('hash');
-
-	$redirect = _request('redirect');
-	if ($redirect == null) {
-		$redirect = '';
+	list($objet, $id_objet) = explode('=', $arg);
+	if ($objet === 'id_rubrique') {
+		$id_rubrique = intval($id_objet);
+		$objet = 'rubrique';
+		$creer_objet = 'article';
+	} else {
+		$id_article = intval($id_objet);
+		$objet = 'article';
+		$creer_objet = false;
 	}
 
 	include_spip('inc/securiser_action');
 
-	if (($id_rubrique and !autoriser('creerarticledans', 'rubrique', $id_rubrique))
-		or
-		($id_article and !autoriser('modifier', 'article', $id_article))
+	if (
+		($id_rubrique and !autoriser('creerarticledans', 'rubrique', $id_rubrique))
+		or ($id_article and !autoriser('modifier', 'article', $id_article))
 	) {
 		die(_T('avis_non_acces_page'));
 	}
-		
-	// ss-rep temporaire specifique de l'auteur en cours: tmp/odt2spip/id_auteur/
-	// => le créer s'il n'existe pas
-	$base_dezip = _DIR_TMP . 'odt2spip/';	  // avec / final
-	if (!is_dir($base_dezip) and !sous_repertoire(_DIR_TMP, 'odt2spip')) {
-		die(_T('odtspip:err_repertoire_tmp'));
-	}
-	$rep_dezip = $base_dezip . $id_auteur . '/';
-	if (!is_dir($rep_dezip) and !sous_repertoire($base_dezip, $id_auteur)) {
-		die(_T('odtspip:err_repertoire_tmp'));
-	}
-	$rep_pictures = $rep_dezip.'Pictures/';
 
-	// paramètres de conversion de taille des images : cm -> px
-	// (en 96 dpi puisque c'est ce que semble utiliser Writer)
-	$conversion_image = 96/2.54;
-
-	// traitement d'un fichier odt envoye par $_POST
-	$fichier_zip = addslashes($_FILES['fichier_odt']['name']);
-	if ($_FILES['fichier_odt']['name'] == ''
-		or $_FILES['fichier_odt']['error'] != 0
-		or !move_uploaded_file($_FILES['fichier_odt']['tmp_name'], $rep_dezip . $fichier_zip)) {
-		die(_T('odtspip:err_telechargement_fichier'));
+	include_spip('inc/odt2spip');
+	try {
+		$fichier = odt2spip_deplacer_fichier_upload('fichier_odt');
+	} catch (\Exception $e) {
+		die();
 	}
 
-	// dezipper le fichier odt a la mode SPIP
-	include_spip('inc/pclzip');
-	$zip = new PclZip($rep_dezip . $fichier_zip);
-	$ok = $zip->extract(
-		PCLZIP_OPT_PATH,
-		$rep_dezip,
-		PCLZIP_OPT_SET_CHMOD,
-		_SPIP_CHMOD,
-		PCLZIP_OPT_REPLACE_NEWER
+	list($id, $erreurs) = odt2spip_integrer_fichier(
+		$fichier,
+		$objet,
+		$id_objet,
+		$creer_objet,
+		array(
+			'attacher_fichier' => _request('attacher_odt'),
+		)
 	);
-	if ($zip->error_code < 0) {
-		spip_log('charger_decompresser erreur zip ' .
-				$zip->error_code . ' pour fichier ' . $rep_dezip . $fichier_zip);
-		die($zip->errorName(true));	 //$zip->error_code
-	}
-
-	// Création de l'array avec les parametres de l'article:
-	// c'est ici que le gros de l'affaire se passe!
-	$odt2spip_generer_sortie = charger_fonction('odt2spip_generer_sortie', 'inc');
-	$Tarticle = $odt2spip_generer_sortie($id_auteur, $rep_dezip);
 
-	// si necessaire créer l'article
-	include_spip('action/editer_article');
-	if (!$id_article) {
-		$id_article = article_inserer($id_rubrique);
+	if (!$id) {
+		die($erreurs);
 	}
-	
-	// le remplir
-	article_modifier($id_article, $Tarticle);
 
-	// si necessaire recup les id_doc des images associées et les lier à l'article
-	if (isset($Tarticle['Timages']) and count($Tarticle['Timages']) > 0) {
-		foreach ($Tarticle['Timages'] as $id_img) {
-			$champs = array(
-				'parents' => array("article|$id_article"),
-				'statut' => 'publie'
-			);
-			document_modifier($id_img, $champs);
-		}
+	if (is_null(_request('redirect'))) {
+		include_spip('inc/headers');
+		redirige_par_entete(generer_url_entite($id, $creer_objet ? $creer_objet : $objet));
 	}
-	
-	// si nécessaire attacher le fichier odt original à l'article
-	// et lui mettre un titre signifiant
-	if (_request('attacher_odt') == '1') {
-		$titre = $Tarticle['titre'];
-		if (!isset($ajouter_documents)) {
-			$ajouter_documents = charger_fonction('ajouter_documents', 'action');
-		}
-		$id_document = $ajouter_documents(
-			'new',
-			array(
-				array(
-					'tmp_name' =>  $rep_dezip . $fichier_zip,
-					'name' => $fichier_zip,
-					'titrer' => 0,
-					'distant' => 0,
-					'type' => 'document'
-				),
-			),
-			'article',
-			$id_article,
-			'document'
-		);
-		if (
-			$id_document
-			and $id_doc_odt = intval($id_document[0])
-			and $id_doc_odt == $id_document[0]
-		) {
-			$c = array(
-				'titre' => $titre,
-				'descriptif' => _T('odtspip:cet_article_version_odt'),
-				'statut' => 'publie'
-			);
-			document_modifier($id_doc_odt, $c);
-		}
-	}
-
-	// vider le contenu du rep de dezippage
-	if (!function_exists('effacer_repertoire_temporaire')) {
-		include_spip('inc/getdocument');
-	}
-	effacer_repertoire_temporaire($rep_dezip);
-	
-	// aller sur la page de l'article qui vient d'être créée
-	redirige_par_entete(
-		parametre_url(
-			str_replace('&amp;', '&', urldecode($redirect)),
-			'id_article',
-			$id_article,
-			'&'
-		)
-	);
 }
diff --git a/formulaires/odt2spip.html b/formulaires/odt2spip.html
index 22889f7e01a7ae8da44a3b7d3ee25f75d5e54d7b..3017b58ffb8f2df54eabd9fc2d476da1a461a53f 100644
--- a/formulaires/odt2spip.html
+++ b/formulaires/odt2spip.html
@@ -1,49 +1,41 @@
-#CACHE{0}
-<div id="boite_odt2spip" class="formulaire_spip formulaire_odt2spip" style="">
-	<style type="text/css">#boite_odt2spip label { display: inline; }</style>
-	[(#ENV{exec}|=={rubrique}|oui)
-		<h3 class="titrem"><:odtspip:importer_fichier:></h3>
-		[<img class="cadre-icone" width="24" height="24" alt="" src="(#CHEMIN{odt2spip-24.png})"/>]
-		<form action="#URL_ACTION_AUTEUR{'odt2spip_importe','id_rubrique='#ENV{id_rubrique},#URL_ECRIRE{article}}" method="POST" enctype="multipart/form-data">
-	]
-	[(#ENV{exec}|=={article}|oui)
-		<h3 class="titrem"><:odtspip:remplacer_article:></h3>
-		[<img class="cadre-icone" width="24" height="24" alt="" src="(#CHEMIN{odt2spip-24.png})"/>]
-		<form action="#URL_ACTION_AUTEUR{'odt2spip_importe','id_article='#ENV{id_article},#URL_ECRIRE{article}}" method="POST" enctype="multipart/form-data">
-	]	
-		<ul>
-			<li>
-				<label for='fichier_odt'><strong><:odtspip:choix_fichier:></strong></label>
-				<input class="file" type='file' name='fichier_odt' id='fichier_odt' size="16" />
-			</li>
-			<li>
-				<strong class="explications"><:odtspip:attacher_fichier_odt:></strong>
-				<label for='attacher_oui'><:odtspip:oui:>
-				<input type='radio' name='attacher_odt' value='1' id='attacher_oui'[ (#CONFIG{odt2spip/defaut_attacher}|=={oui}|oui)checked='checked'] />
-				</label>
-				<label for='attacher_non'>
-				<input type='radio' name='attacher_odt' value='0' id='attacher_non'[ (#CONFIG{odt2spip/defaut_attacher}|=={non}|oui)checked='checked'] />
-				<:odtspip:non:></label>
-			</li>
-			<li>
-				<strong><:odtspip:images_mode_document:></strong>
-				<label for='mode_image'><:odtspip:mode_image:>
-				<input type='radio' name='mode_image' value='image' id='mode_image' checked='checked'/></label>
-				<label for='mode_document'><input type='radio' name='mode_image' value='document' id='mode_document'/>
-				<:odtspip:mode_document:></label>
-			</li>
-			<li>
-				<strong><:odtspip:langue_publication:></strong>
-				#SET{lang_ec,#EVAL{$GLOBALS['meta']['langue_site'];}}
-				<select name='lang_publi' id='lang_publi' style='font-size:1em;'>
-					<BOUCLE_langues(DATA){liste #EVAL{$GLOBALS['meta']['langues_proposees'];}}>
-					<option value="#VALEUR"[ (#VALEUR|=={#GET{lang_ec}}|oui) selected="selected"]>#VALEUR</option>
-					</BOUCLE_langues>
-				</select>	
-			</li>
-		</ul>
+<div  class="formulaire_spip formulaire_#FORM">
+
+	[(#ENV{objet}|=={rubrique}|oui)<h3 class="titrem"><:odtspip:importer_fichier:></h3>]
+	[(#ENV{objet}|!={rubrique}|oui)<h3 class="titrem"><:odtspip:remplacer_article:></h3>]
+
+	[<img class="cadre-icone" width="24" height="24" alt="" src="(#CHEMIN{odt2spip-24.png})"/>]
+
+	[<p class="reponse_formulaire reponse_formulaire_ok">(#ENV*{message_ok})</p>]
+	[<p class="reponse_formulaire reponse_formulaire_erreur">(#ENV*{message_erreur})</p>]
+
+<BOUCLE_editable(CONDITION){si #EDITABLE}>
+	<form action="#ENV{action}" method="POST" enctype="multipart/form-data"><div>
+		#ACTION_FORMULAIRE
+		<div class="editer-groupe">
+			[(#PLUGIN{bigup}|oui)
+				[(#SAISIE_FICHIER{bigup, fichier, label=<:odtspip:choix_fichier:>, accept=#ENV{_accept}, obligatoire=oui})]
+			][(#PLUGIN{bigup}|non)
+				[(#SAISIE{input, fichier, type=file, label=<:odtspip:choix_fichier:>, accept=#ENV{_accept}, obligatoire=oui})]
+			]
+
+			[(#SAISIE{case, attacher_fichier, label_case=<:odtspip:attacher_fichier_odt:>})]
+
+			[(#SAISIE{radio, mode_image,
+				label=<:odtspip:images_mode_document:>,
+				datas=#ARRAY{image,<:odtspip:mode_image:>,document,<:odtspip:mode_document:>},
+			})]
+
+			[(#SAISIE{selecteur_langue, lang_publi, label=<:odtspip:langue_publication:>})]
+		</div>
+
 		<p class="boutons">
-			<input type="submit" name="Valider" value="<:bouton_valider:>" class="submit" />
+			<input type="submit" name="Valider" value="<:bouton_valider|attribut_html:>" class="submit" />
 		</p>
-	</form>
+	</div></form>
+</BOUCLE_editable>
 </div>
+<style type="text/css">
+	.lat .formulaire_#FORM .editer.editer_fichier label {
+		display:none;
+	}
+</style>
diff --git a/formulaires/odt2spip.php b/formulaires/odt2spip.php
new file mode 100644
index 0000000000000000000000000000000000000000..264ed3dd04ae355e829964ab4d88995cdb91cfe4
--- /dev/null
+++ b/formulaires/odt2spip.php
@@ -0,0 +1,114 @@
+<?php
+
+if (!defined('_ECRIRE_INC_VERSION')) {
+	return;
+}
+
+/**
+ * Charger le contenu d’un document ODT dans un objet SPIP.
+ *
+ * @param string $objet
+ * @param int $id_objet
+ * @param null|string $creer_objet
+ *     Si définie, créera un nouvel objet enfant de $objet/$id_objet.
+ *     Note que seul le cas 'article' dans une rubrique est actuellement géré.
+ * @param string $retour
+ * @return array|false
+ */
+function formulaires_odt2spip_charger_dist($objet, $id_objet, $creer_objet = null, $retour = '') {
+	include_spip('inc/config');
+
+	if ($creer_objet and !autoriser('creer' . objet_type($creer_objet) . 'dans', $objet, $id_objet)) {
+		return false;
+	} elseif (!autoriser('modifier', $objet, $id_objet)) {
+		return false;
+	}
+
+	$valeurs = array(
+		'objet' => $objet,
+		'id_objet' => $id_objet,
+		'creer_objet' => $creer_objet,
+		'attacher_fichier' => lire_config('odt2spip/defaut_attacher'),
+		'mode_image' => 'image',
+		// interne.
+		'_bigup_rechercher_fichiers' => true,
+		'_accept' => '.odt',
+	);
+
+	return $valeurs;
+}
+
+
+/**
+ * Vérifier
+ *
+ * @param string $objet
+ * @param int $id_objet
+ * @param null|string $creer_objet
+ *     Si définie, créera un nouvel objet enfant de $objet/$id_objet.
+ *     Note que seul le cas 'article' dans une rubrique est actuellement géré.
+ * @param string $retour
+ * @return array
+ */
+function formulaires_odt2spip_verifier_dist($objet, $id_objet, $creer_objet = null, $retour = '') {
+	$erreurs = array();
+
+	if (!in_array(_request('mode_image'), array('image', 'document'))) {
+		$erreurs['mode_image'] = _T('info_obligatoire');
+	}
+
+	if (empty($_FILES['fichier']['name'])) {
+		$erreurs['fichier'] = _T('info_obligatoire');
+	} elseif ($_FILES['fichier']['error'] != 0) {
+		$erreurs['fichier'] = _L('Un problème est survenu pour récupérer le fichier');
+	} elseif (pathinfo($_FILES['fichier']['name'], PATHINFO_EXTENSION) !== 'odt') {
+		$erreurs['fichier'] = _L('Le fichier doit être au format .odt');
+	}
+
+	return $erreurs;
+}
+
+
+/**
+ * Traiter
+ *
+ * @param string $objet
+ * @param int $id_objet
+ * @param null|string $creer_objet
+ *     Si définie, créera un nouvel objet enfant de $objet/$id_objet.
+ *     Note que seul le cas 'article' dans une rubrique est actuellement géré.
+ * @param string $retour
+ * @return array
+ */
+function formulaires_odt2spip_traiter_dist($objet, $id_objet, $creer_objet = null, $retour = '') {
+	$res = array(
+		'editable' => true,
+	);
+
+	include_spip('inc/odt2spip');
+	try {
+		$fichier = odt2spip_deplacer_fichier_upload('fichier');
+	} catch (\Exception $e) {
+		$res['message_erreur'] = $e->getMessage();
+		return $res;
+	}
+
+	list($id, $erreurs) = odt2spip_integrer_fichier(
+		$fichier,
+		$objet,
+		$id_objet,
+		$creer_objet,
+		array(
+			'attacher_fichier' => _request('attacher_fichier'),
+		)
+	);
+
+	if (!$id) {
+		$res['message_erreur'] = $erreurs;
+		return $res;
+	}
+
+	$res['redirect'] = generer_url_entite($id, $creer_objet ? $creer_objet : $objet);
+	$res['message_ok'] = _L('Fichier pris en compte');
+	return $res;
+}
\ No newline at end of file
diff --git a/inc/odt2spip.php b/inc/odt2spip.php
new file mode 100644
index 0000000000000000000000000000000000000000..9de9287c4131ed6996a146f2d59a5fe953e44fd1
--- /dev/null
+++ b/inc/odt2spip.php
@@ -0,0 +1,222 @@
+<?php
+
+if (!defined('_ECRIRE_INC_VERSION')) {
+	return;
+}
+
+/**
+ * Retourne le répertoire de stockage des documents à traiter
+ * @return string
+ * @throws \Exception
+ */
+function odt2spip_get_repertoire_temporaire() {
+	// ss-rep temporaire specifique de l'auteur en cours: tmp/odt2spip/id_auteur/
+	// => le créer s'il n'existe pas
+	$base_dezip = _DIR_TMP . 'odt2spip/';  // avec / final
+	if (!is_dir($base_dezip) and !sous_repertoire(_DIR_TMP, 'odt2spip')) {
+		throw new \Exception(_T('odtspip:err_repertoire_tmp'));
+	}
+
+	include_spip('inc/session');
+	$id_auteur = session_get('id_auteur');
+	$rep_dezip = $base_dezip . $id_auteur . '/';
+
+	if (!is_dir($rep_dezip) and !sous_repertoire($base_dezip, $id_auteur)) {
+		throw new \Exception(_T('odtspip:err_repertoire_tmp'));
+	}
+
+	// $rep_pictures = $rep_dezip.'Pictures/';
+	return $rep_dezip;
+}
+
+/**
+ * Déplace un fichier posté dans un répertoire temporaire de travail
+ * @return string
+ * @throws \Exception
+ */
+function odt2spip_deplacer_fichier_upload($key) {
+	$rep_dezip = odt2spip_get_repertoire_temporaire();
+
+	// traitement d'un fichier envoyé par $_POST
+	if (
+		empty($_FILES[$key]['name'])
+		or $_FILES[$key]['error'] != 0
+		or !($fichier = $rep_dezip . addslashes($_FILES[$key]['name']))
+	) {
+		throw new \Exception(_T('odtspip:err_telechargement_fichier'));
+	}
+
+	include_spip('inc/documents');
+	if (!deplacer_fichier_upload($_FILES[$key]['tmp_name'], $fichier, true)) {
+		throw new \Exception(_T('odtspip:err_telechargement_fichier'));
+	}
+
+	return $fichier;
+}
+
+/**
+ * Dézippe un fichier dans le répertoire temporaire d’odt2spip
+ * @param string $fichier Chemin du fichier ODT
+ * @return bool
+ * @throws \Exception
+ */
+function odt2spip_deziper_fichier($fichier) {
+	$rep_dezip = odt2spip_get_repertoire_temporaire();
+
+	// dezipper le fichier odt a la mode SPIP
+	include_spip('inc/pclzip');
+	$zip = new \PclZip($fichier);
+	$ok = $zip->extract(
+		PCLZIP_OPT_PATH,
+		$rep_dezip,
+		PCLZIP_OPT_SET_CHMOD,
+		_SPIP_CHMOD,
+		PCLZIP_OPT_REPLACE_NEWER
+	);
+
+	if ($zip->error_code < 0) {
+		spip_log('charger_decompresser erreur zip ' . $zip->error_code . ' pour fichier ' . $fichier, 'odt2spip.' . _LOG_ERREUR);
+		throw new \Exception($zip->errorName(true));
+	}
+
+	return ($ok > 0);
+}
+
+/**
+ * Intègre le contenu du fichier dans l’objet indiqué (ou un nouvel enfant)
+ *
+ * @param string $fichier
+ * @param string $objet
+ * @param int $id_objet
+ * @param string $objet_dest Nouvel objet enfant, si indiqué
+ * @param array $options {
+ *     @var bool attacher_fichier
+ * }
+ * @return array {
+ *     @var bool|int $id_objet ou fales,
+ *     @var string|null $errors,
+ * }
+ */
+function odt2spip_integrer_fichier($fichier, $objet, $id_objet, $objet_dest = '', $options = array()) {
+	list($champs, $erreurs) = odt2spip_analyser_fichier($fichier);
+	if ($erreurs) {
+		return array(false, $erreurs);
+	}
+	// si necessaire créer l'objet
+	if ($objet_dest) {
+		include_spip('action/editer_objet');
+		$id_objet = objet_inserer($objet_dest, $id_objet);
+		$objet = $objet_dest;
+		if (!$id_objet) {
+			return array(false, _L('Impossible de créer le nouvel objet'));
+		}
+	}
+
+	odt2spip_objet_modifier($fichier, $objet, $id_objet, $champs, $options);
+
+	// vider le contenu du rep de dezippage
+	include_spip('inc/getdocument');
+	effacer_repertoire_temporaire(odt2spip_get_repertoire_temporaire());
+
+	// identifiant d’objet créé éventuellement.
+	return array($id_objet, null);
+}
+
+/**
+ * Analyse le fichier ODT transmis
+ * @param string $fichier Chemin vers le fichier ODT
+ * @return array
+ */
+function odt2spip_analyser_fichier($fichier) {
+	try {
+		if (!odt2spip_deziper_fichier($fichier)) {
+			return array(false, _L('Impossible de décompresser le fichier'));
+		}
+	} catch (\Exception $e) {
+		return array(false, _L('Impossible de décompresser le fichier'));
+	}
+
+	try {
+		$rep_dezip = odt2spip_get_repertoire_temporaire();
+	} catch (\Exception $e) {
+		return array(false, _L('Impossible d’attribuer un répertoire temporaire'));
+	}
+
+	// Création de l'array avec les parametres de l'article:
+	// c'est ici que le gros de l'affaire se passe!
+	$odt2spip_generer_sortie = charger_fonction('odt2spip_generer_sortie', 'inc');
+	try {
+		$champs = $odt2spip_generer_sortie($rep_dezip, $fichier);
+	} catch (\Exception $e) {
+		spip_log($e->getMessage(), 'odt2spip.' . _LOG_ERREUR);
+		return array(false, _L('Erreur lors de l’analyse du fichier ODT.'));
+	}
+
+	return array($champs, null);
+}
+
+/**
+ * Modifie le contenu d’un objet avec les champs indiqués
+ *
+ * Note qu’une clé contient la liste des images.
+ *
+ * @param string $fichier
+ * @param string $objet
+ * @param int $id_objet
+ * @param array $set
+ * @param array $options
+ * @return bool
+ */
+function odt2spip_objet_modifier($fichier, $objet, $id_objet, $set, $options = array()) {
+
+	// le remplir
+	include_spip('action/editer_objet');
+	objet_modifier($objet, $id_objet, $set);
+
+	// si necessaire recup les id_doc des images associées et les lier à l'article
+	if (!empty($set['Timages']) > 0) {
+		foreach ($set['Timages'] as $id_img) {
+			$champs = array(
+				'parents' => array($objet . '|' . $id_objet),
+				'statut' => 'publie'
+			);
+			document_modifier($id_img, $champs);
+		}
+	}
+
+	// si nécessaire attacher le fichier odt à l'article
+	// et lui mettre un titre signifiant
+	if (!empty($options['attacher_fichier'])) {
+		$titre = $set['titre'];
+		$ajouter_documents = charger_fonction('ajouter_documents', 'action');
+		$id_document = $ajouter_documents(
+			'new',
+			array(
+				array(
+					'tmp_name' =>  $fichier,
+					'name' => basename($fichier),
+					'titrer' => 0,
+					'distant' => 0,
+					'type' => 'document'
+				),
+			),
+			$objet,
+			$id_objet,
+			'document'
+		);
+		if (
+			$id_document
+			and $id_doc_odt = intval($id_document[0])
+			and $id_doc_odt == $id_document[0]
+		) {
+			$c = array(
+				'titre' => $titre,
+				'descriptif' => _T('odtspip:cet_article_version_odt'),
+				'statut' => 'publie'
+			);
+			document_modifier($id_doc_odt, $c);
+		}
+	}
+
+	return true;
+}
\ No newline at end of file
diff --git a/inc/odt2spip_generer_sortie.php b/inc/odt2spip_generer_sortie.php
index 21b85860c5dcaa71b081dd36b4622496d6e07f17..003b3dfaad2aed27c2559882be3b5d43a1397b6b 100644
--- a/inc/odt2spip_generer_sortie.php
+++ b/inc/odt2spip_generer_sortie.php
@@ -26,14 +26,14 @@ if (!defined('_ECRIRE_INC_VERSION')) {
  * intertitre_enrichis. Les images sont extraites du document .odt et sont prêtes
  * à être insérées dans le futur article SPIP.
  *
- * @param int $id_auteur Utilisateur courant
  * @param string $rep_dezip Répertoire où est dezippé le fichier odt
- * @return Array
+ * @param string $fichier_source Chemin du fichier source (permet d’affecter un titre si le document n’en a pas trouvé)
+ * @return array Couples (nom de champ d’article => valeur)
+ * @throws \Exception
  */
-function inc_odt2spip_generer_sortie($id_auteur, $rep_dezip) {
+function inc_odt2spip_generer_sortie($rep_dezip, $fichier_source = '') {
 	// variables en dur pour xml en entree et xslt utilisee
-	// $xml_entre = $rep_dezip . 'content.xml';  // chemin du fichier xml a lire
-	$xml_entre = _DIR_TMP . 'odt2spip/' . $id_auteur . '/content.xml';  // chemin du fichier xml a lire
+	$xml_entre = $rep_dezip . 'content.xml';  // chemin du fichier xml a lire
 	$xslt_texte = _DIR_PLUGIN_ODT2SPIP . 'inc/odt2spip.xsl'; // chemin de la xslt a utiliser pour le texte
 
 	// determiner si le plugin enluminure_typo ou intertitres_enrichis est present & actif
@@ -67,7 +67,7 @@ function inc_odt2spip_generer_sortie($id_auteur, $rep_dezip) {
 	// on est php5: utiliser les fonctions de la classe XSLTProcessor
 	// verifier que l'extension xslt est active
 	if (!class_exists('XSLTProcessor')) {
-		die(_T('odtspip:err_extension_xslt'));
+		throw new \Exception(_T('odtspip:err_extension_xslt'));
 	}
 	$proc = new XSLTProcessor();
 
@@ -82,7 +82,7 @@ function inc_odt2spip_generer_sortie($id_auteur, $rep_dezip) {
 
 	// lancer le parseur
 	if (!$xml_sortie = $proc->transformToXml($xml)) {
-		die(_T('odtspip:err_transformation_xslt'));
+		throw new \Exception(_T('odtspip:err_transformation_xslt'));
 	}
 
 	// construire l'array des parametres de l'article
@@ -133,7 +133,7 @@ function inc_odt2spip_generer_sortie($id_auteur, $rep_dezip) {
 
 	// si malgré toutes les magouille xslt la balise  <titre> est vide, mettre le nom du fichier odt
 	if ($Tarticle['titre'] == '') {
-		$Tarticle['titre'] = str_replace(array('_', '-', '.odt'), array(' ', ' ', ''), $fichier_zip);
+		$Tarticle['titre'] = str_replace(array('_', '-', '.odt'), array(' ', ' ', ''), basename($fichier_source));
 	}
 
 	// traiter les images: dans tous les cas il faut les integrer dans la table documents
diff --git a/lang/odtspip_fr.php b/lang/odtspip_fr.php
index 4c6cfdbf49865a15caa35db50eb995624d900f2d..120e57bffca6b1cffe23299769d413c66356d207 100644
--- a/lang/odtspip_fr.php
+++ b/lang/odtspip_fr.php
@@ -14,46 +14,50 @@
 
 $GLOBALS[$GLOBALS['idx_lang']] = array(
 	// A
-	'attacher_fichier_odt' => 'Attacher le fichier ODT &agrave; l\'article?',
-  
+	'attacher_fichier_odt' => 'Attacher le fichier ODT à l’article ?',
+	'attacher_fichier_source' => 'Attacher le fichier à l’article ?',
+	'attacher_fichier_odt_genere' => 'Attacher le fichier ODT généré à l’article ?',
+
 	// C
-	'choix_fichier' => 'Fichier ODT &agrave; utiliser : ',
+	'choix_fichier' => 'Fichier ODT à utiliser : ',
+	'choix_fichier_source' => 'Fichier source',
 	'label_defaut_attacher' => 'Par défaut le champ "Attacher le fichier..." est coché',
 	'titre_page_configurer' => 'Configuration du plugin odt2spip',
 
-    // D
-    'defaut_attacher' => 'Choix par défaut pour l\'attachement du fichier ODT',
-    
+	// D
+	'defaut_attacher' => 'Choix par défaut pour l’attachement du fichier ODT',
 
     // E
-	'err_enregistrement_fichier_sortie' => 'Erreur lors de l\'enregistrement du fichier de snippet ',
-	'err_extension_xslt'=> 'Les fonctions XSLT ne sont pas operationnelles : demandez l\'activation des extensions XSL de PHP &agrave votre administrateur de serveur',
-	'err_import_snippet' => 'Erreur lors de la cr&eacute;ation de l\'article par le plugin Snippets. V&eacute;rifiez que celui-ci est correctement install&eacute; et activ&eacute;.',
-	'err_repertoire_tmp' => 'Erreur: le dossier tmp/odt2spip ou son sous-dossier /id_auteur n\'a pu &ecirc;tre cr&eacute;&eacute;',
-	'err_telechargement_fichier' => 'Erreur: le fichier t&eacute;l&eacute;charg&eacute; n\'a pu &ecirc;tre r&eacute;cup&eacute;r&eacute;',
+	'err_enregistrement_fichier_sortie' => 'Erreur lors de l’enregistrement du fichier de snippet ',
+	'err_extension_xslt'=> 'Les fonctions XSLT ne sont pas operationnelles : demandez l’activation des extensions XSL de PHP à votre administrateur de serveur',
+	'err_import_snippet' => 'Erreur lors de la création de l’article par le plugin Snippets. Vérifiez que celui-ci est correctement installé et activé.',
+	'err_repertoire_tmp' => 'Erreur: le dossier tmp/odt2spip ou son sous-dossier /id_auteur n’a pu être créé',
+	'err_telechargement_fichier' => 'Erreur: le fichier téléchargé n’a pu être récupéré',
 	'err_transformation_xslt' => 'Erreur lors de la transformation XSLT du fichier ODT',
 	'err_transformation_xslt_mathml' => 'Erreur XSLT lors de la conversion du MathML',
 
     // I
-	'images_mode_document' => 'Images attach&eacute;es en mode:',
+	'images_mode_document' => 'Images attachées en mode:',
 	'mode_image' => 'images',
-	'importer_fichier' => 'Cr&eacute;er un article &agrave; partir d\'un fichier ODT',
-        
-    // L
-    'langue_publication' => 'Langue de l\'article', 
-    
-    // M
-    'mode_document' => 'documents',
+	'importer_fichier' => 'Créer un article à partir d’un fichier ODT',
+	'importer_fichier_source' => 'Créer un article à partir d’un fichier',
+
+	// L
+	'langue_publication' => 'Langue de l’article',
+
+	// M
+	'mode_document' => 'documents',
 
 	// N
 	'non' => 'non',
-		
+
 	// O
 	'oui' => 'oui',
-	
+
 	// R
-	'remplacer_article' => 'Remplacer l\'article par le contenu d\'un fichier ODT',
-	
+	'remplacer_article' => 'Remplacer l’article par le contenu d’un fichier ODT',
+	'remplacer_article_source' => 'Remplacer l’article par le contenu d’un fichier',
+
 	// T
 	'cet_article_version_odt' => 'Version .odt de cet article' 
 	
diff --git a/odt2spip_pipelines.php b/odt2spip_pipelines.php
index 384a72102777639342bd9c451291012dfcb5f891..ce4692192c9ac7bb0781757d97d2fd15dc3189ea 100644
--- a/odt2spip_pipelines.php
+++ b/odt2spip_pipelines.php
@@ -28,18 +28,46 @@ if (!defined('_ECRIRE_INC_VERSION')) {
  */
 function odt2spip_affiche_gauche($flux) {
 	if (
-		$flux['args']['exec']=='rubrique'
+		$flux['args']['exec'] == 'rubrique'
 		and $id_rubrique = $flux['args']['id_rubrique']
 		and autoriser('ecrire')
 	) {
-		$out = recuperer_fond('formulaires/odt2spip', array('id_rubrique' => $id_rubrique, 'exec' => 'rubrique'));
+		$out = recuperer_fond(
+			'prive/squelettes/inclure/odt2spip',
+			array(
+				'objet' => 'rubrique',
+				'id_objet' => $id_rubrique,
+				'creer_objet' => 'article'
+			)
+		);
+		/*$out .= recuperer_fond(
+			'prive/squelettes/inclure/document2spip',
+			array(
+				'objet' => 'rubrique',
+				'id_objet' => $id_rubrique,
+				'creer_objet' => 'article'
+			)
+		);*/
 		$flux['data'] .= $out;
 	} elseif (
-		$flux['args']['exec']=='article'
+		$flux['args']['exec'] == 'article'
 		and $id_article = $flux['args']['id_article']
 		and autoriser('modifier', 'article', $id_article)
 	) {
-		$out = recuperer_fond('formulaires/odt2spip', array('id_article' => $id_article, 'exec' => 'article'));
+		$out = recuperer_fond(
+			'prive/squelettes/inclure/odt2spip',
+			array(
+				'objet' => 'article',
+				'id_objet' => $id_article
+			)
+		);
+		/*$out .= recuperer_fond(
+			'prive/squelettes/inclure/document2spip',
+			array(
+				'objet' => 'article',
+				'id_objet' => $id_article
+			)
+		);*/
 		$flux['data'] .= $out;
 	}
 	return $flux;
diff --git a/paquet.xml b/paquet.xml
index a234a85149925dc2c05a5f762500ececf04dfbc5..0b46e69252bbe1cc38ebef0f13c3168f5cf7994d 100644
--- a/paquet.xml
+++ b/paquet.xml
@@ -1,14 +1,15 @@
 <paquet
 	prefix="odt2spip"
 	categorie="edition"
-	version="2.1.4"
+	version="3.0.0-dev"
 	schema="201406091738"
-	etat="test"
-	compatibilite="[3.0.0;3.2.*]"
+	etat="dev"
+	compatibilite="[3.1.0;3.2.*]"
 	logo="odt2spip-64.png"
 	documentation="https://contrib.spip.net/3001"
->	
-	<nom>odt2spip</nom> 	<!-- générateur d'articles spip à partir de fichiers odt -->
+>
+	<nom>odt2spip</nom>
+	<!-- générateur d'articles spip à partir de fichiers odt -->
 	<auteur>cy_altern</auteur>
 	<copyright>2009-2017</copyright>
 	<licence lien="http://www.gnu.org/licenses/lgpl-3.0.html">LGPL 3</licence>
@@ -16,4 +17,7 @@
 	Vasil Yaroshevich, yarosh@raleigh.ru</credit>
 
 	<pipeline nom="affiche_gauche" inclure="odt2spip_pipelines.php" />
+
+	<necessite nom="saisies" compatibilite="[2.18.0;]" />
+	<utilise nom="bigup" compatibilite="[1.0.0-dev;]" />
 </paquet>
\ No newline at end of file
diff --git a/prive/squelettes/inclure/document2spip.html b/prive/squelettes/inclure/document2spip.html
new file mode 100644
index 0000000000000000000000000000000000000000..3f5054221269d49bf67f8e07658760477c7fbb2e
--- /dev/null
+++ b/prive/squelettes/inclure/document2spip.html
@@ -0,0 +1,3 @@
+<div class="ajax">
+	#FORMULAIRE_DOCUMENT2SPIP{#ENV{objet},#ENV{id_objet},#ENV{creer_objet}}
+</div>
\ No newline at end of file
diff --git a/prive/squelettes/inclure/odt2spip.html b/prive/squelettes/inclure/odt2spip.html
new file mode 100644
index 0000000000000000000000000000000000000000..310c7d1a136ac5ed52093ceee6008241b0d95fde
--- /dev/null
+++ b/prive/squelettes/inclure/odt2spip.html
@@ -0,0 +1,3 @@
+<div class="ajax">
+	#FORMULAIRE_ODT2SPIP{#ENV{objet},#ENV{id_objet},#ENV{creer_objet}}
+</div>
\ No newline at end of file