$dir = "data"; if ( getenv("TODO_PER_USER_FOLDERS") == 'TRUE') { $dir = "data/".$_SERVER['PHP_AUTH_USER']; if (!file_exists($dir)) { mkdir($dir, 0777); } } if ($_SERVER['REQUEST_METHOD'] == 'POST') { if (isset($_POST["filename"])) { updateFile($_POST, $dir); } elseif (isset($_POST["delete"])) { deleteFile($_POST, $dir); } } else { dirPoller($dir); } function updateFile($update, $dir) { $filename = basename($update["filename"]); if (endsWith($filename, ".txt")) { file_put_contents($dir . "/" . $filename, isset($update["content"]) ? $update["content"] : ""); echo json_encode(microtime(True)); } else { echo json_encode(null); } } function deleteFile($delete, $dir) { $filename = $dir . "/" . basename($delete["delete"]); if (endsWith($filename, ".txt") && file_exists($filename)) { unlink($filename); echo json_encode(microtime(True)); } else { echo json_encode(null); } } function dirPoller($datadir) { // hang for a maximum of 30 seconds $live_for = isset($_GET['live_for']) ? min((int)$_GET['live_for'], 180) : 30; // tell PHP it can timeout after 30 seconds set_time_limit($live_for); ini_set("max_execution_time", $live_for); // whether the loop has returned anything or not $has_returned = false; // start the wait-poll loop while ($live_for--) { // if ajax request has send a timestamp, then $last_ajax_call = timestamp, else $last_ajax_call = null $last_ajax_call = isset($_GET['timestamp']) ? (float)$_GET['timestamp'] : null; // get timestamp of when file has been changed the last time $last_change_in_data_files = dirTimestamp($datadir); // if no timestamp delivered via ajax or data.txt has been changed SINCE last ajax timestamp if ($last_ajax_call == null || (int)($last_change_in_data_files * 1000) > (int)($last_ajax_call * 1000)) { // get content of data.txt $data = dirFiles($datadir); // put data.txt's content and timestamp of last data.txt change into array $data["timestamp"] = $last_change_in_data_files; // encode to JSON, render the result (for AJAX) echo json_encode($data); // leave this loop step $has_returned = true; break; } else { // block for 1 second sleep(1); continue; } } if (!$has_returned) { echo json_encode(array("timestamp" => $last_change_in_data_files)); } } // Open a directory, and read its contents // http://www.w3schools.com/php/func_directory_readdir.asp function dirFiles($datadir) { $files = array(); $creation_timestamps = array(); if ($dh = opendir($datadir)) { while (($filename = readdir($dh)) !== false){ if (endsWith($filename, ".txt")) { $filepath = $datadir . "/" . $filename; $files[$filename] = file_get_contents($filepath); $creation_timestamps[$filename] = filectime($filepath); } } closedir($dh); } return Array("files" => $files, "creation_timestamps" => $creation_timestamps); } function dirTimestamp($datadir) { // PHP caches file data, like requesting the size of a file, by default. clearstatcache() clears that cache clearstatcache(); $timestamp = filemtime_precise($datadir . "/."); if ($dh = opendir($datadir)){ while (($filename = readdir($dh)) !== false){ if (endsWith($filename, ".txt")) { $filepath = $datadir . "/" . $filename; $file_timestamp = filemtime_precise($filepath); if ($file_timestamp > $timestamp) { $timestamp = $file_timestamp; } } } closedir($dh); } return $timestamp; } // test if a string ends with another string // http://stackoverflow.com/a/619725/2131094 function endsWith($string, $test) { $strlen = strlen($string); $testlen = strlen($test); if ($testlen > $strlen) return false; return substr_compare($string, $test, $strlen - $testlen, $testlen) === 0; } // get precise file modification time (including milliseconds) // http://stackoverflow.com/a/20248406/2131094 function filemtime_precise($path){ $dateUnix = @shell_exec('stat --format "%y" \'' . $path . '\''); $date = explode(".", $dateUnix); return (float)(filemtime($path).".".substr(isset($date[1]) ? $date[1] : "00000000", 0, 8)); } function cors() { // Allow from any origin if (isset($_SERVER['HTTP_ORIGIN'])) { header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}"); header('Access-Control-Allow-Credentials: true'); header('Access-Control-Max-Age: 86400'); // cache for 1 day } // Access-Control headers are received during OPTIONS requests if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') { if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS"); if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}"); exit(0); } } ?>