JsTree v1.0 - How to manipulate effectively the data from the backend to render the trees and operate correctly?

Posted by Jean Paul on Stack Overflow See other posts from Stack Overflow or by Jean Paul
Published on 2012-06-13T19:08:41Z Indexed on 2012/06/24 3:16 UTC
Read the original article Hit count: 1279

Filed under:
|
|
|

Backend info: PHP 5 / MySQL

URL: http://github.com/downloads/vakata/jstree/jstree_pre1.0_fix_1.zip

  • Table structure for table discussions_tree --

        CREATE TABLE IF NOT EXISTS `discussions_tree` (
          `id` int(11) NOT NULL AUTO_INCREMENT,
          `parent_id` int(11) NOT NULL DEFAULT '0',
          `user_id` int(11) NOT NULL DEFAULT '0',
          `label` varchar(16) DEFAULT NULL,
          `position` bigint(20) unsigned NOT NULL DEFAULT '0',
          `left` bigint(20) unsigned NOT NULL DEFAULT '0',
          `right` bigint(20) unsigned NOT NULL DEFAULT '0',
          `level` bigint(20) unsigned NOT NULL DEFAULT '0',
          `type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
          `h_label` varchar(16) NOT NULL DEFAULT '',
          `fulllabel` varchar(255) DEFAULT NULL,
          UNIQUE KEY `uidx_3` (`id`),
          KEY `idx_1` (`user_id`),
          KEY `idx_2` (`parent_id`)
        ) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ;
    
    
        /*The first element should in my understanding not even be shown*/
        INSERT INTO `discussions_tree` (`id`, `parent_id`, `user_id`, `label`, `position`, `left`, `right`, `level`, `type`, `h_label`, `fulllabel`) VALUES
        (0,  0, 0, 'Contacts', 0, 1, 1, 0, NULL, '', NULL);
    
        INSERT INTO `discussions_tree` (`id`, `parent_id`, `user_id`, `label`, `position`, `left`, `right`, `level`, `type`, `h_label`, `fulllabel`) VALUES
        (1,  0, 0, 'How to Tag', 1, 2, 2, 0, 'drive', '', NULL);
    

Front End :

I've simplified the logic, it has 6 trees actually inside of a panel and that works fine

        $array  = array("Discussions");
        $id_arr  = array("d");

        $nid = 0;
        foreach ($array as $k=> $value) 
        {
            $nid++;
        ?>

        <li id="<?=$value?>" class="label">
                <a href='#<?=$value?>'><span> <?=$value?> </span></a>
        <div class="sub-menu"  style="height:auto; min-height:120px; background-color:#E5E5E5" >
                        <div class="menu" id="menu_<?=$id_arr[$k]?>" style="position:relative; margin-left:56%">
                            <img src="./js/jsTree/create.png" alt=""  id="create" title="Create" >
                            <img src="./js/jsTree/rename.png" alt="" id="rename" title="Rename" >
                            <img src="./js/jsTree/remove.png" alt=""  id="remove"  title="Delete">
                            <img src="./js/jsTree/cut.png" alt=""  id="cut" title="Cut" >
                            <img src="./js/jsTree/copy.png" alt="" id="copy" title="Copy">
                            <img src="./js/jsTree/paste.png" alt=""   id="paste" title="Paste">                         
                        </div>
                <div id="<?=$id_arr[$k]?>" class="jstree_container"></div>
        </div>
        </li>

            <!-- JavaScript neccessary for this tree : <?=$value?> -->
            <script type="text/javascript" >


            jQuery(function ($) {

            $("#<?=$id_arr[$k]?>").jstree({


                    // List of active plugins used
                    "plugins" : [ "themes", "json_data", "ui", "crrm" , "hotkeys" , "types" , "dnd", "contextmenu"],  

                //  "ui" :{ "initially_select" : ["#node_"+ $nid ] } ,

                "crrm": { "move": { "always_copy": "multitree" }, "input_width_limit":128 },

               "core":{ "strings":{  "new_node" : "New Tag"  }},

               "themes": {"theme": "classic"},

                    "json_data" : { 

                        "ajax" : {

                            "url" : "./js/jsTree/server-<?=$id_arr[$k]?>.php",
                            "data" : function (n) { 
                                // the result is fed to the AJAX request `data` option
                                return { 
                                    "operation" : "get_children", 
                                  "id" : n.attr ? n.attr("id").replace("node_","") : 1,
                                  "state" : "",
                                    "user_id": <?=$uid?>

                                }; 
                            }
                        }
                    }
                ,
                    "types" : {
                        "max_depth" : -1,
                        "max_children" : -1,
                        "types" : {
                            // The default type
                            "default" : {
                                "hover_node":true,
                                "valid_children" : [ "default" ],
                            },

                            // The `drive` nodes 
                            "drive" : {
                                // can have files and folders inside, but NOT other `drive` nodes
                                "valid_children" : [ "default", "folder" ],
                                "hover_node":true,
                                "icon" : {
                                    "image" : "./js/jsTree/root.png"
                                },
                                // those prevent the functions with the same name to be used on `drive` nodes.. internally the `before` event is used
                                "start_drag" : false,
                                "move_node" : false,
                                "remove_node" : false
                            }
                        }
                    },

                "contextmenu" : { "items" : customMenu , "select_node": true}
                })

            //Hover function binded to jstree
               .bind("hover_node.jstree", function (e, data) {
                     $('ul li[rel="drive"], ul li[rel="default"], ul li[rel=""]').each(function(i) {
                   $(this).find("a").attr('href', $(this).attr("id")+".php" );
                    })
            })



            //Create function binded to jstree
                .bind("create.jstree", function (e, data) {
                    $.post(
                        "./js/jsTree/server-<?=$id_arr[$k]?>.php", 
                        { 
                            "operation" : "create_node", 
                            "id" : data.rslt.parent.attr("id").replace("node_",""), 
                            "position" : data.rslt.position,
                            "label" : data.rslt.name,
                            "href" : data.rslt.obj.attr("href"),
                            "type" : data.rslt.obj.attr("rel"),
                            "user_id": <?=$uid?>
                        }, 
                        function (r) {
                            if(r.status) {
                            $(data.rslt.obj).attr("id", "node_" + r.id);

                            }
                            else {
                                $.jstree.rollback(data.rlbk);
                            }
                        }
                    );
                })

            //Remove operation  
                .bind("remove.jstree", function (e, data) {
                    data.rslt.obj.each(function () {
                        $.ajax({
                            async : false,
                            type: 'POST',
                            url: "./js/jsTree/server-<?=$id_arr[$k]?>.php",
                            data : { 
                                "operation" : "remove_node", 
                                "id" : this.id.replace("node_",""),
                                "user_id": <?=$uid?>
                            }, 
                            success : function (r) {
                                if(!r.status) {
                                    data.inst.refresh();
                                }
                            }
                        });
                    });
                })

            //Rename operation
                .bind("rename.jstree", function (e, data) {
                data.rslt.obj.each(function () {            
                        $.ajax({
                            async : true,
                            type: 'POST',
                            url: "./js/jsTree/server-<?=$id_arr[$k]?>.php",
                            data : { 
                                "operation" : "rename_node", 
                                "id" : this.id.replace("node_",""),
                                "label" : data.rslt.new_name,
                                "user_id": <?=$uid?>
                            },
                            success : function (r) {
                                if(!r.status) {         
                                    data.inst.refresh();
                                }
                            }
                        });
                });
                })


            //Move operation    
                .bind("move_node.jstree", function (e, data) {
                    data.rslt.o.each(function (i) {
                        $.ajax({
                            async : false,
                            type: 'POST',
                            url: "./js/jsTree/server-<?=$id_arr[$k]?>.php",
                            data : { 
                                "operation" : "move_node", 
                                "id" : $(this).attr("id").replace("node_",""), 
                                "ref" : data.rslt.cr === -1 ? 1 : data.rslt.np.attr("id").replace("node_",""), 
                                "position" : data.rslt.cp + i,
                                "label" : data.rslt.name,
                                "copy" : data.rslt.cy ? 1 : 0,
                                "user_id": <?=$uid?>

                            },
                            success : function (r) {
                                if(!r.status) {
                                    $.jstree.rollback(data.rlbk);
                                }
                                else {
                                    $(data.rslt.oc).attr("id", "node_" + r.id);
                                    if(data.rslt.cy && $(data.rslt.oc).children("UL").length) {
                                        data.inst.refresh(data.inst._get_parent(data.rslt.oc));
                                    }
                                }
                            }
                        });
                    });
                });


            // This is for the context menu to bind with operations on the right clicked node
            function customMenu(node) {
                // The default set of all items

            var control;

                var items = {

                          createItem: { 
                        label: "Create",
                        action: function (node) {   return {createItem: this.create(node) }; }
                    },

                            renameItem: { 
                        label: "Rename",
                        action: function (node) {  return {renameItem: this.rename(node) }; }
                    },

                        deleteItem: { 
                        label: "Delete",
                        action: function (node) {     return {deleteItem: this.remove(node) }; },
                        "separator_after": true

                    },
                            copyItem: { 
                                    label: "Copy",
                                    action: function (node) {  $(node).addClass("copy");  return {copyItem: this.copy(node)  }; }
                    },
                                    cutItem: { 
                                    label: "Cut",
                                    action: function (node) { $(node).addClass("cut");  return {cutItem: this.cut(node)  }; }
                    },
                            pasteItem: { 
                                    label: "Paste",
                                    action: function (node) {  $(node).addClass("paste");    return {pasteItem: this.paste(node) }; }
                    }


                };

                // We go over all the selected items as the context menu only takes action on the one that is right clicked
                    $.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).each(function(index,element)
                    {   

                            if ( $(element).attr("id") != $(node).attr("id") )
                            {
                                // Let's deselect all nodes that are unrelated to the context menu -- selected but are not the one right clicked

                                $("#<?=$id_arr[$k]?>").jstree("deselect_node", '#'+$(element).attr("id") );  
                            }
                        });

            //if any previous click has the class for copy or cut
                 $("#<?=$id_arr[$k]?>").find("li").each(function(index,element)     
                 {  
                            if ($(element) != $(node) )
                        {   
                            if(  $(element).hasClass("copy") || $(element).hasClass("cut") ) control=1;
                        }
                        else if(  $(node).hasClass("cut") || $(node).hasClass("copy"))
                            {
                            control=0;
                            }
                });


                //only remove the class for cut or copy if the current operation is to paste
            if($(node).hasClass("paste") )
            {   
                control=0;   
                // Let's loop through all elements and try to find if the paste operation was done already      
                $("#<?=$id_arr[$k]?>").find("li").each(function(index,element)
                {       
                    if( $(element).hasClass("copy") ) $(this).removeClass("copy");  
                        if ( $(element).hasClass("cut") ) $(this).removeClass("cut"); 
                      if ( $(element).hasClass("paste") ) $(this).removeClass("paste");  
                    });
            }


                   switch (control)
                   {
                    //Remove the paste item from the context menu
                    case 0:
                                    switch ($(node).attr("rel"))
                                        {

                                                case "drive":
                                                    delete items.renameItem;
                                                    delete items.deleteItem;
                                                    delete items.cutItem;
                                                    delete items.copyItem;
                                                delete items.pasteItem;
                                                break;

                                                case "default":
                                              delete items.pasteItem;
                                                break;

                                        }   
                    break;

                   //Remove the paste item from the context menu only on the node that has either copy or cut added class
                            case 1:
                            if( $(node).hasClass("cut") || $(node).hasClass("copy") )
                            {
                                                    switch ($(node).attr("rel"))
                                                    {

                                                                    case "drive":
                                                                        delete items.renameItem;
                                                                        delete items.deleteItem;
                                                                        delete items.cutItem;
                                                                        delete items.copyItem;
                                                                    delete items.pasteItem;
                                                                    break;

                                                                    case "default":
                                                                  delete items.pasteItem;
                                                                    break;


                                                    }
                                            }   
                                                else //Re-enable it on the clicked node that does not have the cut or copy class
                                                    {
                                        switch ($(node).attr("rel"))
                                                    {

                                                            case "drive":
                                                                delete items.renameItem;
                                                                delete items.deleteItem;
                                                                delete items.cutItem;
                                                                delete items.copyItem;
                                                            break;

                                                    }       
                                                }   

                        break;

                    //initial state don't show the paste option on any node
                    default:                switch ($(node).attr("rel"))
                                                            {

                                                                    case "drive":
                                                                        delete items.renameItem;
                                                                        delete items.deleteItem;
                                                                        delete items.cutItem;
                                                                        delete items.copyItem;
                                                                        delete items.pasteItem;
                                                                    break;

                                                                    case "default":
                                                                  delete items.pasteItem;
                                                                    break;

                                                            }   

                    break;

                        }

                return items;

            }


                $("#menu_<?=$id_arr[$k]?> img").hover(
              function () {
                $(this).css({'cursor':'pointer','outline':'1px double teal'})
              }, 
              function () {
                $(this).css({'cursor':'none','outline':'1px groove transparent'})
              }
            );



                $("#menu_<?=$id_arr[$k]?> img").click(function () {

                    switch(this.id) {

            //Create only the first element
            case "create":
            if ( $.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).length )
            {   
            $.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).each(function(index,element){    
                    switch(index)
                    {   
                    case 0:     $("#<?=$id_arr[$k]?>").jstree("create", '#'+$(element).attr("id"), null, /*{attr : {href: '#' }}*/null ,null, false);   

                    break;


                    default: $("#<?=$id_arr[$k]?>").jstree("deselect_node", '#'+$(element).attr("id") );
                    break;
                    }

                });

            }
            else
            {
            $.facebox('<p class=\'p_inner error bold\'>A selection needs to be made to work with this operation');
            setTimeout(function(){
            $.facebox.close();
            }, 2000);
            }
            break;



            //REMOVE
            case "remove":
            if ( $.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).length )
            {   
                        $.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).each(function(index,element){

                        //only execute if the current node is not the first one (drive)
                        if( $(element).attr("id") !=  $("div.jstree > ul > li").first().attr("id") )
                        { 
                        $("#<?=$id_arr[$k]?>").jstree("remove",'#'+$(element).attr("id"));  
                        }
                        else $("#<?=$id_arr[$k]?>").jstree("deselect_node", '#'+$(element).attr("id") );
                        });

            }
            else
            {
            $.facebox('<p class=\'p_inner error bold\'>A selection needs to be made to work with this operation');
            setTimeout(function(){
            $.facebox.close();
            }, 2000);
            }   
            break;





            //RENAME NODE only one selection
            case "rename":
            if ( $.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).length )
            {   
            $.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).each(function(index,element){    

                        if( $(element).attr("id") !=  $("div.jstree > ul > li").first().attr("id") )
                        { 

                                switch(index)
                                {   
                                case 0:     $("#<?=$id_arr[$k]?>").jstree("rename", '#'+$(element).attr("id") );
                                break;

                                default: $("#<?=$id_arr[$k]?>").jstree("deselect_node", '#'+$(element).attr("id") );
                                break;
                                }

                        }
                     else $("#<?=$id_arr[$k]?>").jstree("deselect_node", '#'+$(element).attr("id") );


                });
            }
            else
            {
            $.facebox('<p class=\'p_inner error bold\'>A selection needs to be made to work with this operation');
            setTimeout(function(){
            $.facebox.close();
            }, 2000);
            }
            break;



            //Cut
            case "cut":
            if ( $.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).length )
            {   
            $.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).each(function(index,element){    
                    switch(index)
                    {   
                    case 0:
                                $("#<?=$id_arr[$k]?>").jstree("cut", '#'+$(element).attr("id")); 

                                    $.facebox('<p class=\'p_inner teal\'>Operation "Cut" successfully done.<p class=\'p_inner teal bold\'>Where to place it?');
                                    setTimeout(function(){
                                    $.facebox.close();
                                    $("#<?=$id_arr[$k]?>").jstree("deselect_node", '#'+$(element).attr("id"));
                                    }, 2000);   

                                    break;

                    default: $("#<?=$id_arr[$k]?>").jstree("deselect_node", '#'+$(element).attr("id") );
                    break;
                    }

                });

            }
            else
            {
            $.facebox('<p class=\'p_inner error bold\'>A selection needs to be made to work with this operation');
            setTimeout(function(){
            $.facebox.close();
            }, 2000);
            }
            break;



            //Copy
            case "copy":
            if ( $.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).length )
            {   
            $.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).each(function(index,element){    
                    switch(index)
                    {   
                    case 0:

                    $("#<?=$id_arr[$k]?>").jstree("copy", '#'+$(element).attr("id")); 

                        $.facebox('<p class=\'p_inner teal\'>Operation "Copy": Successfully done.<p class=\'p_inner teal bold\'>Where to place it?');
                        setTimeout(function(){
                        $.facebox.close();
                        $("#<?=$id_arr[$k]?>").jstree("deselect_node", '#'+$(element).attr("id") );
                        }, 2000);       
                    break;

                    default: $("#<?=$id_arr[$k]?>").jstree("deselect_node", '#'+$(element).attr("id") );
                    break;
                    }

                });

            }
            else
            {
            $.facebox('<p class=\'p_inner error bold\'>A selection needs to be made to work with this operation');
            setTimeout(function(){
            $.facebox.close();
            }, 2000);
            }
            break;


            case "paste":
            if ( $.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).length )
            {   
            $.jstree._reference("#<?=$id_arr[$k]?>").get_selected(false, true).each(function(index,element){    
                    switch(index)
                    {   
                    case 0:

                                $("#<?=$id_arr[$k]?>").jstree("paste", '#'+$(element).attr("id")); 

                                    break;

                    default: $("#<?=$id_arr[$k]?>").jstree("deselect_node", '#'+$(element).attr("id") );
                    break;
                    }

                });

            }
            else 
            {
            $.facebox('<p class=\'p_inner error bold\'>A selection needs to be made to work with this operation');
            setTimeout(function(){
            $.facebox.close();
            }, 2000);
            }
            break;
                    }
                });

            <?
                }

            ?>

server.php

        $path='../../../..';
        require_once "$path/phpfoo/dbif.class";
        require_once "$path/global.inc";


        // Database config & class
        $db_config = array(
            "servername"=> $dbHost,
            "username"  => $dbUser,
            "password"  => $dbPW,
            "database"  => $dbName
        );


        if(extension_loaded("mysqli")) require_once("_inc/class._database_i.php"); 
        else require_once("_inc/class._database.php"); 

        //Tree class
        require_once("_inc/class.ctree.php");


        $dbLink = new dbif();
        $dbErr = $dbLink->connect($dbName,$dbUser,$dbPW,$dbHost);


        $jstree = new json_tree();

        if(isset($_GET["reconstruct"])) {
            $jstree->_reconstruct();
            die();
        }

        if(isset($_GET["analyze"])) {
            echo $jstree->_analyze();
            die();
        }

        $table = '`discussions_tree`';

        if($_REQUEST["operation"] && strpos($_REQUEST["operation"], "_") !== 0 && method_exists($jstree, $_REQUEST["operation"])) {



        foreach($_REQUEST as $k => $v) 
        {       
                switch($k)
                {
                case 'user_id':

                    //We are passing the user_id from the $_SESSION on each request and trying to pick up the min and max value from the table that matches the 'user_id'
                    $sql = "SELECT max(`right`) , min(`left`) FROM  $table WHERE `user_id`=$v";
                    //If the select does not return any value then just let it be :P
                    if (!list($right, $left)=$dbLink->getRow($sql))
                    {
                    $sql = $dbLink->dbSubmit("UPDATE $table SET `user_id`=$v WHERE `id` = 1 AND `parent_id` = 0");
                    $sql = $dbLink->dbSubmit("UPDATE $table SET `user_id`=$v WHERE `parent_id` = 1 AND `label`='How to Tag' ");
                    }
                    else
                    {
                    $sql = $dbLink->dbSubmit("UPDATE $table SET `user_id`=$v, `right`=$right+2 WHERE `id` = 1 AND `parent_id` = 0");
                    $sql = $dbLink->dbSubmit("UPDATE $table SET `user_id`=$v, `left`=$left+1, `right`=$right+1 WHERE `parent_id` = 1 AND `label`='How to Tag' ");
                    }
                break;
            }
        }

            header("HTTP/1.0 200 OK");
            header('Content-type: application/json; charset=utf-8');
            header("Cache-Control: no-cache, must-revalidate");
            header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
            header("Pragma: no-cache");
            echo $jstree->{$_REQUEST["operation"]}($_REQUEST);
        die();
        }
        header("HTTP/1.0 404 Not Found");
        ?>      

The problem: DND *(Drag and Drop) works, Delete works, Create works, Rename works, but Copy, Cut and Paste don't work

© Stack Overflow or respective owner

Related posts about php

Related posts about mysql