web-dev-qa-db-fra.com

Comment obtenir une réponse Ajax dans un module?

Je suis en train de coder mon premier module/administrateur expérimental afin de pouvoir comprendre comment Joomla effectue les appels ajax. Je n'ai besoin que d'actualiser le contenu de mon module avec des informations sur le serveur et quelques appels Google Charts. Je souhaite utiliser les techniques Joomla afin de pouvoir construire plus de modules en toute confiance à l'avenir, en utilisant les meilleures pratiques.

J'ai coupé 90% de la fonctionnalité de mon projet pour isoler les points où je suis bloqué.

mod_filefinder.php

defined('_JEXEC') or die;
require_once dirname(__FILE__) . '/helper.php';
if(!defined('DS')) define('DS', DIRECTORY_SEPARATOR);
$filefinder = new FileFinder(JFactory::getApplication()->input->post->getString('path'));
require JModuleHelper::getLayoutPath('mod_filefinder');

helper.php

class FileFinder
{
    public $currentdirectory = "";
    public $subdirectories = array();
    public $js = "";
    public $html = "";
    function __construct($path = null)
    {
        if (!$path) {
            chdir('../');
            $this->currentdirectory = basename(JPATH_BASE);
            $this->subdirectories = glob($this->currentdirectory.DS."*", GLOB_ONLYDIR);
            $this->html = "container";
            $this->js = "
                    jQuery(function($) {
                        $('#path').on('change', function() {
                        var path = $(this).val();
                        console.log({path: path});
                        $.ajax({
                            url: 'index.php?option=com_ajax&module=filefinder&method=FileFinder&format=raw',  // &ignoreMessages 
                            type: 'POST',
                            async: true,
                            cache: false,
                            data: {path: path},
                            success: function(response){
                                console.log(response); 
                            }
                        });
                    });
                    $('#path').trigger('change');
                });
             ";
        } else {
            $this->currentdirectory = $path;
            $this->subdirectories = glob($this->currentdirectory.DS."*", GLOB_ONLYDIR);
        }
    }

    public function FileFinderAjax() {
        ?><script>console.log('hello');</script><?php
        echo "hello";  // how do I get Ajax to get in here?!?
    }
}

default.php

defined('_JEXEC') or die;

if ($filefinder->html == "container") { // provide container with default contents
    echo "<div id=\"filefinder_container\">";
        echo "<span>", JTEXT::_("Current Directory"), " ", $filefinder->currentdirectory, "</span>";
        echo "<select id=\"path\"><option value=\"\">Select a Subdirectory</option>";
        foreach ($filefinder->subdirectories as $subdirectory) {
            echo "<option>$subdirectory</option>";  //  value=\"$filefinder->currentdirectory".DS."$subdirectory\"
        }
    echo "</select>";
    echo "</div>";
} else {  // return only the new contents to be inserted into old container
    echo "<span>", JTEXT::_("Current Directory"), " ", $filefinder->currentdirectory, "</span>";
    echo "<select id=\"path\"><option value=\"\">Select a Subdirectory</option>";
    foreach ($filefinder->subdirectories as $subdirectory) {
        echo "<option>$subdirectory</option>";  //  value=\"$filefinder->currentdirectory".DS."$subdirectory\"
    }
    echo "</select>";
}
if ($filefinder->js) {
    JHtml::_('jquery.framework');
    $document = JFactory::getDocument();
    $document->addScriptDeclaration($filefinder->js);
}

J'ai les fichiers index.html Et mod_filefinder.xml En place, je ne les ajoute tout simplement pas à mon message parce que c'est inutile.

Pour le moment, mon fichier console.log affiche ces informations après avoir sélectionné un nouveau répertoire:

{path: "administrator\cache"}
POST http://localhost/jdem01/administrator/index.php?option=com_ajax&module=filefinder&method=FileFinder&format=raw 404 (Not Found)

J'ai aussi essayé:

  • rendant FileFinderAjax() une static fonction publique
  • et environ 50 ajustements de code hélicoïdal dont je ne me souviens plus et que je ne tiens pas à publier.

J'ai inutilement lu:

https://www.ostraining.com/blog/joomla/search-ajax/https://docs.joomla.org/Using_Joomla_Ajax_Interfacehttps://docs.joomla.org/J3.x:Creating_a_simple_module/Developing_a_Basic_ModuleEnvoi POST données dans le module - AJAXtilisation de AJAX dans un module personnalisé - comment le faire?
(... et d'innombrables autres ressources docs/forums/stackoverflow/stackexchange)
(... et toutes les "questions similaires" recommandées dans l'encadré jaune à droite de ma question)

J'utilise Joomla 3.8.6


Mise à jour après le support préliminaire de @jamesgarrett et @Lodder ...

Je ne reçois tout simplement pas la chaîne de réponse souhaitée.

Voici mes fichiers (dépouillés encore plus loin):

administrateur/modules/mod_filefinder/mod_filefinder.php

defined('_JEXEC') or die;
require_once dirname(__FILE__) . '/helper.php';    
$filefinder = new modFileFinderHelper();
require JModuleHelper::getLayoutPath('mod_filefinder');

administrateur/modules/mod_filefinder/helper.php

class modFileFinderHelper
{
    function __construct() {
        $this->currentdirectory = basename(JPATH_BASE);
    }

    public function FileFinderAjax() {
        $data = JFactory::getApplication()->input->post->getArray([]);
        $path = $data['data']['path'];
        ?><script>console.log('<?=$path?>');</script><?php
        echo "hello $path";
        JExit;
    }
}

administrateur/modules/mod_filefinder/tmpl/default.php

defined('_JEXEC') or die;
JHtml::_('jquery.framework');
JHtml::_('script', 'administrator/modules/mod_filefinder/ajax.js');

echo "<div id=\"filefinder_container\">{$filefinder->currentdirectory}</div>";

administrateur/modules/mod_filefinder/ajax.js

jQuery(function($) {
    function loadContent(path) {
        console.log({path: path});
        var request = {
            option       : 'com_ajax',
            module       : 'filefinder',  // to target: mod_filefinder
            method       : 'FileFinder',  // to target: function FileFinderAjax in class modFileFinderHelper
            format       : 'raw',
            data         : {path: path}
        };
        console.log(request);
        $.ajax({
            method: 'POST',
            data: request
        })
        .success(function(response){
            console.log('response: '+response);
        });
    }
    console.log('onload, calling ajax with default path value: '+$('#filefinder_container').html());
    loadContent($('#filefinder_container').html());

    $('#path').on('change', function() {
        console.log('change event triggered, calling ajax with path value: '+$(this).val());
        loadContent($(this).val());
    });
});

Ce sont mes données console.log:

enter image description here

Alors laissez-moi essayer de poser cette question différemment.

Quelle est la structure de fichier la plus simple qui me permettra de:

  1. Charge une structure HTML vide.
  2. Envoyer un appel AJAX) à un fichier php à usage unique (construire une chaîne json))
  3. Recevoir la réponse json et distribuer les données json dans la structure html

Simple droit? S'il vous plaît aider.

4
mickmackusa

Pour que la demande ajax mappe à l'aide de votre module, la classe doit être nommée "modFilefinderHelper"

De la documentation:

Les demandes de module doivent inclure la variable de module dans l'URL, associée au nom du module (c'est-à-dire module = session pour mod_session).

Cette valeur est également utilisée pour:

Nom du répertoire dans lequel rechercher le fichier d'assistance, par exemple. /modules/mod_session/helper.php Nom de la classe à appeler, par exemple. modSessionHelper

doc pertinente


Modifier après la mise à jour de la question

J'ai mis à jour et commenté deux des fichiers où vous rencontrez des problèmes

<?php
class modFileFinderHelper
{
    function __construct() {
        $this->currentdirectory = basename(JPATH_BASE);
    }

    // static methods only (the constructor above wont run for your ajax request)
    public static function FileFinderAjax() {
        // Here we get the data passed via ajax - note the values themselves have not been filtered!
        $data = JFactory::getApplication()->input->post->get('data',[],'array');

        // Presumably you would here do something worthwhile
        $response = [
            'something' => 12345,
            'your_path' => $data['path'],
        ];
        // Usually you can dump json and die, but there's a inbuild responder that'll encode things and die on your behalf
        // you'll see client side it also adds a few generic values like "success"
        echo new JResponseJson($response);
    }
}


jQuery(function($) {

    // grab a dom selector so we don't have to grab it each time we update
    var filefinder_container = $('#filefinder_container');

    function loadContent(path) {
        console.log({path: path});
        var request = {
            option       : 'com_ajax',
            module       : 'filefinder',  // to target: mod_filefinder
            method       : 'FileFinder',  // to target: function FileFinderAjax in class modFileFinderHelper
            format       : 'raw',
            data         : {path: path}
        };
        console.log(request);
        $.ajax({
            method: 'POST',
            data: request
        })
        .success(function(response){
            console.log('response: '+response);
            // we've got json but we need an object so let's parse it!
            response = jQuery.parseJSON(response);
            // and now we can use it like this.
            filefinder_container.text(response.data.something + " and " + response.data.your_path);
        });
    }
    console.log('onload, calling ajax with default path value: '+$('#filefinder_container').html());
    loadContent($('#filefinder_container').html());

    $('#path').on('change', function() {
        console.log('change event triggered, calling ajax with path value: '+$(this).val());
        loadContent($(this).val());
    });
});

De plus, ce n'est pas un problème avec jQuery, mais avec certains autres frameworks FE tels que angularjs ou vuesjs, vous risquez de rencontrer un problème de Joomla qui n'était pas au courant de votre entrée car il était alimenté en tant que JSON réel, et non en tant que formulaire. Pour en finir, vous voudriez ajouter quelque chose comme

$input = Factory::getApplication()->input;
$jsonInput = json_decode(file_get_contents('php://input'));
if($jsonInput){
    foreach ($jsonInput as $k => $v){
        $input->def($k,$v);
    }
}
4
jamesgarrett

Ok, la première chose à faire est d’abord, récupérez votre code JavaScript à partir du fichier PHP) et vers un fichier .js fichier.

Le fichier doit être placé dans le répertoire media, mais juste pour une mesure temporaire, il suffit de le placer à la racine du répertoire du module.

Voici le code avec quelques modifications, telles que le passage des paramètres d'URL sous forme d'objet rwquest, par opposition à une URL directe. Cela vous aidera lorsque vous travaillez avec des sites Web multilingues et des URL SEF.

ajax.js:

jQuery(function($) {
    var path = $('#path');

    path.on('change', function() {
        var value = $(this).val();
        var request = {
            option       : 'com_ajax',
            module       : 'filefinder',
            method       : 'FileFinder',
            format       : 'raw',
            'data[path]' : value,
        };

        $.ajax({
            type: 'POST',
            data: request,
            success: function(response){
                console.log(response); 
            }
        });

    });

    path.trigger('change');
});

Le code révisé de l’assistant avec quelques modifications de code supplémentaires pour vous permettre d’obtenir les données Ajax.

helper.php:

defined('_JEXEC') or die('Restricted access');

class ModfileFinderHelper
{
    public $currentdirectory = '';
    public $subdirectories = array();
    public $container = false;

    public function __construct($path = null)
    {
        if (!$path)
        {
            chdir('../');
            $this->currentdirectory = basename(JPATH_BASE);
            $this->subdirectories = glob($this->currentdirectory . DS . '*', GLOB_ONLYDIR);
            $this->container = true;

        }
        else
        {
            $this->currentdirectory = $path;
            $this->subdirectories = glob($this->currentdirectory . DS . '*', GLOB_ONLYDIR);
        }
    }

    public function FileFinderAjax()
    {
        $data = JFactory::getApplication()->input->post->getArray([]);
        $path = $data['data']['server'];

        var_dump($path);
        exit;

        // Check the browser console (Network tab) and the path should be displayed under the "response"
    }
}

Et enfin le default default.php:

<?php

defined('_JEXEC') or die;

JHtml::_('jquery.framework');
JHtml::_('script', 'modules/mod_filefinder/ajax.js');

?>

<?php if ($filefinder->container) : ?>
    <div id="filefinder_container">
<?php endif; ?>

    <span><?php echo JText::_('Current Directory') . ' ' . $filefinder->currentdirectory; ?></span>
    <select id="path">
        <option value="">Select a Subdirectory</option>
        <?php foreach ($filefinder->subdirectories as $subdirectory) : ?>
            <option><?php echo $subdirectory; ?></option>
        <?php endforeach; ?>
    </select>

<?php if ($filefinder->container) : ?>
    </div>
<?php endif; ?>

mod_filefinder.php:

En raison du changement de nom de classe, mettez à jour la ligne suivante dans ce fichier à partir de:

$filefinder = new FileFinder(JFactory::getApplication()->input->post->getString('path'));

à:

$path = JFactory::getApplication()->input->post->getString('path');
$filefinder = new ModfileFinderHelper($path);

Globalement, juste quelques simplifications de code et améliorations ici et là.

J'espère que cela t'aides


Sécurité

Il est recommandé d'implémenter une vérification de jeton pour les appels Ajax:

Comment ajouter de l'anti-usurpation CSRF aux formulaires

3
Lodder