2013-02-07 02:36:38 +04:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace OC\Files;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* class Mapper is responsible to translate logical paths to physical paths and reverse
|
|
|
|
*/
|
|
|
|
class Mapper
|
|
|
|
{
|
2013-02-15 20:42:17 +04:00
|
|
|
private $unchangedPhysicalRoot;
|
|
|
|
|
|
|
|
public function __construct($rootDir) {
|
|
|
|
$this->unchangedPhysicalRoot = $rootDir;
|
|
|
|
}
|
|
|
|
|
2013-02-07 02:36:38 +04:00
|
|
|
/**
|
|
|
|
* @param string $logicPath
|
|
|
|
* @param bool $create indicates if the generated physical name shall be stored in the database or not
|
|
|
|
* @return string the physical path
|
|
|
|
*/
|
|
|
|
public function logicToPhysical($logicPath, $create) {
|
|
|
|
$physicalPath = $this->resolveLogicPath($logicPath);
|
|
|
|
if ($physicalPath !== null) {
|
|
|
|
return $physicalPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->create($logicPath, $create);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param string $physicalPath
|
2013-02-15 20:42:17 +04:00
|
|
|
* @return string
|
2013-02-07 02:36:38 +04:00
|
|
|
*/
|
|
|
|
public function physicalToLogic($physicalPath) {
|
|
|
|
$logicPath = $this->resolvePhysicalPath($physicalPath);
|
|
|
|
if ($logicPath !== null) {
|
|
|
|
return $logicPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->insert($physicalPath, $physicalPath);
|
|
|
|
return $physicalPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param string $path
|
|
|
|
* @param bool $isLogicPath indicates if $path is logical or physical
|
|
|
|
* @param $recursive
|
2013-02-15 20:42:17 +04:00
|
|
|
* @return void
|
2013-02-07 02:36:38 +04:00
|
|
|
*/
|
|
|
|
public function removePath($path, $isLogicPath, $recursive) {
|
|
|
|
if ($recursive) {
|
|
|
|
$path=$path.'%';
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($isLogicPath) {
|
|
|
|
$query = \OC_DB::prepare('DELETE FROM `*PREFIX*file_map` WHERE `logic_path` LIKE ?');
|
|
|
|
$query->execute(array($path));
|
|
|
|
} else {
|
|
|
|
$query = \OC_DB::prepare('DELETE FROM `*PREFIX*file_map` WHERE `physic_path` LIKE ?');
|
|
|
|
$query->execute(array($path));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param $path1
|
|
|
|
* @param $path2
|
|
|
|
* @throws \Exception
|
|
|
|
*/
|
|
|
|
public function copy($path1, $path2)
|
|
|
|
{
|
|
|
|
$path1 = $this->stripLast($path1);
|
|
|
|
$path2 = $this->stripLast($path2);
|
|
|
|
$physicPath1 = $this->logicToPhysical($path1, true);
|
|
|
|
$physicPath2 = $this->logicToPhysical($path2, true);
|
|
|
|
|
|
|
|
$query = \OC_DB::prepare('SELECT * FROM `*PREFIX*file_map` WHERE `logic_path` LIKE ?');
|
|
|
|
$result = $query->execute(array($path1.'%'));
|
|
|
|
$updateQuery = \OC_DB::prepare('UPDATE `*PREFIX*file_map`'
|
|
|
|
.' SET `logic_path` = ?'
|
|
|
|
.' AND `physic_path` = ?'
|
|
|
|
.' WHERE `logic_path` = ?');
|
|
|
|
while( $row = $result->fetchRow()) {
|
|
|
|
$currentLogic = $row['logic_path'];
|
|
|
|
$currentPhysic = $row['physic_path'];
|
|
|
|
$newLogic = $path2.$this->stripRootFolder($currentLogic, $path1);
|
|
|
|
$newPhysic = $physicPath2.$this->stripRootFolder($currentPhysic, $physicPath1);
|
|
|
|
if ($path1 !== $currentLogic) {
|
|
|
|
try {
|
|
|
|
$updateQuery->execute(array($newLogic, $newPhysic, $currentLogic));
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
error_log('Mapper::Copy failed '.$currentLogic.' -> '.$newLogic.'\n'.$e);
|
|
|
|
throw $e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param $path
|
|
|
|
* @param $root
|
|
|
|
* @return bool|string
|
|
|
|
*/
|
|
|
|
public function stripRootFolder($path, $root) {
|
|
|
|
if (strpos($path, $root) !== 0) {
|
|
|
|
// throw exception ???
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (strlen($path) > strlen($root)) {
|
|
|
|
return substr($path, strlen($root));
|
|
|
|
}
|
|
|
|
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
private function stripLast($path) {
|
|
|
|
if (substr($path, -1) == '/') {
|
2013-02-09 20:35:47 +04:00
|
|
|
$path = substr_replace($path, '', -1);
|
2013-02-07 02:36:38 +04:00
|
|
|
}
|
|
|
|
return $path;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function resolveLogicPath($logicPath) {
|
|
|
|
$logicPath = $this->stripLast($logicPath);
|
2013-02-10 14:49:37 +04:00
|
|
|
$query = \OC_DB::prepare('SELECT * FROM `*PREFIX*file_map` WHERE `logic_path_hash` = ?');
|
|
|
|
$result = $query->execute(array(md5($logicPath)));
|
2013-02-07 02:36:38 +04:00
|
|
|
$result = $result->fetchRow();
|
2013-02-11 16:53:10 +04:00
|
|
|
if ($result === false) {
|
|
|
|
return null;
|
|
|
|
}
|
2013-02-07 02:36:38 +04:00
|
|
|
|
|
|
|
return $result['physic_path'];
|
|
|
|
}
|
|
|
|
|
|
|
|
private function resolvePhysicalPath($physicalPath) {
|
|
|
|
$physicalPath = $this->stripLast($physicalPath);
|
2013-02-10 14:49:37 +04:00
|
|
|
$query = \OC_DB::prepare('SELECT * FROM `*PREFIX*file_map` WHERE `physic_path_hash` = ?');
|
|
|
|
$result = $query->execute(array(md5($physicalPath)));
|
2013-02-07 02:36:38 +04:00
|
|
|
$result = $result->fetchRow();
|
|
|
|
|
|
|
|
return $result['logic_path'];
|
|
|
|
}
|
|
|
|
|
|
|
|
private function create($logicPath, $store) {
|
|
|
|
$logicPath = $this->stripLast($logicPath);
|
|
|
|
$index = 0;
|
|
|
|
|
|
|
|
// create the slugified path
|
|
|
|
$physicalPath = $this->slugifyPath($logicPath);
|
|
|
|
|
|
|
|
// detect duplicates
|
|
|
|
while ($this->resolvePhysicalPath($physicalPath) !== null) {
|
2013-03-12 12:15:53 +04:00
|
|
|
$physicalPath = $this->slugifyPath($logicPath, $index++);
|
2013-02-07 02:36:38 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// insert the new path mapping if requested
|
|
|
|
if ($store) {
|
|
|
|
$this->insert($logicPath, $physicalPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $physicalPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function insert($logicPath, $physicalPath) {
|
2013-02-10 14:49:37 +04:00
|
|
|
$query = \OC_DB::prepare('INSERT INTO `*PREFIX*file_map`(`logic_path`, `physic_path`, `logic_path_hash`, `physic_path_hash`) VALUES(?, ?, ?, ?)');
|
|
|
|
$query->execute(array($logicPath, $physicalPath, md5($logicPath), md5($physicalPath)));
|
2013-02-07 02:36:38 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
private function slugifyPath($path, $index=null) {
|
2013-02-15 20:42:17 +04:00
|
|
|
$path = $this->stripRootFolder($path, $this->unchangedPhysicalRoot);
|
|
|
|
|
2013-02-07 02:36:38 +04:00
|
|
|
$pathElements = explode('/', $path);
|
|
|
|
$sluggedElements = array();
|
|
|
|
|
|
|
|
foreach ($pathElements as $pathElement) {
|
2013-02-11 16:53:10 +04:00
|
|
|
// remove empty elements
|
|
|
|
if (empty($pathElement)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-02-07 02:36:38 +04:00
|
|
|
// TODO: remove file ext before slugify on last element
|
|
|
|
$sluggedElements[] = self::slugify($pathElement);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// TODO: add the index before the file extension
|
|
|
|
//
|
|
|
|
if ($index !== null) {
|
|
|
|
$last= end($sluggedElements);
|
|
|
|
array_pop($sluggedElements);
|
|
|
|
array_push($sluggedElements, $last.'-'.$index);
|
|
|
|
}
|
2013-02-11 16:53:10 +04:00
|
|
|
|
2013-02-15 20:42:17 +04:00
|
|
|
$sluggedPath = $this->unchangedPhysicalRoot.implode(DIRECTORY_SEPARATOR, $sluggedElements);
|
|
|
|
return $this->stripLast($sluggedPath);
|
2013-02-07 02:36:38 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Modifies a string to remove all non ASCII characters and spaces.
|
|
|
|
*
|
|
|
|
* @param string $text
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
private function slugify($text)
|
|
|
|
{
|
|
|
|
// replace non letter or digits by -
|
|
|
|
$text = preg_replace('~[^\\pL\d]+~u', '-', $text);
|
|
|
|
|
|
|
|
// trim
|
|
|
|
$text = trim($text, '-');
|
|
|
|
|
|
|
|
// transliterate
|
|
|
|
if (function_exists('iconv')) {
|
|
|
|
$text = iconv('utf-8', 'us-ascii//TRANSLIT', $text);
|
|
|
|
}
|
|
|
|
|
|
|
|
// lowercase
|
|
|
|
$text = strtolower($text);
|
|
|
|
|
|
|
|
// remove unwanted characters
|
|
|
|
$text = preg_replace('~[^-\w]+~', '', $text);
|
|
|
|
|
2013-03-12 12:24:58 +04:00
|
|
|
if (empty($text)) {
|
|
|
|
return uniqid();
|
2013-02-07 02:36:38 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return $text;
|
|
|
|
}
|
|
|
|
}
|