'),array('_'),$title);
$title = trim($title);
// Remove control characters
return preg_replace( '#[[:cntrl:]]#u', '', $title ) ; // [\x00-\x1F\x7F]
}
/**
* Clean a string of html that may be used as file content
*
* @param string $text The string to be cleansed. Passed by reference
*/
public static function CleanText(&$text){
\gp\tool\Editing::tidyFix($text);
self::rmPHP($text);
self::FixTags($text);
$text = \gp\tool\Plugins::Filter('CleanText',array($text));
}
/**
* Use html parser to check the validity of $text
*
* @param string $text The html content to be checked. Passed by reference
*/
public static function FixTags(&$text){
$gp_html_output = new \gp\tool\Editing\HTML($text);
$text = $gp_html_output->result;
}
/**
* Remove php tags from $text
*
* @param string $text The html content to be checked. Passed by reference
*/
public static function rmPHP(&$text){
$search = array('','');
$replace = array('<?','<?php','?>');
$text = str_replace($search,$replace,$text);
}
/**
* Removes any NULL characters in $string.
* @since 3.0.2
* @param string $string
* @return string
*/
public static function NoNull($string){
$string = preg_replace('/\0+/', '', $string);
return preg_replace('/(\\\\0)+/', '', $string);
}
/**
* Save the content for a new page in /data/_pages/
* @since 1.8a1
*
*/
public static function NewTitle($title, $section_content = false, $type='text'){
// get the file for the title
if( empty($title) ){
return false;
}
$file = self::PageFile($title);
if( !$file ){
return false;
}
// organize section data
$file_sections = array();
if( is_array($section_content) && isset($section_content['type']) ){
$file_sections[0] = $section_content;
}elseif( is_array($section_content) ){
$file_sections = $section_content;
}else{
$file_sections[0] = array(
'type' => $type,
'content' => $section_content
);
}
// add meta data
$meta_data = array(
'file_number' => self::NewFileNumber(),
'file_type' => $type,
);
return self::SaveData($file,'file_sections',$file_sections,$meta_data);
}
/**
* Return the data file location for a title
* Since v4.6, page files are within a subfolder
* As of v2.3.4, it defaults to an index based file name but falls back on title based file name for installation and backwards compatibility
*
*
* @param string $title
* @return string The path of the data file
*/
public static function PageFile($title){
global $dataDir, $config, $gp_index;
$index_path = false;
if( gp_index_filenames && isset($gp_index[$title]) && isset($config['gpuniq']) ){
// page.php
$index_path = $dataDir.'/data/_pages/'.substr($config['gpuniq'],0,7).'_'.$gp_index[$title].'/page.php';
if( file_exists($index_path) ){
return $index_path;
}
// without folder -> rename it
$old_index = $dataDir.'/data/_pages/'.substr($config['gpuniq'],0,7).'_'.$gp_index[$title].'.php';
if( file_exists($old_index) ){
if( self::Rename($old_index, $index_path) ){
return $index_path;
}
return $old_index;
}
}
//using file name instead of index
$normal_path = $dataDir.'/data/_pages/'.str_replace('/','_',$title).'/page.php';
if( !$index_path || self::Exists($normal_path) ){
return $normal_path;
}
//without folder -> rename it
$old_path = $dataDir.'/data/_pages/'.str_replace('/','_',$title).'.php';
if( self::Exists($old_path) ){
if( $index_path && self::Rename($old_path, $index_path) ){
return $index_path;
}
if( self::Rename($old_path, $normal_path) ){
return $normal_path;
}
return $old_path;
}
return $index_path;
}
public static function NewFileNumber(){
global $config;
if( !isset($config['file_count']) ){
$config['file_count'] = 0;
}
$config['file_count']++;
\gp\admin\Tools::SaveConfig();
return $config['file_count'];
}
/**
* Get the meta data for the specified file
*
* @param string $file
* @return array
*/
public static function GetTitleMeta($file){
self::Get($file,'meta_data');
return self::$last_meta;
}
/**
* Return an array of info about the data file
*
*/
public static function GetFileStats($file){
$file_stats = self::Get($file,'file_stats');
if( $file_stats ){
return $file_stats;
}
return array('created'=> time());
}
/**
* Save a file with content and data to the server
* This function will be deprecated in future releases. Using it is not recommended
*
* @param string $file The path of the file to be saved
* @param string $contents The contents of the file to be saved
* @param string $code The data to be saved
* @param string $time The unix timestamp to be used for the $fileVersion
* @return bool True on success
*/
public static function SaveFile($file,$contents,$code=false,$time=false){
$result = self::FileStart($file,$time);
if( $result !== false ){
$result .= "\n".$code;
}
$result .= "\n\n?".">\n";
$result .= $contents;
return self::Save($file,$result);
}
/**
* Save raw content to a file to the server
*
* @param string $file The path of the file to be saved
* @param string $contents The contents of the file to be saved
* @return bool True on success
*/
public static function Save($file,$contents){
global $gp_not_writable;
if( !self::WriteLock() ){
return false;
}
$exists = self::Exists($file);
//make sure directory exists
if( !$exists ){
$dir = \gp\tool::DirName($file);
if( !file_exists($dir) ){
self::CheckDir($dir);
}
}
$fp = @fopen($file,'wb');
if( $fp === false ){
$gp_not_writable[] = $file;
return false;
}
if( !$exists ){
@chmod($file,gp_chmod_file);
}elseif( function_exists('opcache_invalidate') && substr($file,-4) === '.php' ){
opcache_invalidate($file);
}
$return = fwrite($fp,$contents);
fclose($fp);
return ($return !== false);
}
/**
* Rename a file
* @since 4.6
*/
public static function Rename($from,$to){
global $gp_not_writable;
if( !self::WriteLock() ){
return false;
}
//make sure directory exists
$dir = \gp\tool::DirName($to);
if( !file_exists($dir) && !self::CheckDir($dir) ){
return false;
}
return rename($from, $to);
}
/**
* Replace $to with $from
*
*/
public static function Replace($from, $to){
$temp_dir = '';
// move the $to out of the way if it exists
if( file_exists($to) ){
$temp_dir = $to.'_'.time();
if( !self::rename($to,$temp_dir) ){
return false;
}
}
// rename $from -> $to
if( !self::rename($from, $to) ){
if( $temp_dir ){
self::rename( $temp_dir, $to );
}
return false;
}
if( !empty($temp_dir) ){
self::RmAll($temp_dir);
}
return true;
}
/**
* Get a write lock to prevent simultaneous writing
* @since 3.5.3
*/
public static function WriteLock(){
if( defined('gp_has_lock') ){
return gp_has_lock;
}
$expires = gp_write_lock_time;
if( self::Lock('write',gp_random,$expires) ){
define('gp_has_lock',true);
return true;
}
trigger_error('CMS write lock could not be obtained.');
define('gp_has_lock',false);
return false;
}
/**
* Get a lock
* Loop and delay to wait for the removal of existing locks (maximum of about .2 of a second)
*
*/
public static function Lock($file,$value,&$expires){
global $dataDir;
$tries = 0;
$lock_file = $dataDir.'/data/_lock_'.sha1($file);
$file_time = 0;
$elapsed = 0;
while($tries < 1000){
if( !file_exists($lock_file) ){
file_put_contents($lock_file,$value);
usleep(100);
}elseif( !$file_time ){
$file_time = filemtime($lock_file);
}
$contents = @file_get_contents($lock_file);
if( $value === $contents ){
@touch($lock_file);
return true;
}
if( $file_time ){
$elapsed = time() - $file_time;
if( $elapsed > $expires ){
@unlink( $lock_file);
}
}
clearstatcache();
usleep(100);
$tries++;
}
if( $file_time ){
$expires -= $elapsed;
}
return false;
}
/**
* Remove a lock file if the value matches
*
*/
public static function Unlock($file,$value){
global $dataDir;
$lock_file = $dataDir.'/data/_lock_'.sha1($file);
if( !file_exists($lock_file) ){
return true;
}
$contents = @file_get_contents($lock_file);
if( $contents === false ){
return true;
}
if( $value === $contents ){
unlink($lock_file);
return true;
}
return false;
}
/**
* Save array(s) to a $file location
* Takes 2n+3 arguments
*
* @param string $file The location of the file to be saved
* @param string $varname The name of the variable being saved
* @param array $array The value of $varname to be saved
*
* @deprecated 4.3.5
*/
public static function SaveArray(){
if( gp_data_type === '.json' ){
throw new Exception('SaveArray() cannot be used for json data. Use SaveData() instead');
}
$args = func_get_args();
$count = count($args);
if( ($count %2 !== 1) || ($count < 3) ){
trigger_error('Wrong argument count '.$count.' for \gp\tool\Files::SaveArray() ');
return false;
}
$file = array_shift($args);
$file_stats = array();
$data = '';
while( count($args) ){
$varname = array_shift($args);
$array = array_shift($args);
if( $varname == 'file_stats' ){
$file_stats = $array;
}else{
$data .= self::ArrayToPHP($varname,$array);
$data .= "\n\n";
}
}
$data = self::FileStart($file,time(),$file_stats).$data;
return self::Save($file,$data);
}
/**
* Save array to a $file location
*
* @param string $file The location of the file to be saved
* @param string $varname The name of the variable being saved
* @param array $array The value of $varname to be saved
* @param array $meta meta data to be saved along with $array
*
*/
public static function SaveData($file, $varname, $array, $meta = array() ){
$file = self::FilePath($file);
if( gp_data_type === '.json' ){
$json = self::FileStart_Json($file);
$json[$varname] = $array;
$json['meta_data'] = $meta;
$content = json_encode($json);
}else{
$content = self::FileStart($file);
$content .= self::ArrayToPHP($varname,$array);
$content .= "\n\n";
$content .= self::ArrayToPHP('meta_data',$meta);
}
return self::Save($file,$content);
}
/**
* Experimental
*
*/
private static function FileStart_Json($file, $time = null ){
global $gpAdmin;
if( is_null($time) ) $time = time();
//file stats
$file_stats = self::GetFileStats($file);
$file_stats['gpversion'] = gpversion;
$file_stats['modified'] = $time;
$file_stats['username'] = false;
if( \gp\tool::loggedIn() ){
$file_stats['username'] = $gpAdmin['username'];
}
$json = array();
$json['file_stats'] = $file_stats;
return $json;
}
/**
* Return the beginning content of a data file
*
*/
public static function FileStart($file, $time=null, $file_stats = array() ){
global $gpAdmin;
if( is_null($time) ) $time = time();
//file stats
$file_stats = (array)$file_stats + self::GetFileStats($file);
$file_stats['gpversion'] = gpversion;
$file_stats['modified'] = $time;
if( \gp\tool::loggedIn() ){
$file_stats['username'] = $gpAdmin['username'];
}else{
$file_stats['username'] = false;
}
return '<'.'?'.'php'
. "\ndefined('is_running') or die('Not an entry point...');"
. "\n".'$fileVersion = \''.gpversion.'\';' /* @deprecated 3.0 */
. "\n".'$fileModTime = \''.$time.'\';' /* @deprecated 3.0 */
. "\n".self::ArrayToPHP('file_stats',$file_stats)
. "\n\n";
}
public static function ArrayToPHP($varname,&$array){
return '$'.$varname.' = '.var_export($array,true).';';
}
/**
* Insert a key-value pair into an associative array
*
* @param mixed $search_key Value to search for in existing array to insert before
* @param mixed $new_key Key portion of key-value pair to insert
* @param mixed $new_value Value portion of key-value pair to insert
* @param array $array Array key-value pair will be added to
* @param int $offset Offset distance from where $search_key was found. A value of 1 would insert after $search_key, a value of 0 would insert before $search_key
* @param int $length If length is omitted, nothing is removed from $array. If positive, then that many elements will be removed starting with $search_key + $offset
* @return bool True on success
*/
public static function ArrayInsert($search_key,$new_key,$new_value,&$array,$offset=0,$length=0){
$array_keys = array_keys($array);
$array_values = array_values($array);
$insert_key = array_search($search_key,$array_keys);
if( ($insert_key === null) || ($insert_key === false) ){
return false;
}
array_splice($array_keys,$insert_key+$offset,$length,$new_key);
array_splice($array_values,$insert_key+$offset,$length,'fill'); //use fill in case $new_value is an array
$array = array_combine($array_keys, $array_values);
$array[$new_key] = $new_value;
return true;
}
/**
* Replace a key-value pair in an associative array
* ArrayReplace() is a shortcut for using \gp\tool\Files::ArrayInsert() with $offset = 0 and $length = 1
*/
public static function ArrayReplace($search_key,$new_key,$new_value,&$array){
return self::ArrayInsert($search_key,$new_key,$new_value,$array,0,1);
}
/**
* Check recursively to see if a directory exists, if it doesn't attempt to create it
*
* @param string $dir The directory path
* @param bool $index Whether or not to add an index.hmtl file in the directory
* @return bool True on success
*/
public static function CheckDir($dir,$index=true){
global $config;
if( !file_exists($dir) ){
$parent = \gp\tool::DirName($dir);
self::CheckDir($parent,$index);
//ftp mkdir
if( isset($config['useftp']) ){
if( !self::FTP_CheckDir($dir) ){
return false;
}
}else{
if( !@mkdir($dir,gp_chmod_dir) ){
return false;
}
@chmod($dir,gp_chmod_dir); //some systems need more than just the 0755 in the mkdir() function
}
// make sure there's an index.html file
// only check if we just created the directory, we don't want to keep creating an index.html file if a user deletes it
if( $index && gp_dir_index ){
$indexFile = $dir.'/index.html';
if( !file_exists($indexFile) ){
//not using \gp\tool\Files::Save() so we can avoid infinite looping (it's safe since we already know the directory exists and we're not concerned about the content)
file_put_contents($indexFile,'');
@chmod($indexFile,gp_chmod_file);
}
}
}
return true;
}
/**
* Remove a directory
* Will only work if directory is empty
*
*/
public static function RmDir($dir){
global $config;
//ftp
if( isset($config['useftp']) ){
return self::FTP_RmDir($dir);
}
return @rmdir($dir);
}
/**
* Remove a file or directory and it's contents
*
*/
public static function RmAll($path){
if( empty($path) ) return false;
if( is_link($path) ) return @unlink($path);
if( !is_dir($path) ) return @unlink($path);
$success = true;
$subDirs = array();
//$files = scandir($path);
$files = self::ReadDir($path,false);
foreach($files as $file){
$full_path = $path.'/'.$file;
if( !is_link($full_path) && is_dir($full_path) ){
$subDirs[] = $full_path;
continue;
}
if( !@unlink($full_path) ){
$success = false;
}
}
foreach($subDirs as $subDir){
if( !self::RmAll($subDir) ){
$success = false;
}
}
if( $success ){
return self::RmDir($path);
}
return false;
}
/**
* Get the correct path for the data file
* Two valid methods to get a data file path:
* Full path: /var/www/html/site/data/_site/config.php
* Relative: _site/config
*
*/
public static function FilePath($path){
global $dataDir;
if( substr($path,-4) !== '.php' ){
$path = $dataDir.'/data/'.ltrim($path,'/').'.php';
}
if( gp_data_type === '.json' ){
$path = substr($path,0,-4).'.gpjson';
}
return $path;
}
/* FTP Function */
public static function FTP_RmDir($dir){
$conn_id = self::FTPConnect();
$dir = self::ftpLocation($dir);
return ftp_rmdir($conn_id,$dir);
}
public static function FTP_CheckDir($dir){
$conn_id = self::FTPConnect();
$dir = self::ftpLocation($dir);
if( !ftp_mkdir($conn_id,$dir) ){
return false;
}
return ftp_site($conn_id, 'CHMOD 0777 '. $dir );
}
public static function FTPConnect(){
global $config;
static $conn_id = false;
if( $conn_id ){
return $conn_id;
}
if( empty($config['ftp_server']) ){
return false;
}
$conn_id = @ftp_connect($config['ftp_server'],21,6);
if( !$conn_id ){
//trigger_error('ftp_connect() failed for server : '.$config['ftp_server']);
return false;
}
$login_result = @ftp_login($conn_id,$config['ftp_user'],$config['ftp_pass'] );
if( !$login_result ){
//trigger_error('ftp_login() failed for server : '.$config['ftp_server'].' and user: '.$config['ftp_user']);
return false;
}
register_shutdown_function(array('\\gp\\tool\\Files','ftpClose'),$conn_id);
return $conn_id;
}
public static function ftpClose($connection=false){
if( $connection !== false ){
@ftp_quit($connection);
}
}
public static function ftpLocation(&$location){
global $config,$dataDir;
$len = strlen($dataDir);
$temp = substr($location,$len);
return $config['ftp_root'].$temp;
}
/**
* @deprecated 3.0
* Use \gp\tool\Editing::CleanTitle() instead
* Used by Simple_Blog1
*/
public static function CleanTitle($title,$spaces = '_'){
trigger_error('Deprecated Function');
return \gp\tool\Editing::CleanTitle($title,$spaces);
}
}
}
namespace{
class gpFiles extends gp\tool\Files{}
}