0

I'm using a basic multiple upload Javascript that doesn't allow to re-order files. I would like to be able to drag and drop pictures and save their new position on the server. The tricky part is that I do not store those files in a database.

The upload process works that way : as you can see, it's a simple loop that rename the pics from 001.jpg to 099.jpg and place them in the right folder.

if(isset($_FILES['images']) && $_FILES['images']['size'] > 0) {
        if(!file_exists('images/upload/' . $id_client . '/photos/' . $gallery)) {
            mkdir('images/upload/' . $id_client . '/photos/' . $gallery);
        }
        else {
            $gallery = $data_m[0]['gallery'];
        }
        $i = htmlspecialchars(trim($_POST['nb_img']) + 1);
        foreach($_FILES['images']['tmp_name'] as $index => $tmpName) {
            if(!empty($tmpName) && is_uploaded_file($tmpName)) {
                if(substr($_FILES['images']['type'][$index], 0, 5) == "image") {
                    if($i < 10) {
                        $img_url = '00' . $i . '.' . pathinfo($_FILES['images']['name'][$index], PATHINFO_EXTENSION);
                    }
                    else {
                        $img_url = '0' . $i . '.' . pathinfo($_FILES['images']['name'][$index], PATHINFO_EXTENSION);    
                    }
                    move_uploaded_file( $tmpName, $dir_upload . '/photos/' . $gallery . '/'. $img_url);
                }
            }
            $i++;
        }
    }

The display is as simple : the page just checks the folder and shows the content in alphabetical order :

if(!empty($data['gallery']) && file_exists($dir_upload . '/photos/' . $data['gallery'])) {
            $dirname = $dir_upload . '/photos/' . $data['gallery'];
            $gallery = scandir($dirname);
            asort($gallery);
            $row = 1;
            echo '<div class="gallery">';
                echo '<div class="row">';
                foreach($gallery as $pix) {
                    if(!empty($pix) && $pix != '.' && $pix != '..') {
                        if($row >5) { 
                            echo '</div>'; 
                            echo '<div class="row">';
                            $row = 1; 
                        }
                        $url_img = $dirname . '/' . $pix;
                        echo '<a class="col-md mt-1 mr-1 mb-1 ml-1 btn btn-secondary img-'.$row.' disabled" href="' . $url_img . '" rel="clearbox[gallery=' . $data['gallery'] . ']" style="background:url(' . $url_img . ') center; background-size:cover; height:210px;">';
                        echo '</a>';
                        $row++;
                    }
                }
                echo '</div>';
            echo '</div>';
        }

My immediate need would be to be able to simply drag and drop the pics and rename them on the server. For example, if I drag 005.jpg in third position, it would rename it 002-1608378266.jpg (adding the actual Timestamp so it places itself before 003.jpg without erasing the previous file).

I'm aware it's not the best option (not even sure that's doable) and I'm already working on a database solution, but I need an emergency solution to calm down users while I develop the new script.

You can have a look at the test server using id and password Stackoverflow : https://www.0000.yelofox.fr/page-10-at-vero-eos-et-accusam

Thanks to those who will try and help me !

Greg
  • 65
  • 1
  • 10
  • As an emergency alternative, saving the order in a JSON file will be easier than trying to rename the pictures one after the other – blex Dec 19 '20 at 12:06
  • I'm not familiar with JSON but found a few tutorials. So each object will be one of my pictures and I can use and "order" attributes ? – Greg Dec 19 '20 at 12:23
  • Yes, basically, you can create an Array containing the names of your files in the correct order (which you can manipulate). The main functions you would need would be `file_get_contents` _(to retrieve the JSON string from the file)_, `json_decode` _(to parse that JSON string into an Array you can manipulate)_, `json_encode` _(to convert it back to JSON)_ and `file_put_contents` to save it to the file. I found [this example](https://stackoverflow.com/a/38303923/1913729). You would not need to do everything they did, but it might help you. – blex Dec 19 '20 at 12:33
  • Just a tip: your question seems to be way too broad for StackOverflow. If I'm not mistaken, you're asking how to handle the front end, and how to handle the backend ? We don't know what your frontend's code look like _(the link is not working, and you are supposed to put all the info we need in the question itself)_, and as for the backend, you're showing us what happens when pictures are uploaded, but I'm guessing that the pics can be reordered later? Try to break up your problem into multiple smaller steps, look for solutions to each of them, & edit your question so that it's more specific – blex Dec 19 '20 at 12:45
  • That's pretty much all of it. The files just upload (code section 1) and show (code section 2). There's unfortunately nothing more I can add as I haven't implemented any drag&drop script yet. As for the link, it's just supposed to show the context (and I'm surprised it doesn't work for you). – Greg Dec 19 '20 at 12:51
  • I'll try the JSON tuts by the way but it seems way out of my league. – Greg Dec 19 '20 at 12:52
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/226170/discussion-between-blex-and-greg). – blex Dec 19 '20 at 13:00

1 Answers1

1

We were able to get to the bottom of things in the chat, and here is a proposed solution:

The order of images is stored in a JSON file. That file is then read when we want to get the list of images a gallery contains, and updated both when new images are added, and when the user sorts these images.

In order to accomplish this, I made a file containing only utility functions, and a demo page:

utils.php

<?php 

/**
 * Returns the path to a gallery's directory
 */
function getClientGalleryDirPath($clientId, $gallery) {
  return 'images/upload/' . $clientId . '/photos/' . $gallery;
}

/**
 * Returns the path to a gallery's data file
 */
function getClientGalleryDataPath($clientId, $gallery) {
  return getClientGalleryDirPath($clientId, $gallery) . '/data.json';
}

/**
 * Makes sure the client's gallery has a directory, and a JSON file
 * containing the list of images
 */
function ensureClientGalleryExists($clientId, $gallery) {
  $dirPath = getClientGalleryDirPath($clientId, $gallery);

  if(!file_exists($dirPath)) {
    mkdir($dirPath, 0755, true);
  }

  $jsonPath = getClientGalleryDataPath($clientId, $gallery);

  if (!file_exists($jsonPath)) {
    // Create an empty Array
    $data = [];

    // If pictures exist, we'll add them
    $pictureFiles = scandir($dirPath);
    foreach($pictureFiles as $pic) {
      if(!empty($pic) && $pic != '.' && $pic != '..') {
          array_push($data, $pic);
      }
    }
    // Make the Array a JSON string, and save it
    $json = json_encode($data);
    file_put_contents($jsonPath, $json);
  }
}

/**
 * Ensures the gallery is created, and returns the data
 */
function getOrCreateClientGallery($clientId, $gallery) {
  // Make sure it exists
  ensureClientGalleryExists($clientId, $gallery);

  $dataPath = getClientGalleryDataPath($clientId, $gallery);
  $json = file_get_contents($dataPath);

  return json_decode($json, true);
}

/**
 * Saves new data in a client's gallery
 */
function saveClientGallery($clientId, $gallery, $data) {
  // Make sure it exists
  ensureClientGalleryExists($clientId, $gallery);

  $dataPath = getClientGalleryDataPath($clientId, $gallery);
  $json = json_encode($data);

  return file_put_contents($dataPath, $json);
}

/**
 * Uploads pictures, and updates the gallery data
 */
function addPicturesToGallery($clientId, $gallery, $files) {
  // Get the existing data
  $data = getOrCreateClientGallery($clientId, $gallery);
  $dirPath = getClientGalleryDirPath($clientId, $gallery);

  foreach($files['tmp_name'] as $index => $tmpName) {
    if (
         !empty($tmpName) &&
         is_uploaded_file($tmpName) &&
         substr($files['type'][$index], 0, 5) == "image"
       ) {
         // Create a unique name, and move the file
         $newName = uniqid() . '.' . pathinfo($files['name'][$index], PATHINFO_EXTENSION);
         move_uploaded_file($tmpName, $dirPath . '/'. $newName);
         // Add the new name to $data 
         array_push($data, $newName);
    }
  }

  // We're done, save the data
  saveClientGallery($clientId, $gallery, $data);
}

/**
 * Compares the new list with the previous one, and updates it
 */
function  reorderClientGallery($clientId, $gallery, $newData) {
  $oldData = getOrCreateClientGallery($clientId, $gallery);

  // Make sure that every element is present in both Arrays
  // (otherwise there's something wrong with the provided data)
  if (
       count(array_diff($oldData, $newData)) == 0 &&
       count(array_diff($newData, $oldData)) == 0
     ) {
       saveClientGallery($clientId, $gallery, $newData);
  }
}

index.php

<?php 

require_once('utils.php');

// Just for the demo, let's say that:
$clientId = 'robert';
$gallery = 'my-first-gallery';

// If pictures were sent, upload them
if (isset($_FILES['images']) && $_FILES['images']['size'] > 0) {
  addPicturesToGallery($clientId, $gallery, $_FILES['images']);
}

// If a picture ordering was sent, apply it
if (isset($_POST['order'])) {
  reorderClientGallery($clientId, $gallery, json_decode($_POST['order']));
}



// To avoid cluttering the HTML below, I declared this here
function displayGalleryItems($clientId, $gallery) {
  $dirPath = getClientGalleryDirPath($clientId, $gallery);
  // Get the gallery and display it
  $data = getOrCreateClientGallery($clientId, $gallery);
  foreach($data as $pic) {
    echo '<div class="pic" data-pic="' . $pic . '" style="background-image: url(\'' . $dirPath . '/' . $pic . '\')"></div>';
  }
}

?><!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>My gallery</title>
  <style>
    #saveBtn { display: none; } /* will only be displayed after reordering */
    .pic { display: inline-block; width: 250px; height: 190px; background-size: cover; margin: .5em; }
  </style>
</head>
<body>
  <h1><?=$gallery?></h1>

  <p>Add pictures to your gallery:</p>
  <form method="post" enctype="multipart/form-data">
    <input type="file" name="images[]" multiple>
    <input type="submit">
  </form>

  <div id="sortable">
    <?php displayGalleryItems($clientId, $gallery); ?>
  </div>

  <form method="post">
    <input type="hidden" name="order">
    <input type="submit" value="Enregistrer" id="saveBtn">
  </form>

  <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
  <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
  <script>
  $(function() {
    $("#sortable").sortable({
      revert: 200,
      update: function() {
        // Every time the order changes, grab the filenames
        var order = $('.pic').map(function(i, p) { return $(p).attr('data-pic'); }).toArray();
        // Set the form input's value
        $('input[name="order"]').val( JSON.stringify(order) );
        // Show the save button
        $('#saveBtn').show();
      }
    });

    $(".pic").disableSelection();
  });
  </script>
</body>
</html>
blex
  • 24,941
  • 5
  • 39
  • 72
  • 1
    Thanks to blex for his patience and talents we managed to do it. His code is clear and fully operational. I learned a lot ! – Greg Dec 20 '20 at 10:41