Merge pull request #516 from dawnbreak/master

Fixed a wrong timestamp update when adding a file.
This commit is contained in:
RedMatrix 2014-06-30 09:19:52 +10:00
commit 0926c2d849
4 changed files with 172 additions and 90 deletions

View file

@ -552,12 +552,12 @@ function z_readdir($channel_id, $observer_hash, $pathname, $parent_hash = '') {
/**
* @function attach_mkdir($channel,$observer_hash,$arr);
*
*
* @brief Create directory.
*
* @param $channel channel array of owner
* @param $observer_hash hash of current observer
* @param $arr parameter array to fulfil request
*
* @param array $channel channel array of owner
* @param string $observer_hash hash of current observer
* @param array $arr parameter array to fulfil request
* Required:
* $arr['filename']
* $arr['folder'] // hash of parent directory, empty string for root directory
@ -641,7 +641,7 @@ function attach_mkdir($channel, $observer_hash, $arr = null) {
$path .= $arr['hash'];
$created = datetime_convert();
$created = datetime_convert();
$r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, filetype, filesize, revision, folder, flags, data, created, edited, allow_cid, allow_gid, deny_cid, deny_gid )
VALUES ( %d, %d, '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ",
@ -665,20 +665,28 @@ function attach_mkdir($channel, $observer_hash, $arr = null) {
);
if($r) {
if(mkdir($path,STORAGE_DEFAULT_PERMISSIONS, true)) {
if(mkdir($path, STORAGE_DEFAULT_PERMISSIONS, true)) {
$ret['success'] = true;
$ret['data'] = $arr;
// update the parent folder's lastmodified timestamp
$e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d LIMIT 1",
dbesc($created),
dbesc($arr['folder']),
intval($channel_id)
);
}
else {
logger('attach_mkdir: ' . mkdir . ' ' . $path . 'failed.');
$ret['message'] = t('mkdir failed.');
}
}
else
else {
$ret['message'] = t('database storage failed.');
}
return $ret;
}
/**
@ -727,31 +735,30 @@ function attach_change_permissions($channel_id, $resource, $allow_cid, $allow_gi
return;
}
/**
* @brief Delete a file.
* @brief Delete a file/directory.
*
* @param $channel_id
* @param $resource
* @param int $channel_id
* @param string $resource a hash to delete
*/
function attach_delete($channel_id, $resource) {
$c = q("select channel_address from channel where channel_id = %d limit 1",
$c = q("SELECT channel_address FROM channel WHERE channel_id = %d LIMIT 1",
intval($channel_id)
);
$channel_address = (($c) ? $c[0]['channel_address'] : 'notfound');
$r = q("select hash, flags from attach where hash = '%s' and uid = %d limit 1",
$r = q("SELECT hash, flags, folder FROM attach WHERE hash = '%s' AND uid = %d limit 1",
dbesc($resource),
intval($channel_id)
);
if(! $r)
return;
// If resource is a directory delete everything in the directory recursive
if($r[0]['flags'] & ATTACH_FLAG_DIR) {
$x = q("select hash, flags from attach where folder = '%s' and uid = %d",
dbesc($resource),
@ -763,8 +770,10 @@ function attach_delete($channel_id, $resource) {
}
}
}
// delete a file from filesystem
if($r[0]['flags'] & ATTACH_FLAG_OS) {
$y = q("select data from attach where hash = '%s' and uid = %d limit 1",
$y = q("SELECT data FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1",
dbesc($resource),
intval($channel_id)
);
@ -778,14 +787,22 @@ function attach_delete($channel_id, $resource) {
}
}
$z = q("delete from attach where hash = '%s' and uid = %d limit 1",
// delete from database
$z = q("DELETE FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1",
dbesc($resource),
intval($channel_id)
);
// update the parent folder's lastmodified timestamp
$e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d LIMIT 1",
dbesc(datetime_convert()),
dbesc($r[0]['folder']),
intval($channel_id)
);
return;
}
/**
* @brief Returns path to file in cloud/.
*

View file

@ -23,22 +23,33 @@ require_once('include/attach.php');
*/
class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
/**
* @brief The path inside /cloud
*/
private $red_path;
private $folder_hash;
// @todo I think this is not used anywhere, we always strip '/cloud' and only use it in debug
/**
* @brief The full path as seen in the browser.
* /cloud + $red_path
* @todo I think this is not used anywhere, we always strip '/cloud' and only use it in debug
*/
private $ext_path;
private $root_dir = '';
private $auth;
/**
* @brief The real path on the filesystem.
* The actual path in store/ with the hashed names.
*/
private $os_path = '';
/**
* Sets up the directory node, expects a full path.
* @brief Sets up the directory node, expects a full path.
*
* @param string $ext_path a full path
* @param $auth_plugin
* @param RedBasicAuth &$auth_plugin
*/
public function __construct($ext_path, &$auth_plugin) {
logger('RedDirectory::__construct() ' . $ext_path, LOGGER_DEBUG);
logger('RedDirectory::__construct() ' . $ext_path, LOGGER_DATA);
$this->ext_path = $ext_path;
// remove "/cloud" from the beginning of the path
$this->red_path = ((strpos($ext_path, '/cloud') === 0) ? substr($ext_path, 6) : $ext_path);
@ -54,7 +65,7 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
}
}
function log() {
private function log() {
logger('RedDirectory::log() ext_path ' . $this->ext_path, LOGGER_DATA);
logger('RedDirectory::log() os_path ' . $this->os_path, LOGGER_DATA);
logger('RedDirectory::log() red_path ' . $this->red_path, LOGGER_DATA);
@ -113,10 +124,49 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
throw new DAV\Exception\NotFound('The file with name: ' . $name . ' could not be found.');
}
/**
* @brief Returns the name of the directory.
*
* @return string
*/
public function getName() {
logger('RedDirectory::getName returns: ' . basename($this->red_path), LOGGER_DATA);
logger('RedDirectory::getName() returns: ' . basename($this->red_path), LOGGER_DATA);
return (basename($this->red_path));
}
/**
* @brief Renames the directory.
*
* @todo handle duplicate directory name
*
* @throw DAV\Exception\Forbidden
* @param string $name The new name of the directory.
* @return void
*/
public function setName($name) {
logger('RedDirectory::setName(): ' . basename($this->red_path) . ' -> ' . $name, LOGGER_DATA);
if ((! $name) || (! $this->auth->owner_id)) {
logger('RedDirectory::setName(): permission denied');
throw new DAV\Exception\Forbidden('Permission denied.');
}
if (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage')) {
logger('RedDirectory::setName(): permission denied');
throw new DAV\Exception\Forbidden('Permission denied.');
}
list($parent_path, ) = DAV\URLUtil::splitPath($this->red_path);
$new_path = $parent_path . '/' . $name;
$r = q("UPDATE attach SET filename = '%s' WHERE hash = '%s' AND uid = %d LIMIT 1",
dbesc($name),
dbesc($this->folder_hash),
intval($this->auth->owner_id)
);
$this->red_path = $new_path;
}
/**
* @brief Creates a new file in the directory.
@ -130,18 +180,18 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
* @throws DAV\Exception\Forbidden
* @param string $name Name of the file
* @param resource|string $data Initial payload
* @return null|string
* @return null|string ETag
*/
public function createFile($name, $data = null) {
logger('RedDirectory::createFile(): ' . $name, LOGGER_DEBUG);
logger('RedDirectory::createFile(): ' . $name, LOGGER_DATA);
if (! $this->auth->owner_id) {
logger('createFile: permission denied');
logger('RedDirectory::createFile(): permission denied');
throw new DAV\Exception\Forbidden('Permission denied.');
}
if (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage')) {
logger('createFile: permission denied');
logger('RedDirectory::createFile(): permission denied');
throw new DAV\Exception\Forbidden('Permission denied.');
}
@ -204,7 +254,7 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
);
// update the folder's lastmodified timestamp
$e = q("UPDATE attach SET edited = '%s' WHERE folder = '%s' AND uid = %d LIMIT 1",
$e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d LIMIT 1",
dbesc($edited),
dbesc($this->folder_hash),
intval($c[0]['channel_id'])
@ -250,8 +300,9 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
if ($r) {
$result = attach_mkdir($r[0], $this->auth->observer, array('filename' => $name, 'folder' => $this->folder_hash));
if (! $result['success'])
if (! $result['success']) {
logger('RedDirectory::createDirectory(): ' . print_r($result, true), LOGGER_DEBUG);
}
}
}
@ -305,7 +356,7 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
if (! $path_arr)
return;
logger('RedDirectory::getDir(): path: ' . print_r($path_arr, true), LOGGER_DEBUG);
logger('RedDirectory::getDir(): path: ' . print_r($path_arr, true), LOGGER_DATA);
$channel_name = $path_arr[0];
@ -315,7 +366,7 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
);
if (! $r) {
throw new DAV\Exception\NotFound('The file with name: ' . $channel_name . ' could not be found');
throw new DAV\Exception\NotFound('The file with name: ' . $channel_name . ' could not be found.');
return;
}
@ -350,9 +401,13 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
}
/**
* @brief Returns the last modification time for the directory, as a unix
* @brief Returns the last modification time for the directory, as a UNIX
* timestamp.
*
* It looks for the last edited file in the folder. If it is an empty folder
* it returns the lastmodified time of the folder itself, to prevent zero
* timestamps.
*
* @return int last modification time in UNIX timestamp
*/
public function getLastModified() {
@ -360,9 +415,15 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
dbesc($this->folder_hash),
intval($this->auth->owner_id)
);
if ($r)
return datetime_convert('UTC', 'UTC', $r[0]['edited'], 'U');
return '';
if (! $r) {
$r = q("SELECT edited FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1",
dbesc($this->folder_hash),
intval($this->auth->owner_id)
);
if (! $r)
return '';
}
return datetime_convert('UTC', 'UTC', $r[0]['edited'], 'U');
}
/**
@ -470,8 +531,8 @@ class RedFile extends DAV\Node implements DAV\IFile {
// @todo only 3 values are needed
$c = q("SELECT * FROM channel WHERE channel_id = %d AND NOT (channel_pageflags & %d) LIMIT 1",
intval(PAGE_REMOVED),
intval($this->auth->owner_id)
intval($this->auth->owner_id),
intval(PAGE_REMOVED)
);
$r = q("SELECT flags, folder, data FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1",
@ -512,13 +573,16 @@ class RedFile extends DAV\Node implements DAV\IFile {
);
// update the folder's lastmodified timestamp
$e = q("UPDATE attach SET edited = '%s' WHERE folder = '%s' AND uid = %d LIMIT 1",
$e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d LIMIT 1",
dbesc($edited),
dbesc($r[0]['folder']),
intval($c[0]['channel_id'])
);
// @todo do we really want to remove the whole file if an update fails because of maxfilesize or quota?
// @todo do we really want to remove the whole file if an update fails
// because of maxfilesize or quota?
// There is an Exception "InsufficientStorage" or "PaymentRequired" for
// our service class from SabreDAV we could use.
$maxfilesize = get_config('system', 'maxfilesize');
if (($maxfilesize) && ($size > $maxfilesize)) {
@ -622,12 +686,14 @@ class RedFile extends DAV\Node implements DAV\IFile {
}
/**
* @brief Delete the current file.
* @brief Delete the file.
*
* @throw DAV\Exception\Forbidden
* @return void
*/
public function delete() {
logger('RedFile::delete(): ' . basename($this->name), LOGGER_DEBUG);
if ((! $this->auth->owner_id) || (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage'))) {
throw new DAV\Exception\Forbidden('Permission denied.');
}
@ -726,7 +792,7 @@ function RedCollectionData($file, &$auth) {
$permission_error = false;
for ($x = 1; $x < count($path_arr); $x++) {
$r = q("select id, hash, filename, flags from attach where folder = '%s' and filename = '%s' and uid = %d and (flags & %d) $perms limit 1",
$r = q("SELECT id, hash, filename, flags FROM attach WHERE folder = '%s' AND filename = '%s' AND uid = %d AND (flags & %d) $perms LIMIT 1",
dbesc($folder),
dbesc($path_arr[$x]),
intval($channel_id),
@ -989,11 +1055,17 @@ class RedBasicAuth extends DAV\Auth\Backend\AbstractBasic {
$this->currentUser = $name;
}
function setBrowserPlugin($browser) {
/**
* @brief Set browser plugin.
*
* @see RedBrowser::set_writeable()
* @param DAV\Browser\Plugin $browser
*/
public function setBrowserPlugin($browser) {
$this->browser = $browser;
}
// internal? loggin function
// internal? logging function
function log() {
logger('dav: auth: channel_name ' . $this->channel_name, LOGGER_DATA);
logger('dav: auth: channel_id ' . $this->channel_id, LOGGER_DATA);
@ -1037,9 +1109,7 @@ class RedBrowser extends DAV\Browser\Plugin {
}
/**
* Creates the directory listing for the given path.
*
* @todo Why not use RedDirectory for this?
* @brief Creates the directory listing for the given path.
*
* @param string $path which should be displayed
*/

View file

@ -1,13 +1,16 @@
<?php
/**
* @file mod/cloud.php
* @brief Initialize Red Matrix's cloud (SabreDAV)
*
*/
use Sabre\DAV;
require_once('vendor/autoload.php');
require_once('vendor/autoload.php');
// workaround for HTTP-auth in CGI mode
if(x($_SERVER,'REDIRECT_REMOTE_USER')) {
$userpass = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"],6)) ;
$userpass = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"], 6)) ;
if(strlen($userpass)) {
list($name, $password) = explode(':', $userpass);
$_SERVER['PHP_AUTH_USER'] = $name;
@ -16,18 +19,19 @@
}
if(x($_SERVER,'HTTP_AUTHORIZATION')) {
$userpass = base64_decode(substr($_SERVER["HTTP_AUTHORIZATION"],6)) ;
$userpass = base64_decode(substr($_SERVER["HTTP_AUTHORIZATION"], 6)) ;
if(strlen($userpass)) {
list($name, $password) = explode(':', $userpass);
list($name, $password) = explode(':', $userpass);
$_SERVER['PHP_AUTH_USER'] = $name;
$_SERVER['PHP_AUTH_PW'] = $password;
}
}
/**
* @brief Fires up the SabreDAV server.
*
* @param App &$a
*/
function cloud_init(&$a) {
require_once('include/reddav.php');
@ -42,13 +46,10 @@ function cloud_init(&$a) {
$profile = 0;
$channel = $a->get_channel();
$a->page['htmlhead'] .= '<link rel="alternate" type="application/atom+xml" href="' . $a->get_baseurl() . '/feed/' . $which .'" />' . "\r\n" ;
$a->page['htmlhead'] .= '<link rel="alternate" type="application/atom+xml" href="' . $a->get_baseurl() . '/feed/' . $which . '" />' . "\r\n" ;
if($which)
profile_load($a,$which,$profile);
profile_load($a, $which, $profile);
$auth = new RedBasicAuth();
@ -64,15 +65,13 @@ function cloud_init(&$a) {
$auth->channel_account_id = $channel['channel_account_id'];
if($channel['channel_timezone'])
$auth->timezone = $channel['channel_timezone'];
}
}
$auth->observer = $ob_hash;
}
}
if($_GET['davguest'])
$_SESSION['davguest'] = true;
$_SERVER['QUERY_STRING'] = str_replace(array('?f=','&f='),array('',''),$_SERVER['QUERY_STRING']);
$_SERVER['QUERY_STRING'] = strip_zids($_SERVER['QUERY_STRING']);
$_SERVER['QUERY_STRING'] = preg_replace('/[\?&]davguest=(.*?)([\?&]|$)/ism','',$_SERVER['QUERY_STRING']);
@ -81,8 +80,11 @@ function cloud_init(&$a) {
$_SERVER['REQUEST_URI'] = strip_zids($_SERVER['REQUEST_URI']);
$_SERVER['REQUEST_URI'] = preg_replace('/[\?&]davguest=(.*?)([\?&]|$)/ism','',$_SERVER['REQUEST_URI']);
$rootDirectory = new RedDirectory('/',$auth);
$rootDirectory = new RedDirectory('/', $auth);
// A SabreDAV server-object
$server = new DAV\Server($rootDirectory);
// prevent overwriting changes each other with a lock backend
$lockBackend = new DAV\Locks\Backend\File('store/[data]/locks');
$lockPlugin = new DAV\Locks\Plugin($lockBackend);
@ -95,11 +97,11 @@ function cloud_init(&$a) {
// In order to avoid prompting for passwords for viewing a DIRECTORY, add the URL query parameter 'davguest=1'
$isapublic_file = false;
$davguest = ((x($_SESSION,'davguest')) ? true : false);
$davguest = ((x($_SESSION, 'davguest')) ? true : false);
if((! $auth->observer) && ($_SERVER['REQUEST_METHOD'] === 'GET')) {
try {
$x = RedFileData('/' . $a->cmd,$auth);
$x = RedFileData('/' . $a->cmd, $auth);
if($x instanceof RedFile)
$isapublic_file = true;
}
@ -113,21 +115,19 @@ function cloud_init(&$a) {
$auth->Authenticate($server, t('Red Matrix - Guests: Username: {your email address}, Password: +++'));
}
catch ( Exception $e) {
logger('mod_cloud: auth exception' . $e->getMessage());
http_status_exit($e->getHTTPCode(),$e->getMessage());
logger('mod_cloud: auth exception' . $e->getMessage());
http_status_exit($e->getHTTPCode(), $e->getMessage());
}
}
// $browser = new DAV\Browser\Plugin();
// provide a directory view for the cloud in Red Matrix
$browser = new RedBrowser($auth);
$auth->setBrowserPlugin($browser);
$server->addPlugin($browser);
// All we need to do now, is to fire up the server
$server->exec();

View file

@ -9,7 +9,6 @@
<th>{{t('Last modified')}}</th>
</tr>
<tr><td colspan="8"><hr></td></tr>
{{if $parentpath}}
<tr>
<td>{{$parentpath.icon}}</td>
@ -20,29 +19,25 @@
<td></td>
</tr>
{{/if}}
{{foreach $entries as $item}}
<tr>
<td>{{$item.icon}}</td>
<td style="min-width: 15em"><a href="{{$item.fullPath}}">{{$item.displayName}}</a></td>
<td style="min-width: 15em"><a href="{{$item.fullPath}}">{{$item.displayName}}</a></td>
{{if $item.is_owner}}
<td>{{$item.attachIcon}}</td>
<td style="position:relative;"><a href="{{$item.fileStorageUrl}}/{{$item.attachId}}/edit" title="{{t('Edit')}}"><i class="icon-pencil btn btn-default"></i></a></td>
<td><a href="{{$item.fileStorageUrl}}/{{$item.attachId}}/delete" title="{{t('Delete')}}" onclick="return confirm('{{t('Are you sure you want to delete this item?')}}');"><i class="icon-remove btn btn-default drop-icons"></i></a></td>
<td>{{$item.attachIcon}}</td>
<td style="position:relative;"><a href="{{$item.fileStorageUrl}}/{{$item.attachId}}/edit" title="{{t('Edit')}}"><i class="icon-pencil btn btn-default"></i></a></td>
<td><a href="{{$item.fileStorageUrl}}/{{$item.attachId}}/delete" title="{{t('Delete')}}" onclick="return confirm('{{t('Are you sure you want to delete this item?')}}');"><i class="icon-remove btn btn-default drop-icons"></i></a></td>
{{else}}
<td></td><td></td><td></td>
{{/if}}
<td>{{$item.type}}</td>
<td>{{$item.sizeFormatted}}</td>
<td>{{$item.lastmodified}}</td>
</tr>
<td>{{$item.type}}</td>
<td>{{$item.sizeFormatted}}</td>
<td>{{$item.lastmodified}}</td>
</tr>
{{/foreach}}
<tr><td colspan="8"><hr></td></tr>
</table>
{{if $quota.limit || $quota.used}}
<p><strong>{{t('Total')}}</strong> {{$quota.desc}}</p>
{{/if}}
{{/if}}