diff options
author | RedMatrix <info@friendica.com> | 2014-06-30 09:19:52 +1000 |
---|---|---|
committer | RedMatrix <info@friendica.com> | 2014-06-30 09:19:52 +1000 |
commit | 0926c2d849bdc9f8de676a32e2df4fe29d6e2def (patch) | |
tree | ae54f0cf2ece4b96b1f71f1c34544555d1fc2dbe | |
parent | 362009959e21ff10cc827fdfb1b857f20fe93045 (diff) | |
parent | ffea67524026caec14da923bafe8ad34ec63ef08 (diff) | |
download | volse-hubzilla-0926c2d849bdc9f8de676a32e2df4fe29d6e2def.tar.gz volse-hubzilla-0926c2d849bdc9f8de676a32e2df4fe29d6e2def.tar.bz2 volse-hubzilla-0926c2d849bdc9f8de676a32e2df4fe29d6e2def.zip |
Merge pull request #516 from dawnbreak/master
Fixed a wrong timestamp update when adding a file.
-rw-r--r-- | include/attach.php | 57 | ||||
-rw-r--r-- | include/reddav.php | 128 | ||||
-rw-r--r-- | mod/cloud.php | 54 | ||||
-rw-r--r-- | view/tpl/cloud_directory.tpl | 23 |
4 files changed, 172 insertions, 90 deletions
diff --git a/include/attach.php b/include/attach.php index f5eaaa448..da22e8ed5 100644 --- a/include/attach.php +++ b/include/attach.php @@ -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/. * diff --git a/include/reddav.php b/include/reddav.php index c9b238efb..c4b249598 100644 --- a/include/reddav.php +++ b/include/reddav.php @@ -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 */ diff --git a/mod/cloud.php b/mod/cloud.php index 51cedd2fd..25773066a 100644 --- a/mod/cloud.php +++ b/mod/cloud.php @@ -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(); diff --git a/view/tpl/cloud_directory.tpl b/view/tpl/cloud_directory.tpl index cdcab1990..fc6b3309a 100644 --- a/view/tpl/cloud_directory.tpl +++ b/view/tpl/cloud_directory.tpl @@ -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}}
\ No newline at end of file |