Single page application with file treeview using ajax/php - javascript

I am currently working with a web-based document management system, I am creating it as a single page using ajax/php connection. I have my file tree view, that displays the folders and files using this code:
if (isset($_GET['displayFolderAndFiles'])) {
function listIt ($path) {
$items = scandir($path);
foreach ($items as $item) {
// Ignore the . and .. folders
if ($item != "." AND $item != "..") {
if (is_file($path . $item)) {
// this is the file
}
else {
// this is the directory
// do the list it again!
echo "<li><span class='fa fa-chevron-right caret'></span><button class='btn-der' id='directory" . $id . "' onclick='directoryAction(this);' value='" . $path . $item . "/'>" . $item . "</button>";
echo "<ul class='nested'>";
listIt($path . $item . "/");
//echo("<input type='text' value='".$path.$item."/'>");
echo "</ul></li>";
}
$id++;
}
}
}
listIt("./My Files/");
}
with this code it is hard for me to manipulate the tree view. I use ajax to get the result.
What I want is to reload the tree view when i add, delete file or folder. I also want to load the page once I do some queries in my application without refreshing the page.
I want to have the functionalities like the sample image, the application is FileRun.
Can someone recommend or suggest some ways to address my problem.
Will I use some javascript library or else?
Reference/Sample: Web-based Document Management System (FileRun)

You can use something like this:
public function treeArr($dir){
// First we get the directory
$paths = scandir($dir, SCANDIR_SORT_NONE);
// We remove .. && . from our array
unset($paths[array_search('.', $paths, true)]);
unset($paths[array_search('..', $paths, true)]);
// Add empty array for our tree
$arr = [];
// Check isour paths array empty
if (count($paths) < 1)
return;
// If not empty we get through all paths and add what we want
foreach($paths as $path){
$current_dir = $dir.'/'.$path;
$isDir = is_dir($current_dir);
$expandable = count( scandir( $current_dir ) ) > 2 ? true : false;
// In my case, I needed path name
// Is it expandable (as is it directory and does it contains or is it empty)
// Is it dir or file, if it is not dir it will be false so its file
// And path for that folder or file
$path_data = [
'name' => $path,
'expandable' => $expandable,
'isDir' => $isDir,
'path' => $current_dir,
];
if($expandable) $path_data['data'] = $this->treeArr($dir.'/'.$path);
// If our dir is expandable we go to read files and folders from in it and call self function with that path
array_push($arr, $path_data);
// At the end we add everything into array
}
return $arr;
}
It works for my needs and on client side you can style and add this as you like.
Within foreach you can check for other things, like file extension, date, size and pass everything you need about that. Like if it is html file and you have some live editor, you can check is it html and if it is add like 'isHTML' => true, and then on front:
if(file.isHTML) { //run the code }

Related

How to save changes on multiple contenteditable elements?

I want to make my photo captions editable and then save the changes in localstorage. It works for the first photo but when I want to edit another, changes are saved to this first photo and not the one I edited.
Here's part of my code where I display photos with captions:
<div class="gallery">
<a href="<?=$path1; ?>">
<img src="<?= $path1; ?>">
</a>
<div id="contenteditable">
<p id="caption-photo" contenteditable> <?=pathinfo($path1, PATHINFO_FILENAME)?></p>
</div>
</div>
And it is my js code to save the changes:
const editables = document.querySelectorAll("[contenteditable]");
editables.forEach(el => {
el.addEventListener("blur", () => {
localStorage.setItem("dataStorage-" + el.id, el.innerHTML);
})
});
for (var key in localStorage) {
if (key.includes("dataStorage-")) {
const id = key.replace("dataStorage-","");
document.querySelector("#" + id).innerHTML = localStorage.getItem(key);
}
}
Okay lemme open your mind a bit ... also pardon me for the many answers, I just like comprehensive answers:
Create a new php file with the name update_file.php and put it within the same directory. Then put the php code that handles the requests in this file.
Why is this important? Because we are depending on the responseText being received from the php file which will tell us whether the request was successful or not. And, when the php that handles the request is within the same file as the html, the request will return a response containing the html content of the page --- You can experiment this yourself and see that if you put the code below within the same file, the responseText will be the whole code in the page, and that's not what we want, we want either 0 or 1 ... ok, enough said:
Put this in update_file.php in the same directory as the current file:
<?php
if(isset($_GET["old_name"]) && isset($_GET["new_name"]) && isset($_GET["directory"]) && isset($_GET["extension"])){
$old_name = $_GET["old_name"];
$new_name = $_GET["new_name"];
// The new values from the GET request
$directory = $_GET["directory"];
$extension = $_GET["extension"];
// echo $directory;
// echo $extension;
// Concatenate the values with the directories
// 1 thing to note, if the image is in the current directory,
// the value you will receive for $directory will be "."
// So the method below ensures flexibility with different directories
$dot = ".";
// $dot is added before the extension
$slash = "/";
// $slash is added after the directory name
// if ".", it will be "./' -- for current directory
// if "some/directory" , it will be "some/directory/"
// Then add to the file name and the extension
$full_oldname = $directory . $slash . $old_name . $dot . $extension;
$full_newname = $directory . $slash . $new_name . $dot . $extension;
echo rename($full_oldname, $full_newname);
}
?>
Then this is your current file:
<?php
$some_records = ["record1.jpeg", "uploads/record2.jpeg", "images/subdirectory1/record3.jpeg", "images/record4.jpg", "images/subdirectory2/record5.jpg"];
// Assuming your directory has subdirectories
$counter = 1;
?>
<div class="gallery">
<?php foreach ($some_records as $path) {
echo '<a href="' . $path . '">
<img src="' . $path . '">
</a>
<div id="contenteditable">
<p id="caption-photo' . $counter . '" contenteditable>' . pathinfo($path, PATHINFO_FILENAME) . '</p>
<input type="hidden" name="directory-"' . $counter . '" value="' . pathinfo($path, PATHINFO_DIRNAME) . '" />
<input type="hidden" name="extension-"' . $counter . '" value="' . pathinfo($path, PATHINFO_EXTENSION) . '" />
</div>';
// Note the 2 hidden inputs above
$counter++;
}
?>
</div>
<script>
function update_file(old_name, new_name, directory, extension) {
// The function has been updated with the new parameters
var update = new XMLHttpRequest();
update.open("GET", "update_file.php?old_name=" + old_name + "&new_name=" + new_name + "&directory=" + directory + "&extension=" + extension, true);
// The XHR request has also been updated with the new values
update.onreadystatechange = function() {
if (this.readyState == this.DONE && this.status == 200) {
if (this.responseText != null) {
console.log(this.responseText);
// This will either be 0 or 1, if correct values have been passed to it
if(this.responseText == 1){
console.log("Renamed successfully!");
}
else {
console.log("Error renaming!");
}
}
else {
console.log("Not sent!!");
}
}
};
update.send();
}
document.addEventListener("DOMContentLoaded", function(){
const editables = document.querySelectorAll("[contenteditable]");
editables.forEach(el => {
var curr = el.innerHTML;
// The next element after "el" is the first hidden input with the directory
var dir = el.nextElementSibling.value;
// The next element after the above hidden input is the second hidden input with the extension
// Note the 2 ".nextElementSibling" accessors
var ext = el.nextElementSibling.nextElementSibling.value;
// To get the value of any input(that can lie within a form control) use "[element].value'
console.log(ext);
console.log(dir);
el.addEventListener("blur", () => {
localStorage.setItem("dataStorage-" + el.id, el.innerHTML);
console.log(el.innerHTML);
update_file(curr, el.innerHTML, dir, ext);
})
});
for(var i = 1; i < editables.length+1; i++){
console.log("dataStorage-caption-photo" + i + " => " + localStorage.getItem("dataStorage-caption-photo" + i));
}
});
</script>
Also read the comments, the main points here are:
How to add the hidden input elements with the respective values
How to access the values with javascript
How to add them to the XHR request and then capture them with php
Finally, how to concatenate them and get your full path
Hope this helps
Here is some developer tools you can be using, I'm not sure which browser you are using but you might find such a similar thing, go to the console tab then click on settings and enable logging of XMLHttpRequests
You will be able to see the requests right after you remove focus on any contenteditable element and confirm whether they are being sent
Here's a suggestion: suppose your foreach loop is:
<?=
#Other php stuff here
#You can define a counter variable:
$counter = 1;
?>
<div class="gallery">
<?= foreach($some_records as $your_value){ ?>
<a href="<?=$path1; ?>">
<img src="<?= $path1; ?>">
</a>
<div id="contenteditable">
#Which you can append to the caption-photo
#So the id of the first will be: caption-photo1
<p id="caption-photo<?= $counter ?>" contenteditable> <?=pathinfo($path1, PATHINFO_FILENAME)?></p>
<?=
$counter++;
#At the end of each loop, increase the value of counter by 1
}
?>
</div>
Also don't mind the foreach loop, I'm sure it doesn't match yours, but just get the aspect of appending a number to the id so that you can get different values
Okay since that doesn't work, here is a snippet of the above but without using the short_open_tags(<?= ... =>), Instead I just used the php(<?php ... ?>) tags as they are:
Try running the code as it is first and check the logs
<?php
$some_records = ["record1.jpg", "record2.jpg", "record3.jpg", "record4.jpg", "record5.jpg"];
$counter = 1;
?>
<div class="gallery">
<?php foreach ($some_records as $path) {
echo '<a href="' . $path . '">
<img src="' . $path . '">
</a>
<div id="contenteditable">
<p id="caption-photo' . $counter . '" contenteditable>' . pathinfo($path, PATHINFO_FILENAME) . '</p>
</div>';
$counter++;
}
?>
</div>
<script>
document.addEventListener("DOMContentLoaded", function(){
const editables = document.querySelectorAll("[contenteditable]");
editables.forEach(el => {
el.addEventListener("blur", () => {
localStorage.setItem("dataStorage-" + el.id, el.innerHTML);
console.log(el.innerHTML);
//When you change the value, it is logged
})
});
/* The loop below does not work as expected */
// for (var key in localStorage) {
// if (key.includes("dataStorage-")) {
// const id = key.replace("dataStorage-", "");
// document.querySelector("#" + id).innerHTML = localStorage.getItem(key);
// ---- The above direct way of selecting the element does not work
// console.log(localStorage.getItem(key));
// }
// }
/* 1 is added to editables.length because the counter starts
from 1 and not 0 */
for(var i = 1; i < editables.length+1; i++){
console.log("dataStorage-caption-photo" + i + " => " + localStorage.getItem("dataStorage-caption-photo" + i));
//This logs all the values associated with the elements
}
});
</script>
This is how the elements are set in the above code:
As you can see the counter is working ok.
And based on your loop that is commented out, this part returns an error because that "direct" method of accessing elements does not work:
Here is some code to assist you:
Place this one before everything in your php file
if(isset($_GET["old_name"]) && isset($_GET["new_name"])){
$old_name = $_GET["old_name"];
// The same variables in the `GET` request
$new_name = $_GET["new_name"];
// Also remember the extension of the files
$ext = ".jpeg";
// As for rename you have to give the full path
// If your folder is uploads/, and the file is oldname.jpg
// Then it should be rename("uploads/" . $old_name $ext, "uploads/" . $new_name . $ext);
// Just provide the full path to the file you are renaming
// If the images are within the same directory, its ok as it is
echo rename($old_name . $ext, $new_name . $ext);
// returns true if successful
// returns false if not successful
// This is the "responseText" that Javascript will be logging
}
And add this function at the very beginning of your <script> tags:
function update_file(old_name, new_name) {
var update = new XMLHttpRequest();
// Initialize the request
update.open("GET", "?old_name=" + old_name + "&new_name=" + new_name, true);
// Put the variables in the request which will be captured by php
update.onreadystatechange = function() {
if (this.readyState == this.DONE && this.status == 200) {
if (this.responseText != null) {
if(this.responseText){
console.log("Renamed successfully!");
}
else {
console.log("Error renaming!");
}
}
else {
console.log("Not sent!!");
}
}
};
update.send();
}
Then change this section of the code to this:
const editables = document.querySelectorAll("[contenteditable]");
editables.forEach(el => {
var curr = el.innerHTML;
// Get the current value before it changes
el.addEventListener("blur", () => {
localStorage.setItem("dataStorage-" + el.id, el.innerHTML);
console.log(el.innerHTML);
update_file(curr, el.innerHTML);
// Call the XMLHttpRequest with the values
})
});
This should give you a head start on how to use XHR requests (that's if you don't use them) ... This is only because for you to get data from javascript to php it has to be through GET or POST ... but data from php to javascript is as easy as putting php tags with the value inside of it.
Also there are literally comments everywhere so you can read and understand how and why it works. Don't hesitate to ask for help.
----------
Ok, as you work on that, here's a tip -- within the foreach loop, you can echo a hidden input as so: ... <input type="hidden" name="directory-' . $counter . ' " value=" ' . pathinfo($path1, PATHINFO_DIRNAME) . ' "/> (assuming this code is inside the foreach loop) ... and also a same hidden input below it for the extension, since images may have different extensions --(PATHINFO_EXTENSION) then instead of "directory-(number)", you have "extension-(number)" ... then update the request to send also the extension and the directory -- [update_info(old_name, new_name, extension, directory)] ... meaning you also update the XHR request to send those too ... And on the php side, you just concatenate the directory with the file name and the extension received.
Now when it comes to actually getting the values from the hidden inputs, remember there is a counter, so based on the value of el.id, which ends with a specific number, you take the value of the hidden input which has a name ending with the same number (this is within the forEach loop) for example for the value of extension, take value whose name attribute starts with "extension-" and ends with the same value as the one for el.id ... do this as well for the directory
Another option is to take the el.nextElementSibling.value for the first hidden input and then the next one too, and those will be the values for directory and extension...
I can actually write all of them down in code but it won't be a learning process for you, happy coding :) ... Hope you can understand the concept in the above explanations

Typeahead.js -- refresh data

I have managed to get this code to work for an application -- http://twitter.github.io/typeahead.js/ but, the problem I have run into, is that I need to be able to re-load the data. I would rather not duplicate the code to do this, although it's an option, it seems kind of silly. I have inserted some PHP into my JavaScript to create an array for the dropdown list, and it works great. But if I stuff it into a function, and then try to call the function in the document open, it doesn't work (nothing appears to happen).
$(document).ready(function()
{
// call function when page loads(?)
getAwards();
}); // end document.ready ...
This is the PHP code, if I comment out the JavaScript function parts it runs and the typeahead code sees the value of the array. If I put it into the function, it doesn't execute despite being called above, and I have no data for the typeahead code ...
function getAwards(
{
// build array for use with typeahead:
<?php
$sql_statement = "select title from awards order by title desc limit 1";
$aw_result = mysqli_query( $connect, $sql_statement );
$aw_row = mysqli_fetch_array( $aw_result );
$last_award = $aw_row["title"];
// need to rewind table:
mysqli_data_seek( $aw_result, 0);
// start from the top:
$sql_statement = "select title from awards order by title";
$aw_result = mysqli_query( $connect, $sql_statement );
$count = 0;
$out = 'awards = [';
while ( $aw_row = mysqli_fetch_array( $aw_result ) )
{
// the quotes deal with forcing this to handle
// branch names with apostrophes in them ...
$count++;
$out .= '"'. $aw_row["title"] . '"';
if( $aw_row["title"] != $last_award )
{
$out .= ',';
}
}
$out .= ']';
echo $out . "\n";
?>
})
I need to be able to update the data, and reload the list while working on the form (I am working that out in my fuzzy brain, but anyway I'll get to that -- currently intend to click a button to update the list used by the typeahead, which is why I want a function ...)
Any suggestions are gratefully accepted. I am at a loss ...

Lost variable i script

I have some problem with a variable in my script I need to "send" the variable through some different files
The variable comes from the link:
index.php?email=emailname#emailname.com
The script is a file upload script. It's using 3 files in the process, please here:
/public_html/upload/index.php
/public_html/upload/content/index.php
/public_html/upload/content/UploadHandler.php
/public_html/upload/index.php is that runs the script and where the variable is received from the link:
index.php?email=emailname#emailname.com
The file code of /public_html/upload/index.php looks like:
<?php
// change the name below for the folder you want
$dir = "content/".$_GET["email"];
$file_to_write = 'test.txt';
$content_to_write = "The content";
if( is_dir($dir) === false )
{
mkdir($dir);
}
$file = fopen($dir . '/' . $file_to_write,"w");
// a different way to write content into
// fwrite($file,"Hello World.");
fwrite($file, $content_to_write);
// closes the file
fclose($file);
// this will show the created file from the created folder on screen
include $dir . '/' . $file_to_write;
$_SESSION['tmem']= $_GET["email"];
?>
I know that $_GET["email"] works since I can the code: <?=$_GET["email"]?> on the index.php file to see if it receive the variable.
The code:
$_SESSION['tmem']= $_GET["email"];
should forward the variable to the next file:
/public_html/upload/content/index.php
that looks like this:
session_start();
$dir = "content/" . $_GET["email"];
error_reporting(E_ALL | E_STRICT);
require('UploadHandler.php');
$upload_handler = new UploadHandler( array ('upload_url' =>$dir) );
And that code should forward the variable to the codes of the script where the upload patch is. Codes on the file: /public_html/upload/content/UploadHandler.php looks like:
'script_url' => $this->get_full_url().'/'.$this->basename($this->get_server_var('SCRIPT_NAME')),
'upload_dir' => dirname($this->get_server_var('SCRIPT_FILENAME')).'/'.$_GET['email'].'/',
'upload_url' => $this->get_full_url().'/'.$_GET['email'].'/',
'input_stream' => 'php://input',
'user_dirs' => false,
'mkdir_mode' => 0755,
'param_name' => 'files',
Can somebody see where in the process I lose the variable?
I think, in this part of code:
session_start();
$dir = "content/" . $_GET["email"];
you try to get your "email" from URI instead of getting it from session (tmem).

Recognising strings between certain tags in external file -PHP

I am new to php, and want a script that can recognise text between certain tags in an external file.
I managed to find an answer here, that recognises the text in tags in a set string, but I am unsure of how to get the file to recognise the tags in an external text file.
PHP:
<?php
function innerh($string, $start, $end){
$string = " ".$string;
$ini = strpos($string,$start);
if ($ini == 0) return "";
$ini += strlen($start);
$len = strpos($string,$end,$ini) - $ini;
return substr($string,$ini,$len);
}
$fullstring = "The <tag>Output</tag>"; // this is the string
$parsed = innerh($fullstring, "<tag>", "</tag>");
echo $parsed;
?>
External File:
<tag>This text</tag> <!-- This is the target -->
Similar to what you are already doing. Currently you are making a string with that tag and when you want to read it from a file you can simply do
$fullstring = file_get_contents('your-file.html');
No other changes are required. You might need to provide full path of that file but that's about it.
That function reads a file and returns its contents in a string which you can save in your variable just like you built the variable manually.
Your code must be somthing like this:
<?php
function innerh($string, $start, $end){
$string = " ".$string;
$ini = strpos($string,$start);
if ($ini == 0) return "";
$ini += strlen($start);
$len = strpos($string,$end,$ini) - $ini;
return substr($string,$ini,$len);
}
// Open a file with READ-ONLY flag ("r") and start of begining for read.
// See: http://php.net/manual/en/function.fopen.php
$fp = fopen("/path/to/file", "r");
// Check that file is opened and ready for read
if ($fp) {
// Until we have content on file, we resume reading
while (!feof($fp)) {
// Read from file, line by line.
// See: http://php.net/manual/en/function.fgets.php
$line = fgets($fp);
// Process line by line and print result
$parsed = innerh($line, "<tag>", "</tag>");
echo $parsed;
/* If your input file is a file without a new line or something like it,
just add a `$line = '';` before while line and change read line with
`$line .= fgets($fp);`, also remove process line and print line. After
that your file is on $line variable ;). */
}
}
?>

PHP random file display

I am using the same script from the link given below to display the files in the directory.
Link list based on directory without file extension and hyphens in php
$directory = 'folder/';
$blacklist = array(
'index'
);
foreach (glob($directory . "*.php") as $file) {
$parts = pathinfo($file);
if (!in_array($parts['filename'], $blacklist)) {
$name = preg_replace("/-/", "", $parts['filename']);
echo "<li>{$name}</li>";
}
}
The above script displays all the files (except index.php) in a folder. But I just want to display five random files. Is this possible?
http://s30.postimg.org/hbb1ce67l/screen.jpg
Based off your edit, I think this is what you're trying to do?
<?php
// Get all files ending in .php in the directory
$rand_files = glob("*.php");
// If the string "index.php" is contained in these results, remove it
// array_search returns the key of the $needle parameter (or false) if not found
if (($location = array_search("index.php", $rand_files)) !== false) {
// If "index.php" was in the results, then delete it from the array using unset()
unset($rand_files[$location]);
}
// Randomly choose 5 of the remaining files:
foreach (array_rand($rand_files, 5) as $rand_index) {
$fname = $rand_files[$rand_index];
echo "<a href='$fname'>$fname</a>\n";
}
?>

Categories

Resources