Browse Source

Première version de la nouvelle branche de FAQ à savoir la 3.

Elle rajoute le traitements de raccourcis SPIP pour rédiger simplement une FAQ et une configuration pour des fonctionnalités supplémentaires. Par défaut, cette version repoduit le comportement de la branche 2
svn/root/tags/v3.1.0
eric@smellup.net 9 years ago
parent
commit
1902995754
  1. 12
      .gitattributes
  2. 52
      css/faq.css
  3. 12
      css/faq_dl.css
  4. BIN
      css/img/bg-dt.png
  5. BIN
      css/img/puce-dt.png
  6. 0
      faq_logo-128.png
  7. 9
      faq_options.php
  8. 113
      faq_pipelines.php
  9. 103
      formulaires/configurer_faq.html
  10. BIN
      icon/faq-32.png
  11. BIN
      icones_barre/faq-16.png
  12. BIN
      icones_barre/faq_question-16.png
  13. BIN
      icones_barre/faq_titre-16.png
  14. 6
      inc-rubrique-faq.html
  15. 28
      inc/faq_formater_tag.php
  16. 25
      inclure/faq_dl.html
  17. 8
      inclure/rubrique-faq.html
  18. 30
      lang/faq_fr.php
  19. 16
      lang/paquet-faq_fr.php
  20. 26
      paquet.xml
  21. 21
      plugin.xml
  22. 6
      prive/squelettes/contenu/configurer_faq.html
  23. BIN
      prive/themes/spip/images/faq-24.png
  24. 189
      wheels/faq.php
  25. 9
      wheels/faq.yaml

12
.gitattributes vendored

@ -1,13 +1,11 @@
* text=auto !eol
css/faq.css -text
css/img/bg-dt.png -text
css/img/faq-close.png -text
css/img/faq-open.png -text
css/img/puce-dt.png -text
/faq_logo-128.png -text
/faq_pipelines.php -text
icon/faq-128.png -text
icon/faq-32.png -text
/inc-rubrique-faq.html -text
inclure/rubrique-faq.html -text
icones_barre/faq-16.png -text
icones_barre/faq_question-16.png -text
icones_barre/faq_titre-16.png -text
js/faq.js -text
/plugin.xml -text
prive/themes/spip/images/faq-24.png -text

52
css/faq.css

@ -1,51 +1,15 @@
/* --------------------------------------------------------------
faq.css
Style des FAQ
by romy.tetue.net - 2009-2011
Styles minimaux du plugin
compatibles avec la version v2 du plugin
-------------------------------------------------------------- */
/*
Si ce n'est pas déjà fait,
commencez pas styler les dfn et dl, dt, dd,
soit en adoptant reset, framework et/ou theme CSS,
soit en copiant les lignes ci-dessous dans votre feuille de style :
dfn { font-weight: bold; font-style: italic; }
dl { margin-bottom: 1em; border-bottom: 1px solid #DDD; }
dl dt,
dl dd { padding: .5em; padding-left: 30px; }
dl dt { background: url(img/puce-dt.png) no-repeat .4em .4em; border-top: 1px solid #DDD; font-weight: bold; color: #0366CB; }
dl dd { padding-top: 0; }
*/
/* Pour le lien de retour au debut de la FAQ */
dl.faq dd a.retour { display: block; padding: .4em .5em 0 0; text-align: right; font-weight: normal; }
dl.faq {}
dl.faq dt { /*background-image: url(img/faq-open.png);*/ background-repeat: no-repeat; cursor: pointer; }
dl.faq dt.close { /*background-image: url(img/faq-close.png);*/ }
dl.faq dd {}
#faq {}
#faq .ancres { margin-bottom: 3em; border-bottom: 1px solid #DDD; }
#faq .ancres li { padding: .5em 1em .5em .1em; border-top: 1px solid #DDD; }
#faq dl { border: 0; }
#faq dl dt,
#faq dl dd { padding: .4em; background: none; border: 0; }
#faq dl dt { position: relative; padding-right: 3em; background: #DDD url(img/bg-dt.png) no-repeat left top; margin-bottom: .4em; }
#faq dl dt .retour { display: block; position: absolute; top: 0; right: 0; padding: .2em .5em 0 0; text-align: right; font-weight: normal; }
#faq dl dd { margin-bottom: 2em; padding-right: 1em; }
/* Rustine devenue inutile
dl.spip_documents,
.spip_documents dl,
.spip_documents dt,
.spip_documents dd { margin: 0 auto; padding: 0; background: 0; border: 0; }
*/
/* Pour les pictogrammes d'ouverture-fermeture des questions de la FAQ */
dl.faq.js dt { padding-left: 1.5em; background-image: url(img/faq-open.png); background-repeat: no-repeat; background-position: 0 0.25em; cursor: pointer; }
dl.faq.js dt.close { background-image: url(img/faq-close.png); }
/* Pour l'impression */
@media print { dl.faq dd { display: block !important; } }
/* end */

12
css/faq_dl.css

@ -0,0 +1,12 @@
/* --------------------------------------------------------------
faq.css
Style des Questions-Réponse
inspiré du plugin FAQ et définitions de romy.tetue.net
-------------------------------------------------------------- */
dl.faq { border: 0; }
dl.faq dt,
dl.faq dd { padding: .4em; background: inherit; border: 0; }
dl.faq dt { position: relative; padding-right: 3em; background-color: #ddd; margin-bottom: .4em; }
dl.faq dd { margin-left: 1em; margin-bottom: 2em; padding-right: 1em; border-left: 1px solid #ddd; }
dl.faq.js dt { background-position: 0 0.6em; }

BIN
css/img/bg-dt.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 B

BIN
css/img/puce-dt.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

0
icon/faq-128.png → faq_logo-128.png

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

9
faq_options.php

@ -0,0 +1,9 @@
<?php
// Sécurité
if (!defined('_ECRIRE_INC_VERSION')) return;
// Ajout des raccourcis dans la liste des wheels
$GLOBALS['spip_wheels']['raccourcis'][] = 'faq.yaml';
?>

113
faq_pipelines.php

@ -1,24 +1,111 @@
<?php
function faq_css(){
$css ="";
$css .= '<link rel="stylesheet" href="'.find_in_path('css/faq.css').'" type="text/css" media="all" />';
return $css;
// Sécurité
if (!defined('_ECRIRE_INC_VERSION')) return;
function faq_insert_head_css($flux){
// On inclut systématiquement les CSS de base
$flux .= '<link rel="stylesheet" href="'.find_in_path('css/faq.css').'" type="text/css" media="all" />';
// On ajoute si la config le demande les CSS propres aux dl
include_spip('inc/config');
$charger_css = lire_config('faq/charger_css') ? true : false;
if ($charger_css)
$flux .= '<link rel="stylesheet" href="'.find_in_path('css/faq_dl.css').'" type="text/css" media="all" />';
return $flux;
}
function faq_insert_head($flux) {
if (intval($GLOBALS['spip_version_branche'])<3){
$flux .= faq_css();
}
$flux .= '<script src="'.find_in_path('js/faq.js').'" type="text/javascript"></script>';
function faq_insert_head($flux){
include_spip('inc/config');
$charger_js = lire_config('faq/charger_js') ? true : false;
if ($charger_js)
$flux .= '<script src="'.find_in_path('js/faq.js').'" type="text/javascript"></script>';
return $flux;
}
function faq_insert_head_css($flux) {
if (intval($GLOBALS['spip_version_branche'])>=3){
$flux .= faq_css();
function faq_porte_plume_barre_pre_charger($barres){
// on ajoute les boutons dans la barre d'édition seulement
foreach (array('edition') as $nom) {
$barre = &$barres[$nom];
$barre->ajouterPlusieursApres('grpCaracteres', array(
array(
"id" => "faq_sep",
"separator" => "---------------",
"display" => true,
),
array(
"id" => 'faq',
"name" => _T('faq:outil_inserer_faq'),
"className" => 'outil_faq',
"openBlockWith" => "<faq>\n",
"closeBlockWith" => "\n</faq>",
"replaceWith" => "function(h){ return outil_faq(h, '?', true);}",
"selectionType" => "line",
"display" => true,
"dropMenu" => array(
// bouton ?
array(
"id" => 'faq_question',
"name" => _T('faq:outil_inserer_question'),
"replaceWith" => "function(h){ return outil_faq(h, '?');}",
"className" => 'outil_faq_question',
"selectionType" => "line",
"forceMultiline" => true,
"display" => true,
),
array(
"id" => 'faq_titre',
"name" => _T('faq:outil_inserer_titre'),
"replaceWith" => "function(h){ return outil_faq(h, ':Nouveau titre');}",
"className" => 'outil_faq_titre',
"selectionType" => "line",
"forceMultiline" => true,
"display" => true,
),
)
)
));
$barre->ajouterFonction("function outil_faq(h, c,recursif) {
if(recursif){
// Cas de la sélection de click sur le bouton de création de faq complète
s = h.selection;
lines = h.selection.split(/\\r?\\n/);
var lines_final = [];
for (j = 0, n = lines.length, i = 0; i < n; i++) {
// si une seule ligne, on se fiche de savoir qu'elle est vide,
// c'est volontaire si on clique le bouton
if (n == 1 || $.trim(lines[i]) !== '') {
if(r = lines[i].match(/^([+-o]) (.*)$/)){
r[1] = r[1].replace(/[+-o]/g, c);
lines_final[j] = r[1]+' '+r[2];
j++;
} else {
lines_final[j] = c + ' '+lines[i];
j++;
}
}
}
return lines_final.join('\\n');
}
// Click sur les autres boutons
if ((s = h.selection) && (r = s.match(/^([+-o]) (.*)$/))){
r[1] = r[1].replace(/[+-o]/g, c);
s = r[1]+' '+r[2];
} else {
s = c + ' '+s;
}
return s;
}");
}
return $flux;
return $barres;
}
function faq_porte_plume_lien_classe_vers_icone($flux){
return array_merge($flux, array(
'outil_faq'=>'faq-16.png',
'outil_faq_question'=>'faq_question-16.png',
'outil_faq_titre'=>'faq_titre-16.png'
));
}
?>

103
formulaires/configurer_faq.html

@ -0,0 +1,103 @@
<div class="formulaire_spip formulaire_configurer formulaire_#FORM">
<img src="#CHEMIN_IMAGE{faq-24.png}" class="cadre-icone" />
[<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 #ENV{editable}}>
<form method="post" action="#ENV{action}" enctype="multipart/form-data"><div>
[(#REM) <!--declarer les hidden qui declencheront le service du formulaire parametre : url d'action -->]
#ACTION_FORMULAIRE{#ENV{action}}
<h2 class="titrem"><:faq:titre_form_configurer:></h2>
<p><:faq:info_config_faq:></p>
<ul>
<li class="fieldset">
<fieldset>
<h3 class="legend"><:faq:legende_ancre_lien:></h3>
<ul>
#SET{name,ancrer_question}#SET{obli,''}#SET{erreurs,#ENV**{erreurs}|table_valeur{#GET{name}}}
<li class="editer editer_[(#GET{name})][ (#GET{obli})][ (#GET{erreurs}|oui)erreur]">
[<span class='erreur_message'>(#GET{erreurs})</span>]
#SET{val,1}
<div class="choix">
<input type="checkbox" class="checkbox" name="#GET{name}" id="#GET{name}_#GET{val}" value="#GET{val}"[ (#GET{val}|=={#ENV{#GET{name}}}|oui)checked="checked"] />
<label for="#GET{name}_#GET{val}"><:faq:label_ancrer_question:></label>
</div>
</li>
#SET{name,lier_faq}#SET{obli,''}#SET{erreurs,#ENV**{erreurs}|table_valeur{#GET{name}}}
<li class="editer editer_[(#GET{name})][ (#GET{obli})][ (#GET{erreurs}|oui)erreur]">
[<span class='erreur_message'>(#GET{erreurs})</span>]
#SET{val,1}
<div class="choix">
<input type="checkbox" class="checkbox" name="#GET{name}" id="#GET{name}_#GET{val}" value="#GET{val}"[ (#GET{val}|=={#ENV{#GET{name}}}|oui)checked="checked"] />
<label for="#GET{name}_#GET{val}"><:faq:label_lier_faq:></label>
</div>
</li>
</ul>
</fieldset>
</li>
<li class="fieldset">
<fieldset>
<h3 class="legend"><:faq:legende_css:></h3>
<ul>
#SET{name,charger_css}#SET{obli,''}#SET{erreurs,#ENV**{erreurs}|table_valeur{#GET{name}}}
<li class="editer editer_[(#GET{name})][ (#GET{obli})][ (#GET{erreurs}|oui)erreur]">
[<span class='erreur_message'>(#GET{erreurs})</span>]
#SET{val,1}
<div class="choix">
<input type="checkbox" class="checkbox" name="#GET{name}" id="#GET{name}_#GET{val}" value="#GET{val}"[ (#GET{val}|=={#ENV{#GET{name}}}|oui)checked="checked"] />
<label for="#GET{name}_#GET{val}"><:faq:label_charger_css:></label>
</div>
</li>
</ul>
</fieldset>
</li>
<li class="fieldset">
<fieldset>
<h3 class="legend"><:faq:legende_comportement:></h3>
<ul>
#SET{name,charger_js}#SET{obli,''}#SET{erreurs,#ENV**{erreurs}|table_valeur{#GET{name}}}
<li class="editer editer_[(#GET{name})][ (#GET{obli})][ (#GET{erreurs}|oui)erreur]">
[<span class='erreur_message'>(#GET{erreurs})</span>]
#SET{val,1}
<div class="choix">
<input type="checkbox" class="checkbox" name="#GET{name}" id="#GET{name}_#GET{val}" value="#GET{val}"[ (#GET{val}|=={#ENV{#GET{name}}}|oui)checked="checked"] />
<label for="#GET{name}_#GET{val}"><:faq:label_charger_js:></label>
</div>
</li>
#SET{name,iconifier_js}#SET{obli,''}#SET{erreurs,#ENV**{erreurs}|table_valeur{#GET{name}}}
<li class="editer editer_[(#GET{name})][ (#GET{obli})][ (#GET{erreurs}|oui)erreur]" id="iconifier_js">
[<span class='erreur_message'>(#GET{erreurs})</span>]
#SET{val,1}
<div class="choix">
<input type="checkbox" class="checkbox" name="#GET{name}" id="#GET{name}_#GET{val}" value="#GET{val}"[ (#GET{val}|=={#ENV{#GET{name}}}|oui)checked="checked"] />
<label for="#GET{name}_#GET{val}"><:faq:label_iconifier_js:></label>
</div>
</li>
</ul>
</fieldset>
</li>
</ul>
[(#REM) ajouter les saisies supplementaires : extra et autre, a cet endroit ]
<!--extra-->
<p class="boutons"><input type='submit' class='submit' value='<:bouton_enregistrer:>' /></p>
</div></form>
</BOUCLE_editable>
</div>
<script type="text/javascript">
function toggle_charger_js(){
if (jQuery('#charger_js_1').prop('checked'))
jQuery('li.editer_iconifier_js').show();
else
jQuery('li.editer_iconifier_js').hide();
}
jQuery(document).ready(function(){
jQuery('#charger_js_1').bind('change',toggle_charger_js);
toggle_charger_js();
});
</script>

BIN
icon/faq-32.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

BIN
icones_barre/faq-16.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
icones_barre/faq_question-16.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
icones_barre/faq_titre-16.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

6
inc-rubrique-faq.html

@ -1,6 +0,0 @@
[(#REM)
RUSTINE DE COMPATIBILITE
Ne plus utiliser ce fichier !
]<INCLURE{fond=inclure/rubrique-faq,id_rubrique,env}>

28
inc/faq_formater_tag.php

@ -0,0 +1,28 @@
<?php
if (!defined("_ECRIRE_INC_VERSION")) return;
/**
* Transforme la chaine représentant le nom du tag en un lien vers la page du mot-clé
* le cas échéans ou renvoie la chaine fournie en entrée.
*
* @param string $valeur
* Le nom du tag qui peut coincinder avec le titre d'un mot-clé
*
* @return string
* La valeur formatée en lien vers le mot-clé ou la valeur d'entrée sinon.
*/
function inc_faq_formater_tag_dist($valeur) {
$tag = $valeur;
if ($id = sql_getfetsel('id_mot', 'spip_mots', 'titre='. sql_quote($tag))) {
include_spip('inc/utils');
$url = generer_url_entite($id, 'mots');
$tag = '<a href="' . $url . '" class="spip_in">' . $valeur . '</a>';
}
return $tag;
}
?>

25
inclure/faq_dl.html

@ -0,0 +1,25 @@
<B_faq>
[<h3 class="spip">
[<a class="ancre" href="##ENV{ancre}" name="(#ENV{ancre})" id="#ENV{ancre}"></a>]
(#ENV{titre})
</h3>]
<dl class="faq[ (#CONFIG{faq/charger_js}|et{#CONFIG{faq/iconifier_js}}|oui)js]">
<BOUCLE_faq(DATA){source table, #ENV{faq}}{par cle}>
[(#CONFIG{faq/ancrer_question}|oui)
[<a class="ancre" href="##ENV{ancre}_#CLE" name="(#ENV{ancre})_#CLE" id="#ENV{ancre}_#CLE"></a>]]
<dt>
[(#VALEUR|table_valeur{question})]
</dt>
<dd>
<BOUCLE_tags(DATA){source table, #VALEUR|table_valeur{tags}}{par valeur}>
[(#COMPTEUR_BOUCLE|=={1}|oui)<ul class="liste-tags">]
<li class="tag">#VALEUR</li>
[(#COMPTEUR_BOUCLE|=={#TOTAL_BOUCLE}|oui)</ul>]
</BOUCLE_tags>
[(#VALEUR|table_valeur{reponse})]
[(#CONFIG{faq/lier_faq}|oui)
[<a class="retour" rel="nofollow" href="#(#ENV{ancre})"><:faq:lien_debut_faq:></a>]]
</dd>
</BOUCLE_faq>
</dl>
</B_faq>

8
inclure/rubrique-faq.html

@ -1,8 +0,0 @@
<B_faq>
<dl class="faq">
<BOUCLE_faq(ARTICLES){id_rubrique}{par num titre}{par date}>
<dt id="dt#COMPTEUR_BOUCLE">#TITRE</dt>
<dd>#TEXTE</dd>
</BOUCLE_faq>
</dl>
</B_faq>

30
lang/faq_fr.php

@ -0,0 +1,30 @@
<?php
// This is a SPIP language file -- Ceci est un fichier langue de SPIP
if (!defined('_ECRIRE_INC_VERSION')) return;
$GLOBALS[$GLOBALS['idx_lang']] = array(
// I
'info_config_faq' => 'Cette configuration s\'applique aux squelettes d\'affichage des FAQ proposés par le plugin, et plus particulièrement aux listes de définitions. Si vous souhaitez définir vos propres affichages pensez à tenir compte de ces paramètres.' ,
// L
'label_charger_css' => 'Utiliser les styles proposés par défaut par le plugin pour les listes de définitions',
'label_charger_js' => 'Utiliser la fonction permettant de plier et déplier les listes de définitions',
'label_iconifier_js' => 'Ajouter des puces en début de chaque question pour matérialiser la fonction de pliage/dépliage',
'label_ancrer_question' => 'Attacher une ancre à chaque question',
'label_lier_faq' => 'Insérer un lien vers le début de la FAQ en fin de chaque réponse',
'legende_ancre_lien' => 'Ancres, liens et tables des matières',
'legende_comportement' => 'Comportement dynamique',
'legende_css' => 'Styles',
'lien_debut_faq' => 'Retour au début de la FAQ',
// O
'outil_inserer_faq' => 'Insérer une nouvelle FAQ',
'outil_inserer_question' => 'Insérer une nouvelle question',
'outil_inserer_titre' => 'Insérer un titre pour la FAQ',
// T
'titre_page_configurer' => 'Configurer le plugin Questions-Réponses',
'titre_form_configurer' => 'Affichages des FAQ',
);
?>

16
lang/paquet-faq_fr.php

@ -0,0 +1,16 @@
<?php
// This is a SPIP language file -- Ceci est un fichier langue de SPIP
if (!defined('_ECRIRE_INC_VERSION')) return;
$GLOBALS[$GLOBALS['idx_lang']] = array(
// T
'faq_description' => 'Ajoute de nouveaux raccourcis typographiques permettant de décrire de manière simple des <abbr title="Foire Aux Questions">FAQ</abbr> dans un contenu SPIP.
Les raccourcis doivent être utilisés à l\'intérieur du sélecteur &#171;<code>faq</code>&#187;. Une configuration permet d\'ajouter des ancres, de rendre la FAQ dépliable, de charger des styles par défaut...
La structure HTML produite est, par défaut, basée sur une liste de définitions.',
'faq_nom' => 'FAQ',
'faq_slogan' => 'Créer simplement une FAQ'
);
?>

26
paquet.xml

@ -0,0 +1,26 @@
<paquet
prefix="faq"
categorie="edition"
version="3.0.0"
etat="test"
compatibilite="[3.0.0;3.0.*]"
logo="faq_logo-128.png"
documentation="http://www.spip-contrib.net/?rubrique891"
>
<nom>D&#233;finitions et FAQ</nom>
<!-- Créer simplement une FAQ -->
<auteur lien="http://romy.tetue.net">romy.tetue.net</auteur>
<auteur lien="http://blog.smellup.net">Eric Lupinacci</auteur>
<licence lien="http://www.gnu.org/licenses/gpl-3.0.html">GPL 3</licence>
<copyright>2009-2013</copyright>
<necessite nom="tw" compatibilite="[0.3.1;[" />
<pipeline nom="insert_head_css" inclure="faq_pipelines.php" />
<pipeline nom="insert_head" inclure="faq_pipelines.php" />
<pipeline nom="porte_plume_lien_classe_vers_icone" inclure="faq_pipelines.php" />
<pipeline nom="porte_plume_barre_pre_charger" inclure="faq_pipelines.php" />
</paquet>

21
plugin.xml

@ -1,21 +0,0 @@
<plugin>
<nom>D&#233;finitions et FAQ</nom>
<icon>icon/faq-32.png</icon>
<prefix>faq</prefix>
<version>2.1.21</version>
<etat>dev</etat>
<categorie>navigation</categorie>
<slogan>FAQ d&#233;pliable</slogan>
<description>Transformez les listes de d&#233;finitions marqu&#233;es du s&eacute;lecteur &#171;<code>faq</code>&#187; en <abbr title="Foire Aux Questions">FAQ</abbr> d&#233;pliable.</description>
<lien>http://www.spip-contrib.net/?rubrique891</lien>
<auteur>[romy.tetue.net->http://romy.tetue.net]</auteur>
<licence>GPL 2009</licence>
<pipeline>
<nom>insert_head</nom>
<inclure>faq_pipelines.php</inclure>
</pipeline>
<necessite id="SPIP" version="[2.0.0;3.0.99]" />
</plugin>

6
prive/squelettes/contenu/configurer_faq.html

@ -0,0 +1,6 @@
[(#AUTORISER{configurer}|sinon_interdire_acces)]
<h1 class='grostitre'><:faq:titre_page_configurer:></h1>
<div class="ajax">
#FORMULAIRE_CONFIGURER_FAQ
</div>

BIN
prive/themes/spip/images/faq-24.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

189
wheels/faq.php

@ -0,0 +1,189 @@
<?php
// Regexp permettant de récupérer chacune des informations additionnelles qui peuvent compléter le titre de la tâche :
// - #tag, tag étant un mot. Exemple : #courses ou #перевод-шаблон
// - type:valeur ou type et valeur sont des mots. Pas utilisé pour l'instant
if (!defined('_FAQ_REGEXP_INFOS_COMPLEMENTAIRES'))
define('_FAQ_REGEXP_INFOS_COMPLEMENTAIRES', '%([\w-]+:|#)([\w.-]+)(?:\s|$)%Uu');
/**
* Analyse du contenu d'un bloc FAQ inclu entre les marqueurs de début (<faq>) et de fin (</faq>)
* puis appelle du squelette avec les paramètres calculés.
*
* Un bloc de FAQ peut contenir plusieurs sous-FAQ qui sont séparées par un titre. Un titre commence sur
* une nouvelle ligne avec comme premier caractère ':'.
* Une question commence sur une nouvelle ligne avec comme premier caractère '?'. La réponse est constituée des lignes
* qui suivent la question jusqu'à la prochaine question, le prochain titre ou la fin du bloc.
* Il est possible de choisir le squelette de sortie en utilisant l'attribut format dans la balise <code><faq></code>.
*
* @param array $t l'index 4 représente le contenu du bloc, l'index 3 la valeur du format si il existe.
* @return string le html généré à partir d'un squelette
*/
function tw_faq($t) {
// Numéro d'appel de la fonction tw_faq dans le hit.
// -- sert à calculer une ancre unique pour la faq ou sous-faq sans nécessiter d'id ou de titre.
static $no_bloc = 0;
// Initialisation du html calculé
$html = $t;
// Extraction de lignes du texte
// La wheel renvoie un tableau à cette callback qui est le résultat d'un preg_match_all.
// Le contenu du tableau est le suivant :
// - index 0 : la capture du pattern complet
// - index 1 : la capture de l'attribut format si il existe
// - index 2 : la capture des quotes entourant la valeur de l'attribut format
// - index 3 : la capture de la valeur de l'attribution format
// - index 4 : la capture du texte compris entre les balises <faq> et </faq>
// --> Seuls les index 3 et 4 sont utilisés.
$lignes = explode("\n", trim($t[4]));
// Initialisation des variables propres à l'ensemble des faqs du bloc
$faqs = array();
$index_faq = 0;
$index_qr = 0;
$index_ligne = 0;
$question_en_cours = false;
// Analyse de chaque ligne du bloc
while ($index_ligne <= count($lignes)){
// Initialisation des variables de la faq en cours
// (pour un bloc faq contenant plusieurs faq séparées par des titres)
if (($index_qr == 0) AND !$question_en_cours) {
$types_info[$index_faq] = array();
}
// On vérifie qu'on a atteint la fin du bloc de texte compris entre <faq> et </faq>.
// -- si c'est le cas, on ajoute la question-reponse en cours si elle existe
// -- sinon, on traite la nouvelle ligne
if ($index_ligne == count($lignes)) {
if ($question_en_cours) {
$faqs[$index_faq][$index_qr] = array(
'question' => $question,
'reponse' => trim($reponse),
'tags' => $tags,
'infos' => $infos,
);
$question_en_cours = false;
}
}
else {
// Extraction de la nouvelle ligne à traiter
$texte = trim($lignes[$index_ligne]);
if ($texte) {
// Extraction du premier caractère de la ligne qui détermine soit :
// - l'indicateur du titre de la question,
// - l'indicateur d'un titre pour la faq,
// - et sinon la réponse comme un descriptif libre de la question précédente.
// Le caractère de question '?' est traité par SPIP et précédés
// d'un '&nbsp;' parfois à cause de la typographie et il faut donc au préalable le supprimer.
if (strpos($texte, '&nbsp;') === 0) {
$texte = substr($texte, 6, strlen($texte)-6);
}
$premier = substr($texte, 0, 1);
if (($premier != '?')
AND ($premier != ':')) {
// La ligne correspond à un texte de réponse non vide si une question est en cours
if ($question_en_cours)
$reponse .= $reponse ? "\n" . $texte : $texte;
}
else {
// Il faut tester si une question est en cours. Si c'est le cas il faut clore la question en cours
// avant de commencer la nouvelle question ('?') ou la nouvelle faq par son titre (':').
if ($question_en_cours) {
$faqs[$index_faq][$index_qr] = array(
'question' => $question,
'reponse' => trim($reponse),
'tags' => $tags,
'infos' => $infos,
);
$question_en_cours = false;
$index_qr += 1;
}
if ($premier == '?') {
// On démarre une nouvelle question
// -- initialisation des variables de la question en cours
$tags = $infos = array();
$question_en_cours = true;
$reponse = '';
$texte = trim(substr($texte, 1, strlen($texte)-1));
// -- le texte de la question, que l'on sépare du reste des informations complémentaires éventuelles
if (preg_match_all(_FAQ_REGEXP_INFOS_COMPLEMENTAIRES, $texte, $infos_complementaires)) {
// Extraction du titre
$question = trim(str_replace($infos_complementaires[0], '', $texte));
// Extraction des informations complémentaires
foreach($infos_complementaires[1] as $_cle => $_prefixe) {
$type = rtrim($_prefixe, ':');
$valeur = $infos_complementaires[2][$_cle];
if ($type == '#') {
// -- les étiquettes
if ($formater = charger_fonction("faq_formater_tag", 'inc', true))
$tags[] = $formater($valeur);
else
$tags[] = $valeur;
}
else {
// -- les informations typées
if ($formater = charger_fonction("faq_formater_${type}", 'inc', true)) {
$infos[$type] = $formater($valeur);
}
else
$infos[$type] = $valeur;
if (!in_array($type, $types_info[$index_faq]))
$types_info[$index_faq][] = $type;
}
}
}
else
$question = $texte;
}
elseif ($premier == ':') {
// Titre d'une nouvelle faq incluse dans le bloc faq en cours de traitement
$index_faq += 1;
$titres[$index_faq] = trim(substr($texte, 1, strlen($texte)-1));
$index_qr = 0;
}
}
}
elseif ($question_en_cours) {
// Ligne vide. Comme elle est incluse dans le texte de la réponse on la conserve
$reponse .= $reponse ? "\n" . $texte : $texte;
}
}
$index_ligne++;
}
// Appel pour chaque FAQ du modèle demandé ou par défaut
if ($faqs) {
$html = '';
$format = $t[3] ? $t[3] : 'dl';
foreach($faqs as $_cle => $_faq) {
if ($_faq) {
$html .= recuperer_fond(
"inclure/faq_${format}",
array(
'titre' => (isset($titres[$_cle]) ? $titres[$_cle] : ''),
'ancre' => "faq_${no_bloc}_${_cle}",
'faq' => $_faq,
'types_info' => $types_info[$_cle]
),
array(
'ajax' => true
)
);
}
}
$no_bloc++;
}
return $html;
}
?>

9
wheels/faq.yaml

@ -0,0 +1,9 @@
--- # Syntaxe des raccourcis SPIP, version 0.0.1
#
# Si on nomme une regle, elle devient surchargeable
faq:
match: "%<faq([^>]*(?:format=([\"'])([^\"']+)\2)[^>]*|)>\n(.*?)\n</faq>%s"
is_callback: Y
replace: tw_faq
priority: -40
Loading…
Cancel
Save