Display and delete files from subfolders based on select value - php

My folder structure has 4 layers with my form in the top layer, currently it displays the files in the top layer only, I want to be able to select a subfolder and display the files in it so they can be deleted if necessary.
Produce
Produce/Meat
Produce/Meat/Beef
Produce/Meat/Beef/Portions
Produce/Meat/Beef/Packaged
Produce/Vegtables
Produce/Vegetables/Fresh
Produce/Vegetables/Fresh/Local etc,.
My form displays the contents of the folder it is in with checkboxes, I can then tick boxes and delete files, but I have added a select and want to be able to display the contents of the selected subfolder and delete files. I made two submit buttons and both work, but the delete feature only works if it's in the top folder.
if ($_POST['delete'] == 'Submit')
{
foreach ((array) $_POST['select'] as $file) {
if(file_exists($file)) {
unlink($file);
}
elseif(is_dir($file)) {
rmdir($file);
}
}
}
$files = array();
$dir = opendir('.');
while(false != ($file = readdir($dir))) {
if(($file != ".") and ($file != "..")and ($file != "error_log")) {
$files[] = $file;
}
}
if ($_POST['action'] == 'Change') {
if($_POST['folder'] == 'AAA'){
$files = array();
$dir = opendir('/home/mysite/public_html/Produce/Vegetables/');
while(false != ($file = readdir($dir))) {
if(($file != ".") and ($file != "..")) {
$files[] = $file;
}
}
}
if($_POST['folder'] == 'BBB'){
$files = array();
$dir = opendir('/home/mysite/public_html/Produce/Meat');
while(false != ($file = readdir($dir))) {
if(($file != ".") and ($file != "..")) {
$files[] = $file;
}
}
}
}
natcasesort($files);
?>
<form id="delete" action="" method="POST">
<?php
echo '<table><tr>';
for($i=0; $i<count($files); $i++) {
if ($i%5 == 0) {
echo '</tr>';
echo '<tr>';
}
echo '<td style="width:180px">
<div class="select-all-col"><input name="select[]" type="checkbox" class="select" value="'.$files[$i].'"/>
'.$files[$i].'</div>
<br />
</td>';
}
echo '</table>';
?>
</table>
<br>
Choose a folder:
<select name="folder"><option value="this" selected>This folder</option><option value="BBB">Meat</option><option value="CCC">Meat/Beef</option><option value="DDD">Meat/Beef/Portions</option><option value="EEE">Meat/Beef/Packaged</option><option value="FFF">Vegetables</option><option value="GGG">Vegetables/Fresh</option><option value="HHH">Vegetables/Fresh/Local</option><option value="III">Vegetables/Fresh/Packaged</option></select>
<br>
<input class="button" type="submit" form="delete" name="action" value="Change"><br>
<button type="submit" form="delete" value="Submit">Delete File/s</button>
</form><br>
How can I utilise the selected value to accomplish this?

First, I'd like to address why you are unable to delete files outside of the top folder. You never change the "current working directory" so calling the deleting functions on deep files will never work as intended and could delete the files in the top folder. To correct this, you will either need to include the path with each file/directory to be deleted or call chdir() once so that unlink() and rmdir() are looking in the right place.
I believe your project still has some natural maturing to do including security and UX. I'll provide a generalized/simple snippet for you to consider/compare against your project to hopefully give you a bit more traction in your development.
Your users will be able to make one of two choices on submission: Change Directory & Remove Files/Directories
For the directory change, your program will need to submit two necessary pieces of information:
The action (action="change")
The new folder (newfolder={variable})
For the file/directory deletion, there will be three necessary pieces of information:
The action (action="delete")
The files/directory (files[]={variable})
The directory to access (folder={variable}) * the value in the <select> cannot be trusted, because a user could change the selected value before selecting files in the current directory for deletion. This value must be statically preserved.*Note, you could just add the paths to the filenames in the checkbox values and eliminate the hidden input -- this will be a matter of programming preference.
Purely for demonstration purposes, I'll reference this static array of folders in my code:
$valid_folders=[
'Produce',
'Produce/Meat',
'Produce/Meat/Beef',
'Produce/Meat/Beef/Portions',
'Produce/Meat/Beef/Packaged',
'Produce/Vegetables',
'Produce/Vegetables/Fresh',
'Produce/Vegetables/Fresh/Local',
'Produce/Vegetables/Fresh/Packaged'
];
In reality, you'll probably want to generate an array of valid/permitted/existing folders. I might recommend that you have a look at this link: List all the files and folders in a Directory with PHP recursive function
if(isset($_POST['action'])){ // if there is a submission
if($_POST['action']=="Delete"){ // if delete clicked
if(in_array($_POST['folder'],$valid_folders)){
$folder=$_POST['folder']; // use valid directory
}else{
$folder=$valid_folders[0]; // set a default directory
}
chdir($folder); // set current working directory
//echo "<div>",getcwd(),"</div>"; // confirm directory is correct
foreach($_POST['files'] as $file){ // loop through all files submitted
if(is_dir($file)){ // check if a directory
rmdir($file); // delete it
}else{ // or a file
unlink($file); // delete it
}
}
}elseif($_POST['action']=="Change"){ // if change clicked
if(in_array($_POST['newfolder'],$valid_folders)){ // use valid new directory
$folder=$_POST['newfolder'];
}else{
//echo "Sorry, invalid folder submitted";
$folder=$valid_folders[0]; // set a default directory
}
}
}else{
$folder=$valid_folders[0]; // no submission, set a default directory
}
$dir = opendir("/{$folder}"); // set this to whatever you need it to be -- considering parent directories
//echo "Accessing: /$folder<br>";
while(false!=($file=readdir($dir))){
if(!in_array($file,['.','..','error_log'])){ // deny dots and error_log; you should also consider preventing the deletion of THIS file as well! Alternatively, you could skip this iterated condition and filter the $files array after the loop is finished.
$files[] = $file;
}
}
natcasesort($files);
echo "<form action=\"\" method=\"POST\">";
echo "<select name=\"newfolder\">";
//echo "<option value=\"\">Select a folder</option>"; // this isn't necessary if the neighboring button is descriptive
foreach($valid_folders as $f){
echo "<option",($folder==$f?" selected":""),">{$f}</option>"; // if a previously submitted directory, show it as selected
}
echo "</select> ";
echo "<button name=\"action\" value=\"Change\">Change To Selected Folder</button>";
echo "<br><br>";
echo "Delete one or more files:";
echo "<table><tr>";
for($i=0,$count=sizeof($files); $i<$count; ++$i){
if($i!=0 && $i%5==0){ // see the reason for this change # https://stackoverflow.com/questions/43565075/new-containing-div-after-every-3-records/43566227#43566227
echo "</tr><tr>";
}
echo "<td style=\"width:180px;\">";
echo "<div><input name=\"files[]\" type=\"checkbox\" value=\"{$files[$i]}\">{$files[$i]}</div>";
echo "</td>";
}
echo "</tr></table>";
echo "<input type=\"hidden\" name=\"folder\" value=\"{$folder}\">"; // retain current directory
echo "<button name=\"action\" value=\"Delete\">Delete Checked File(s)</button>";
echo "</form>";
As for form structure, you could implement <input type="submit"> or <button> to submit the form. I won't discuss the caveats for this question.
You see, in the form, $folder is a value that invisibly passed with the submission. This stops the user from moving to an unintended directory when deleting files.
When action=Delete then $folder and $files are used for processing.When action=Change only newfolder is used for processing.
When there is no action a default folder is declared and files will be listed.

Related

Deleting a file(s) from server PHP - Permission denied unlink

I want to delete files from my local server that's running on my beaglebone.
I have created a page that displays all the files and lets you select the files to delete. (As you can see below)
The webpage returns the names of the files to delete in the form of an array to the php script unlink.php
The code for Unlink.php is:
<?php
$files = $_POST['file'];
print_r($files);
if (empty($files)) {
echo "No files were selected. Go back to 192.168.7.2 and refresh the page." ;
} else {
$N = count($files);
for ($i = 0; $i < $N; $i++) {
$path = '/Logs/';
print_r($path);
#chown($path, 666);
if (unlink($path . $_GET['$files[$i]'])) {
echo ": Deleted";
} else {
echo "fail";
}
}
}
?>
However, whenever I try to delete a file: It fails.
The unlink() php function isn't being implemented properly and I'm not sure why.
How do I do this the right way?
The index.html page is located in /var/www/html and the logs are located in /var/www/html/Logs/. The address of the local server is 192.168.7.2
Form code:
<?php
$url = $_SERVER['DOCUMENT_ROOT'];
$path = "/var/www/html/Logs";
$dh = opendir($path);
$k = 0;
$foo = True;
while (($file = readdir($dh)) !== false) {
if ($file != "." && $file != "..") {
if ($k == 0 || $k % 6 == 0) {
$col .= "<tr><td><input type='checkbox' name='file[]' value='$file'> <a href='Logs/$file'>$file</a><br /></td>";
} else if ($k % 6 == 5) {
$col .= "<td><input type='checkbox' name='file[]' value='$file'> <a href='Logs/$file'>$file</a><br /></td></tr>";
} else {
$col .= "<td><input type='checkbox' name='file[]' value='$file'> <a href='Logs/$file'>$file</a><br /></td>";
}
$k++;
}
}
echo "<form action='unlink.php' method='post'><table>$col</table><br/><center><input type='submit' name='formSubmit' value='Delete' /></center></form>";
closedir($dh);
?>
EDIT: PHP display-errors
Warning: chmod(): Operation not permitted in /var/www/html/unlink.php on line 17
chmod($path . $files[$i], 0755);
Warning: unlink(/var/www/html/Logs/2017.01.24--13.43.43--0.log): Permission denied in /var/www/html/unlink.php on line 18 fail
if (unlink($path . $files[$i]))
but when I check ls -la for /Logs -> it shows up as it belongs to www-data. How do I change the permissions beyond this?
Permissions:
<?php
$files = $_POST['file'];
$path = "/var/www/html/Logs/";
print_r($files);
if (empty($files)) {
echo "No files were selected. Go back to 192.168.7.2 and refresh the page." ;
} else {
foreach ($files as $file) {
if (unlink($path . $file)) {
echo $path. $file ." : Deleted";
} else {
echo $path. $file . " : fail";
}
}
}
?>
That should do it, $path was wrong, and even if you had used the same as in the other code, it was missing a slash.
Nonetheless this code is not going to prevent malicious user actions.
How to avoid UNLINK security risks in PHP?
From your updated question I can see that the Logs folder is a symlink to /media/card/Logs.
What is the output of ls -la /media/card?
Some further reads:
https://en.wikipedia.org/wiki/Symbolic_link
http://php.net/manual/en/control-structures.foreach.php
Make sure the file that you are trying to pass to unlink() / delete is not being opened. If the file is .exe or etc please also check in Windows Task Manager -> Process and then kill it.
Change permission status of file. Maybe you can't have access to delete the file (execute permission).
After you sure the file is not being opened 1, then lets try this code :
// Check existence of file
if (file_exists($cekFile1)) {
// make sure you have permission to delete file
if(chmod($fileDir, 0777)){
if(!unlink($cekFile1)){
echo "unlink is fail !";
}
}else{
echo "chmod is fail";
}
// owner have read, write, execute rights.
// owner's user group only have read rights.
// everybody else only have read rights.
chmod($fileDir, 0744);
}
For more information about chmod(), please check this reference:
php.net.
php-cmod-function.

deleting images by clicking correspondent buttons

I have a folder / directory containing lots of images and other folders / directories.
I am showing the preview of these files using following code:
<?php
$images=array();
$dir_handler = opendir('test') or die("Unable to open path");
$i=0;
while($file = readdir($dir_handler))
{
if(is_dir($file))
continue;
else if($file != '.' && $file != '..' && $file != 'index.php')
{
$images[$i]=$file;
$i++;
}
}
sort($images);
for($i=0; $i<sizeof($images); $i++)
{
echo "<img style='border:1px solid #666666; width:100px;height:100px; margin: 10px;' src='test/".$images[$i]."'/><input type='button' value='nok[]'>";
} closedir($dir);
?>
The problem is that I want to assign a separate button to each file (to each image or folder), So that by clicking each button, its corresponding image (or folder / directory) gets deleted from the main folder and no more being shown as a preview.
Another small problem is that the preview of folders are not being shown with the above code. Why? Any help will be appreciated.
There are several questions here.
First question: Why won't preview of folders show? This is because you show previews by looping through the $images array, but you do not add directories to that array (see your code where you check if it is_dir and then invoke "continue;"). If you want to include the directories, then you should include them in the $images array (or do something else with them).
Second question: How to do the deleting? You need to either extend your existing PHP script or write another one. You would create a link on the delete icon; the link's href would be to the new (or existing) PHP script and you would pass in as parameters the folder or file to be deleted. If it is a folder, then use rmdir(). If it is a file, then use unlink(). I can help you more with this later, if you need it.
You are always displaying an image, you need to do a check if the $images array value is an image or a folder. Then do something different with the display.
To delete a file or folder, use these functions respectively:
unlink();
rmdir();
http://www.php.net/manual/en/function.rmdir.php
http://php.net/manual/en/function.unlink.php
To add a list of folders before your images change your code to:
<?php
$images=array();
$folders = array();
$dir_handler = opendir('test') or die("Unable to open path");
$i=0;
while($file = readdir($dir_handler))
{
if(is_dir($file)) {
$folders[count($folders)] = $file;
}
else if($file != '.' && $file != '..' && $file != 'index.php')
{
$images[$i]=$file;
$i++;
}
}
sort($images);
foreach($folders as $folder){
echo "<a href='#'>$folder</a>";
}
for($i=0; $i<sizeof($images); $i++)
{
echo "<img style='border:1px solid #666666; width:100px;height:100px; margin: 10px;' src='test/".$images[$i]."'/><input type='button' value='nok[]'>";
}
closedir($dir);
?>
Then all you have to do is build out the visual elements of a folder in your view and link to a function that will do your unlink() and rmdir() where needed.

Changing the names of images within a directory and displaying the changed file names

The purpose of the script is to change the names of of a list of images within a directory for an ecommerce site. So specifically when the script is rand a user will type in a word or phrase that they would like a set or list of files to be prefixed with. The script will iterate over each file changing the prefix and appending the next available number starting from zero.
I'd like to display/render on the page to the user what files have been changed. right now when the script is ran it displays the current files within the directory, and then it list the changed files and their names within the directory.
How can i get the file list only to display when the script has finish processing the new names?
Why does the script not append the proper incremented number to the file name? It renames files the following order:
abc0.jpg
abc1.jpg
abc10.jpg
abc11.jpg
abc12.jpg
abc13.jpg
<?php
$display_file_list;
//Allow user to put choose name
if (isset($_POST['file_prefix'])){
$the_user_prefix = $_POST['file_prefix'];
//open the current directory change this to modify where you are looking
$dir = opendir('.');
$i=0;
//Loop though all the files in the directory
while(false !==($file = readdir($dir)))
{
//This is the way we would like the page to function
//if the extention is .jpg
if(strtolower(pathinfo($file, PATHINFO_EXTENSION)) =='jpg')
{
//Put the JPG files in an array to display to the user
$display_file_list[]= $file;
//Do the rename based on the current iteration
$newName = $the_user_prefix.$i . '.jpg';
rename($file, $newName);
//increase for the next loop
$i++;
}
}
//close the directory handle
closedir($dir);
}else{
echo "No file prefix provided";
}
?>
<html>
<body>
<form action="<?=$_SERVER['PHP_SELF']?>" method="POST">
<input type="text" name="file_prefix"><br>
<input type="submit" value="submit" name="submitMe">
</form>
</body>
</html>
<?php
foreach ($display_file_list as $key => $value) {
echo $value. "<br>";
}
?>
"How can i get the file list only to display when the script has finish processing the new names?"
I think this will work for you;
$path = "path/to/files";
$files = glob("{$path}/*.jpg");
$files_renamed = array();
foreach ($files as $i => $file) {
$name = "{$path}/{$prefix}{$i}.jpg";
if (true === rename($file)) {
$files_renamed[] = $name;
}
}
print_r($files_renamed);

Displaying output of file from a dropdown menu selection

First off, I am totally new to PHP, but so far i love the possibilities i have seen with it.
Ok, heres my problem. I have a script that will automatically scan a folder and show me the file names of that folder in a drop-down menu.
<?php
$dirname = "logs";
$dir = opendir($dirname);
echo '<select name="file2">';
echo '<option value="">Logfiles</option>';
while(false != ($file = readdir($dir)))
{
if(($file != ".") and ($file != ".."))
{
echo "<option value=".$file.">$file</option>";
}
}
echo '</select>';
?>
So far so good, that part works just fine.
I then have a piece of code that would show me the contents of a chosen file.
<?php
$content = file("filenamehere");
$data = implode("<br>",$content);
echo $data;
?>
This part on its own also works fine.
I now want to combine these 2 scripts. This is where i get totally lost. I have tried all various combinations of which variable to put as a "filenamehere", but I only get as far as having my choice from the drop-down echoing the chosen filename in the drop-down button. I have not been able to actually get the 2nd part of the code to display the file contents of the file I have chosen. I tried things like ("$file") for the $content variable but nothing happens.
Of course any other single script solution would also be great. I just need to be able to scan a folder, have its files listed in a drop-down, and once i choose a file i want it to be displayed on the page.
Any help would be deeply appreciated since im a total beginner in PHP.
Cheers.
Am assuming you are doing everything in a single page. I have wrapped a form tag around the select dropdown and added a submit button
echo '<form name="displayfile" action="" method="POST">';
echo '<select name="file2">';
echo '<option value="">Logfiles</option>';
while(false != ($file = readdir($dir)))
{
if(($file != ".") and ($file != ".."))
{
echo "<option value=".$file.">$file</option>";
}
}
echo '</select>';
echo '<input type="submit" value="display file" />';
echo '</form>';
The second part
<?php
//file2 is the name of the dropdown
$selectedfile = #$_POST['file2'];
$content = file($selectedfile);
$data = implode("<br>",$content);
echo $data;
?>
Hope this helps

php directory reading issue

I have written this simple script display all the files in a directory as a set of buttons.
This code reads from the upload directory and displays all files inside a submit button in a form.
$handle = opendir("upload");
echo '<form name="form" method="post" action="download.php">';
while($name = readdir($handle)) {
echo '<input type="submit" name="file" value='.$name.' />';
}
echo '</form>';
Now the issue here is; every time I run the script I find two button at the beginning with contents . and ..
I have not been able to figure out what causes this issue.
What you have encountered are two special files used by the file system.
. represents the current directory you are in.1
.. represents the parent directory of the current directory.2
Footnotes:
1. A path such as "/my_dir/././././././file" is equivalent to "/my_dir/file".
2. A path such as "/my_dir/../my_dir/../my_dir/file" is equivalent to "/my_dir/file" since .. will make you move "up" one level.
To get around the issue of showing these two to your user filter the content returned by readdir using something as the below:
while ($name = readdir ($handle)) {
if ($name == '.' || $name == '..')
continue; /* don't echo anything, skip to next read */
echo '<input type="submit" name="file" value='.$name.' />';
}
the directory listing includes . for the current dir and .. for the parent dir.
I usually use this that i got from the PHP manual (http://php.net/manual/en/function.readdir.php)
if ($handle = opendir('.')) {
while (false !== ($entry = readdir($handle))) {
if ($entry != "." && $entry != "..") {
echo "$entry\n";
}
}
closedir($handle);
}
so what you need to do is to exclude the . and .. from the output.
Another solution is to use a FilesystemIterator like this:
foreach(new FilesystemIterator('upload') as $file){
echo $file;
}
It will automatically skip . and .. entries in the filesystem.

Categories