Create and edit json from frontend for drag&drop tool purpose

Hi there !

I am working on a drag&drop tool with kirby. My page is divided in two sections, i have a imgs list on the left and a grid with cols and rows on the right. I can drag imgs from the left into the grid on the right. (see img below)

What i am trying to do is to store the position and the url of the images dropped into the grid, so when i refresh the page they’re still at the same location on the grid. If i move them or drag new imgs into the grid, all imags’s location and url are still stored.
I have multiple pages like this one as subpages.

What i’ve done :

  • A javascript script to get all the imgs dropped (url) and their location (col, row), and store them into an array in json format.
    function getWallState(){
        var jsonFile = [];
        let droppedCeramic = document.querySelectorAll(".dropped-img[draggable='true']");

        for(item of droppedCeramic){
            let ceramicsURL = {
            "name" : item.getAttribute("src"),
            "col" : item.parentNode.getAttribute("col"),
            "row" : item.parentNode.getAttribute("row")
            }
            jsonFile.push(ceramicsURL);
        }

        var jsonToString = JSON.stringify(jsonFile);
        console.log(jsonToString); // output json as text

    } 

the json file :

[
    {
        "name": "http://localhost:8021/calepinage_tool/plainkit-main/media/pages/ceramics/8608efaff4-1649948519/09-100x-crop.jpg",
        "col": "7",
        "row": "1"
    },
    {
        "name": "http://localhost:8021/calepinage_tool/plainkit-main/media/pages/ceramics/0e70c6bebf-1649948519/14-100x-crop.jpg",
        "col": "2",
        "row": "2"
    },
    {
        "name": "http://localhost:8021/calepinage_tool/plainkit-main/media/pages/ceramics/21bc659827-1650018671/screenshot-emoji-glyphs-5-100x-crop.png",
        "col": "5",
        "row": "2"
    },
    {
        "name": "http://localhost:8021/calepinage_tool/plainkit-main/media/pages/ceramics/3b1d9f3851-1649948519/07-100x-crop.jpg",
        "col": "4",
        "row": "3"
    }
]
  • I saw the topic about generating json with kirby but i don’t think it’s what i need, or maybe i’m missing something (i am new to kirby and php).

I think that i need to create a json file after the page is created, and use kirby API to write my jsonArray to this file and get it back with an XMLHttpRequest ( i got this last one).

XMLHttpRequest :

var ceramicsDropped;
    var requestURL = 'ceramics.json';
    var request = new XMLHttpRequest();

    request.open('GET', requestURL);
    request.responseType = 'json';
    request.send();

    request.onload = function() {
    ceramicsDropped = request.response;
    var rowItems = document.getElementsByClassName("row-item");
    for (ceramic of ceramicsDropped){
        //console.log(ceramic);
        var cols = ceramic.col;
        var rows = ceramic.row;
        for (row of rowItems){
        // console.log("col : ", row.getAttribute("col"));
        // console.log("row : ", row.getAttribute("row"));
            var colTag = row.getAttribute("col");
            var rowTag = row.getAttribute("row");
            if ((cols == colTag) && (rows == rowTag)){
                // console.log("row", row);
                // console.log("ceramic", ceramic);
                calepinage(row, ceramic.name);
                }
            }
        }
    } // FIN DU ONLOAD

To be more clear :slightly_smiling_face:

  • how to generate a json file each time a subpage is created
  • how to write into this json file

Is anyone could help me to figure it out ? :;-)]}

You can already create the json file when you create the page, with a page.create:after hook.

To write from the frontend into this file, you have to make an ajax request. This request can go to a route that listens to a POST request that then handles writing to this file (see F::write(), F::write() | Kirby CMS).

Or you post to a json representation of the page instead of to a route.

Thanks for your fast reply ! :slightly_smiling_face:

I create a hook to create my json file in my config/config.php and i works perfectly !
my hook :

    'hooks' => [
      'page.create:after' => function ($page) {
        $path = $page->contentFileDirectory();
        $jsonFile = file_put_contents("{$path}/dropped.json", "json");
      }
    ],

So i’ve got a ‘dropped.json’ file in the same folder as the page created. But i’m still stuck with writing into this json file…

I have a html ‘button’ that calls my ajax request “onclick”
<button onclick="getWallState()">SAVE</button>

My ajax request store in an array the url and positions of imgs dropped in the grid, stringify it and passed to body object of the ajax request, the request url is the json representation of the page.
my ajax request post :

    function getWallState(){
    
        var jsonFile = [];
        let droppedCeramic = document.querySelectorAll(".dropped-img[draggable='true']");
	// GET ALL IMGS DROPPED
        for(item of droppedCeramic){
            let ceramicsURL = {
            "name" : item.getAttribute("src"),
            "col" : item.parentNode.getAttribute("col"),
            "row" : item.parentNode.getAttribute("row")
            }
            jsonFile.push(ceramicsURL);
        }

        var jsonToString = JSON.stringify(jsonFile);
        

        // AJAX REQUEST
        fetch(window.location.href + ".json", {
            method: 'post',
            body: jsonToString,
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
            }
        }).then((response) => {
            console.log(response.json());
            return response.json()
        }).then((res) => {
            if (res.status === 201) {
                console.log("Json successfully posted!")
            }
        }).catch((error) => {
            console.log(error)
        })
    }

my json representation of the page in templates/mypagename.json.php

<?php

$data = [
  'title' => $page->title()->value(),
  'json'  => $page->file('dropped.json')->filename(), // ->read()
  'request'=> isset($_POST["???"])
];

echo json_encode($data);

in the console i get the following :
screenshot

when i visit /mypagename.json url i get :
{"title":"test-hook","json":"dropped.json","request":false}

  • how could i handle this ajax request in my json representation ?
  • after handle the request i should get access to json data sent and i could pass it to the $content parameter of the F::write() ?

I know it’s a noob asking, sorry for that…

:;-|)

Create a controller for the json representation. In that controller, listen for a POST request and if you have a post request, then write the POST data to your json file.

if($kirby->request()->is('POST') {
   // do stuff
}

Thanks for your reply :slight_smile:

I managed to do what i wanted ! Many thanks for your help !!!

my page representation :
templates/wall.json.php

<?php

$data = [
  //'title' => $page->title()->value(),
  'jsonData' => $jsonData, // JSON 
  'file'     => $page->file('dropped.json')->contentFileDirectory(),

];

//echo json_encode($jsonData);
$jsonToWrite = json_encode($jsonData); // encode only json received

// WRITE DROPPED.JSON
$file = $page->file('dropped.json')->contentFileDirectory(); // FILE PATH
F::write("{$file}/dropped.json", $jsonToWrite, $append=false); // WRITE

page representation controller :
controllers/wall.json.php

<?php

return function ($kirby, $site, $page){


    if($kirby->request()->is('POST')) {
        //$myJson = $_POST;
        $myJson = file_get_contents('php://input'); // string data
        $jsonData = json_decode($myJson); // to json data
    } else {
        $jsonData  = "no data or not a post request";
    }

    return [
        'jsonData' => $jsonData
    ];
};

my ajax post request :

    function getWallState(){
        var jsonFile = [];
        let droppedCeramic = document.querySelectorAll(".dropped-img[draggable='true']");
        // PUSH to JSON ARRAY
        for(item of droppedCeramic){
            let ceramicsURL = {
            "name" : item.getAttribute("src"),
            "col" : item.parentNode.getAttribute("col"),
            "row" : item.parentNode.getAttribute("row")
            }
            jsonFile.push(ceramicsURL);
        }
        // json to string
        var jsonToString = JSON.stringify(jsonFile);
        

        // AJAX REQUEST (asynchrone)
        var requestURL = window.location.href + ".json";
        var request = new XMLHttpRequest();

        request.open('POST', requestURL, true);
        //Send the proper header information along with the request
        request.setRequestHeader('Content-type', 'text/plain');
        request.responseType = 'text';
        request.send(jsonToString);

        request.onreadystatechange = function() {
            if (this.readyState == 4 && this.status == 200) {
            console.log("onreadystatechange : ", this.responseText);
            }
        }
    }

You rocks !
:;:–)])