diff options
author | Klaus Weidenbach <Klaus.Weidenbach@gmx.net> | 2014-10-06 23:20:17 +0200 |
---|---|---|
committer | Klaus Weidenbach <Klaus.Weidenbach@gmx.net> | 2014-10-06 23:38:33 +0200 |
commit | 33394f58ff8cc54aebbd35a53b93ec3fa1bfdf57 (patch) | |
tree | 04c74c04454f0b79d329255742dd3903e35d4492 /include | |
parent | cef8955d572636e313623e363aed6bc23b732e4f (diff) | |
download | volse-hubzilla-33394f58ff8cc54aebbd35a53b93ec3fa1bfdf57.tar.gz volse-hubzilla-33394f58ff8cc54aebbd35a53b93ec3fa1bfdf57.tar.bz2 volse-hubzilla-33394f58ff8cc54aebbd35a53b93ec3fa1bfdf57.zip |
Moved RedBrowser class from reddav.php to it's own file.
First step on cleaning up ReadDAV classes and restructuring it bit in
preparation for adding some more DAV features, photo access, CalDAV, etc.
and hopefully also some PHPunits.
Add a PHP5.3 namespace for RedDAV classes. Any objections against this or
the vendor namespace?
Add some more documentation for RedBrowser.
Diffstat (limited to 'include')
-rw-r--r-- | include/RedDAV/RedBrowser.php | 361 | ||||
-rw-r--r-- | include/reddav.php | 329 |
2 files changed, 366 insertions, 324 deletions
diff --git a/include/RedDAV/RedBrowser.php b/include/RedDAV/RedBrowser.php new file mode 100644 index 000000000..dcd54ef82 --- /dev/null +++ b/include/RedDAV/RedBrowser.php @@ -0,0 +1,361 @@ +<?php +/** + * RedMatrix - "The Network" + * + * @link http://github.com/friendica/red + * @license http://opensource.org/licenses/mit-license.php The MIT License (MIT) + */ + +namespace RedMatrix\RedDAV; + +use Sabre\DAV; + +/** + * @brief Provides a DAV frontend for the webbrowser. + * + * RedBrowser is a SabreDAV server-plugin to provide a view to the DAV storage + * for the webbrowser. + * + * @extends \Sabre\DAV\Browser\Plugin + */ +class RedBrowser extends DAV\Browser\Plugin { + + /** + * @see set_writeable() + * @see \Sabre\DAV\Auth\Backend\BackendInterface + * @var RedBasicAuth + */ + private $auth; + + /** + * @brief Constructor for RedBrowser class. + * + * $enablePost will be activated through set_writeable() in a later stage. + * At the moment the write_storage permission is only valid for the whole + * folder. No file specific permissions yet. + * + * Disable assets with $enableAssets = false. Should get some thumbnail views + * anyway. + * + * @param RedBasicAuth &$auth + */ + public function __construct(&$auth) { + $this->auth = $auth; + parent::__construct(false, false); + } + + /** + * The DAV browser is instantiated after the auth module and directory classes + * but before we know the current directory and who the owner and observer + * are. So we add a pointer to the browser into the auth module and vice versa. + * Then when we've figured out what directory is actually being accessed, we + * call the following function to decide whether or not to show web elements + * which include writeable objects. + * + * @todo Maybe this can be solved with some $server->subscribeEvent()? + */ + public function set_writeable() { + if (! $this->auth->owner_id) { + $this->enablePost = false; + } + + if (! perm_is_allowed($this->auth->owner_id, get_observer_hash(), 'write_storage')) { + $this->enablePost = false; + } else { + $this->enablePost = true; + } + } + + /** + * @brief Creates the directory listing for the given path. + * + * @param string $path which should be displayed + */ + public function generateDirectoryIndex($path) { + // (owner_id = channel_id) is visitor owner of this directory? + $is_owner = ((local_user() && $this->auth->owner_id == local_user()) ? true : false); + + if ($this->auth->getTimezone()) + date_default_timezone_set($this->auth->getTimezone()); + + require_once('include/conversation.php'); + + if ($this->auth->owner_nick) { + $html = profile_tabs(get_app(), (($is_owner) ? true : false), $this->auth->owner_nick); + } + + $files = $this->server->getPropertiesForPath($path, array( + '{DAV:}displayname', + '{DAV:}resourcetype', + '{DAV:}getcontenttype', + '{DAV:}getcontentlength', + '{DAV:}getlastmodified', + ), 1); + + $parent = $this->server->tree->getNodeForPath($path); + + $parentpath = array(); + // only show parent if not leaving /cloud/; TODO how to improve this? + if ($path && $path != "cloud") { + list($parentUri) = DAV\URLUtil::splitPath($path); + $fullPath = DAV\URLUtil::encodePath($this->server->getBaseUri() . $parentUri); + + $parentpath['icon'] = $this->enableAssets ? '<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl('icons/parent' . $this->iconExtension) . '" width="24" alt="' . t('parent') . '"></a>' : ''; + $parentpath['path'] = $fullPath; + } + + $f = array(); + foreach ($files as $file) { + $ft = array(); + $type = null; + + // This is the current directory, we can skip it + if (rtrim($file['href'],'/') == $path) continue; + + list(, $name) = DAV\URLUtil::splitPath($file['href']); + + if (isset($file[200]['{DAV:}resourcetype'])) { + $type = $file[200]['{DAV:}resourcetype']->getValue(); + + // resourcetype can have multiple values + if (!is_array($type)) $type = array($type); + + foreach ($type as $k=>$v) { + // Some name mapping is preferred + switch ($v) { + case '{DAV:}collection' : + $type[$k] = t('Collection'); + break; + case '{DAV:}principal' : + $type[$k] = t('Principal'); + break; + case '{urn:ietf:params:xml:ns:carddav}addressbook' : + $type[$k] = t('Addressbook'); + break; + case '{urn:ietf:params:xml:ns:caldav}calendar' : + $type[$k] = t('Calendar'); + break; + case '{urn:ietf:params:xml:ns:caldav}schedule-inbox' : + $type[$k] = t('Schedule Inbox'); + break; + case '{urn:ietf:params:xml:ns:caldav}schedule-outbox' : + $type[$k] = t('Schedule Outbox'); + break; + case '{http://calendarserver.org/ns/}calendar-proxy-read' : + $type[$k] = 'Proxy-Read'; + break; + case '{http://calendarserver.org/ns/}calendar-proxy-write' : + $type[$k] = 'Proxy-Write'; + break; + } + } + $type = implode(', ', $type); + } + + // If no resourcetype was found, we attempt to use + // the contenttype property + if (!$type && isset($file[200]['{DAV:}getcontenttype'])) { + $type = $file[200]['{DAV:}getcontenttype']; + } + if (!$type) $type = t('Unknown'); + + $size = isset($file[200]['{DAV:}getcontentlength']) ? (int)$file[200]['{DAV:}getcontentlength'] : ''; + $lastmodified = ((isset($file[200]['{DAV:}getlastmodified'])) ? $file[200]['{DAV:}getlastmodified']->getTime()->format('Y-m-d H:i:s') : ''); + + $fullPath = DAV\URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path ? $path . '/' : '') . $name, '/')); + + $displayName = isset($file[200]['{DAV:}displayname']) ? $file[200]['{DAV:}displayname'] : $name; + + $displayName = $this->escapeHTML($displayName); + $type = $this->escapeHTML($type); + + $icon = ''; + if ($this->enableAssets) { + $node = $this->server->tree->getNodeForPath(($path ? $path . '/' : '') . $name); + foreach (array_reverse($this->iconMap) as $class=>$iconName) { + if ($node instanceof $class) { + $icon = '<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl($iconName . $this->iconExtension) . '" alt="" width="24"></a>'; + break; + } + } + } + + $parentHash = ""; + $owner = $this->auth->owner_id; + $splitPath = split("/", $fullPath); + if (count($splitPath) > 3) { + for ($i = 3; $i < count($splitPath); $i++) { + $attachName = urldecode($splitPath[$i]); + $attachHash = $this->findAttachHash($owner, $parentHash, $attachName); + $parentHash = $attachHash; + } + } + + $attachIcon = ""; // "<a href=\"attach/".$attachHash."\" title=\"".$displayName."\"><i class=\"icon-download\"></i></a>"; + + // put the array for this file together + $ft['attachId'] = $this->findAttachIdByHash($attachHash); + $ft['fileStorageUrl'] = substr($fullPath, 0, strpos($fullPath, "cloud/")) . "filestorage/" . $this->auth->getCurrentUser(); + $ft['icon'] = $icon; + $ft['attachIcon'] = (($size) ? $attachIcon : ''); + // @todo Should this be an item value, not a global one? + $ft['is_owner'] = $is_owner; + $ft['fullPath'] = $fullPath; + $ft['displayName'] = $displayName; + $ft['type'] = $type; + $ft['size'] = $size; + $ft['sizeFormatted'] = $this->userReadableSize($size); + $ft['lastmodified'] = (($lastmodified) ? datetime_convert('UTC', date_default_timezone_get(), $lastmodified) : ''); + + $f[] = $ft; + } + + // Storage and quota for the account (all channels of the owner of this directory)! + $limit = service_class_fetch($owner, 'attach_upload_limit'); + $r = q("SELECT SUM(filesize) AS total FROM attach WHERE aid = %d", + intval($this->auth->channel_account_id) + ); + $used = $r[0]['total']; + if ($used) { + $quotaDesc = t('%1$s used'); + $quotaDesc = sprintf($quotaDesc, + $this->userReadableSize($used)); + } + if ($limit && $used) { + $quotaDesc = t('%1$s used of %2$s (%3$s%)'); + $quotaDesc = sprintf($quotaDesc, + $this->userReadableSize($used), + $this->userReadableSize($limit), + round($used / $limit, 1)); + } + + // prepare quota for template + $quota['used'] = $used; + $quota['limit'] = $limit; + $quota['desc'] = $quotaDesc; + + $html .= replace_macros(get_markup_template('cloud_directory.tpl'), array( + '$header' => t('Files') . ": " . $this->escapeHTML($path) . "/", + '$parentpath' => $parentpath, + '$entries' => $f, + '$quota' => $quota, + '$name' => t('Name'), + '$type' => t('Type'), + '$size' => t('Size'), + '$lastmod' => t('Last Modified'), + '$parent' => t('parent'), + '$edit' => t('Edit'), + '$delete' => t('Delete'), + '$total' => t('Total') + )); + + $output = ''; + if ($this->enablePost) { + $this->server->broadcastEvent('onHTMLActionsPanel', array($parent, &$output)); + } + $html .= $output; + + get_app()->page['content'] = $html; + construct_page(get_app()); + } + + function userReadableSize($size) { + $ret = ""; + if (is_numeric($size)) { + $incr = 0; + $k = 1024; + $unit = array('bytes', 'KB', 'MB', 'GB', 'TB', 'PB'); + while (($size / $k) >= 1){ + $incr++; + $size = round($size / $k, 2); + } + $ret = $size . " " . $unit[$incr]; + } + return $ret; + } + + /** + * @brief Creates a form to add new folders and upload files. + * + * @param \Sabre\DAV\INode $node + * @param string &$output + */ + public function htmlActionsPanel(DAV\INode $node, &$output) { + if (! $node instanceof DAV\ICollection) + return; + + // We also know fairly certain that if an object is a non-extended + // SimpleCollection, we won't need to show the panel either. + if (get_class($node) === 'Sabre\\DAV\\SimpleCollection') + return; + + $output .= replace_macros(get_markup_template('cloud_actionspanel.tpl'), array( + '$folder_header' => t('Create new folder'), + '$folder_submit' => t('Create'), + '$upload_header' => t('Upload file'), + '$upload_submit' => t('Upload') + )); + } + + /** + * This method takes a path/name of an asset and turns it into url + * suiteable for http access. + * + * @param string $assetName + * @return string + */ + protected function getAssetUrl($assetName) { + return z_root() . '/cloud/?sabreAction=asset&assetName=' . urlencode($assetName); + } + + /** + * @brief Return the hash of an attachment. + * + * Given the owner, the parent folder and and attach name get the attachment + * hash. + * + * @param int $owner + * The owner_id + * @param string $hash + * The parent's folder hash + * @param string $attachName + * The name of the attachment + * @return string + */ + protected function findAttachHash($owner, $parentHash, $attachName) { + $r = q("SELECT hash FROM attach WHERE uid = %d AND folder = '%s' AND filename = '%s' ORDER BY edited DESC LIMIT 1", + intval($owner), + dbesc($parentHash), + dbesc($attachName) + ); + $hash = ""; + if ($r) { + foreach ($r as $rr) { + $hash = $rr['hash']; + } + } + return $hash; + } + + /** + * @brief Returns an attachment's id for a given hash. + * + * This id is used to access the attachment in filestorage/ + * + * @param string $attachHash + * The hash of an attachment + * @return string + */ + protected function findAttachIdByHash($attachHash) { + $r = q("SELECT id FROM attach WHERE hash = '%s' ORDER BY edited DESC LIMIT 1", + dbesc($attachHash) + ); + $id = ""; + if ($r) { + foreach ($r as $rr) { + $id = $rr['id']; + } + } + return $id; + } +}
\ No newline at end of file diff --git a/include/reddav.php b/include/reddav.php index 3de24661e..5c93daf1f 100644 --- a/include/reddav.php +++ b/include/reddav.php @@ -14,14 +14,18 @@ */ use Sabre\DAV; + require_once('vendor/autoload.php'); require_once('include/attach.php'); - /** * @brief RedDirectory class. * * A class that represents a directory. + * + * @extends \Sabre\DAV\Node + * @implements \Sabre\DAV\ICollection + * @implements \Sabre\DAV\IQuota */ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota { @@ -1176,326 +1180,3 @@ class RedBasicAuth extends DAV\Auth\Backend\AbstractBasic { } } // class RedBasicAuth - - - -/** - * @brief RedBrowser class. - * - * RedBrowser is a SabreDAV server-plugin to provide a view to the DAV in - * the browser - */ -class RedBrowser extends DAV\Browser\Plugin { - - /** - * @var RedBasicAuth - */ - private $auth; - - /** - * @brief Constructor for RedBrowser. - * - * @param RedBasicAuth &$auth - */ - function __construct(&$auth) { - $this->auth = $auth; - $this->enableAssets = false; - } - - // The DAV browser is instantiated after the auth module and directory classes but before we know the current - // directory and who the owner and observer are. So we add a pointer to the browser into the auth module and vice - // versa. Then when we've figured out what directory is actually being accessed, we call the following function - // to decide whether or not to show web elements which include writeable objects. - // @todo Maybe this can be solved with some $server->subscribeEvent()? - function set_writeable() { - if (! $this->auth->owner_id) { - $this->enablePost = false; - } - - if (! perm_is_allowed($this->auth->owner_id, get_observer_hash(), 'write_storage')) { - $this->enablePost = false; - } else { - $this->enablePost = true; - } - } - - /** - * @brief Creates the directory listing for the given path. - * - * @param string $path which should be displayed - */ - public function generateDirectoryIndex($path) { - // (owner_id = channel_id) is visitor owner of this directory? - $is_owner = ((local_user() && $this->auth->owner_id == local_user()) ? true : false); - - if ($this->auth->getTimezone()) - date_default_timezone_set($this->auth->getTimezone()); - - require_once('include/conversation.php'); - - if ($this->auth->owner_nick) { - $html = profile_tabs(get_app(), (($is_owner) ? true : false), $this->auth->owner_nick); - } - - $files = $this->server->getPropertiesForPath($path, array( - '{DAV:}displayname', - '{DAV:}resourcetype', - '{DAV:}getcontenttype', - '{DAV:}getcontentlength', - '{DAV:}getlastmodified', - ), 1); - - $parent = $this->server->tree->getNodeForPath($path); - - $parentpath = array(); - // only show parent if not leaving /cloud/; TODO how to improve this? - if ($path && $path != "cloud") { - list($parentUri) = DAV\URLUtil::splitPath($path); - $fullPath = DAV\URLUtil::encodePath($this->server->getBaseUri() . $parentUri); - - $parentpath['icon'] = $this->enableAssets ? '<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl('icons/parent' . $this->iconExtension) . '" width="24" alt="' . t('parent') . '"></a>' : ''; - $parentpath['path'] = $fullPath; - } - - $f = array(); - foreach ($files as $file) { - $ft = array(); - $type = null; - - // This is the current directory, we can skip it - if (rtrim($file['href'],'/')==$path) continue; - - list(, $name) = DAV\URLUtil::splitPath($file['href']); - - if (isset($file[200]['{DAV:}resourcetype'])) { - $type = $file[200]['{DAV:}resourcetype']->getValue(); - - // resourcetype can have multiple values - if (!is_array($type)) $type = array($type); - - foreach ($type as $k=>$v) { - // Some name mapping is preferred - switch ($v) { - case '{DAV:}collection' : - $type[$k] = t('Collection'); - break; - case '{DAV:}principal' : - $type[$k] = t('Principal'); - break; - case '{urn:ietf:params:xml:ns:carddav}addressbook' : - $type[$k] = t('Addressbook'); - break; - case '{urn:ietf:params:xml:ns:caldav}calendar' : - $type[$k] = t('Calendar'); - break; - case '{urn:ietf:params:xml:ns:caldav}schedule-inbox' : - $type[$k] = t('Schedule Inbox'); - break; - case '{urn:ietf:params:xml:ns:caldav}schedule-outbox' : - $type[$k] = t('Schedule Outbox'); - break; - case '{http://calendarserver.org/ns/}calendar-proxy-read' : - $type[$k] = 'Proxy-Read'; - break; - case '{http://calendarserver.org/ns/}calendar-proxy-write' : - $type[$k] = 'Proxy-Write'; - break; - } - } - $type = implode(', ', $type); - } - - // If no resourcetype was found, we attempt to use - // the contenttype property - if (!$type && isset($file[200]['{DAV:}getcontenttype'])) { - $type = $file[200]['{DAV:}getcontenttype']; - } - if (!$type) $type = t('Unknown'); - - $size = isset($file[200]['{DAV:}getcontentlength']) ? (int)$file[200]['{DAV:}getcontentlength'] : ''; - $lastmodified = ((isset($file[200]['{DAV:}getlastmodified'])) ? $file[200]['{DAV:}getlastmodified']->getTime()->format('Y-m-d H:i:s') : ''); - - $fullPath = DAV\URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path ? $path . '/' : '') . $name, '/')); - - $displayName = isset($file[200]['{DAV:}displayname']) ? $file[200]['{DAV:}displayname'] : $name; - - $displayName = $this->escapeHTML($displayName); - $type = $this->escapeHTML($type); - - $icon = ''; - if ($this->enableAssets) { - $node = $this->server->tree->getNodeForPath(($path ? $path . '/' : '') . $name); - foreach (array_reverse($this->iconMap) as $class=>$iconName) { - if ($node instanceof $class) { - $icon = '<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl($iconName . $this->iconExtension) . '" alt="" width="24"></a>'; - break; - } - } - } - - $parentHash = ""; - $owner = $this->auth->owner_id; - $splitPath = split("/", $fullPath); - if (count($splitPath) > 3) { - for ($i = 3; $i < count($splitPath); $i++) { - $attachName = urldecode($splitPath[$i]); - $attachHash = $this->findAttachHash($owner, $parentHash, $attachName); - $parentHash = $attachHash; - } - } - - $attachIcon = ""; // "<a href=\"attach/".$attachHash."\" title=\"".$displayName."\"><i class=\"icon-download\"></i></a>"; - - // put the array for this file together - $ft['attachId'] = $this->findAttachIdByHash($attachHash); - $ft['fileStorageUrl'] = substr($fullPath, 0, strpos($fullPath, "cloud/")) . "filestorage/" . $this->auth->getCurrentUser(); - $ft['icon'] = $icon; - $ft['attachIcon'] = (($size) ? $attachIcon : ''); - // @todo Should this be an item value, not a global one? - $ft['is_owner'] = $is_owner; - $ft['fullPath'] = $fullPath; - $ft['displayName'] = $displayName; - $ft['type'] = $type; - $ft['size'] = $size; - $ft['sizeFormatted'] = $this->userReadableSize($size); - $ft['lastmodified'] = (($lastmodified) ? datetime_convert('UTC', date_default_timezone_get(), $lastmodified) : ''); - - $f[] = $ft; - } - - // Storage and quota for the account (all channels of the owner of this directory)! - $limit = service_class_fetch($owner, 'attach_upload_limit'); - $r = q("SELECT SUM(filesize) AS total FROM attach WHERE aid = %d", - intval($this->auth->channel_account_id) - ); - $used = $r[0]['total']; - if ($used) { - $quotaDesc = t('%1$s used'); - $quotaDesc = sprintf($quotaDesc, - $this->userReadableSize($used)); - } - if ($limit && $used) { - $quotaDesc = t('%1$s used of %2$s (%3$s%)'); - $quotaDesc = sprintf($quotaDesc, - $this->userReadableSize($used), - $this->userReadableSize($limit), - round($used / $limit, 1)); - } - - // prepare quota for template - $quota['used'] = $used; - $quota['limit'] = $limit; - $quota['desc'] = $quotaDesc; - - $html .= replace_macros(get_markup_template('cloud_directory.tpl'), array( - '$header' => t('Files') . ": " . $this->escapeHTML($path) . "/", - '$parentpath' => $parentpath, - '$entries' => $f, - '$quota' => $quota, - '$name' => t('Name'), - '$type' => t('Type'), - '$size' => t('Size'), - '$lastmod' => t('Last Modified'), - '$parent' => t('parent'), - '$edit' => t('Edit'), - '$delete' => t('Delete'), - '$total' => t('Total') - )); - - $output = ''; - if ($this->enablePost) { - $this->server->broadcastEvent('onHTMLActionsPanel', array($parent, &$output)); - } - $html .= $output; - - get_app()->page['content'] = $html; - construct_page(get_app()); - } - - function userReadableSize($size) { - $ret = ""; - if (is_numeric($size)) { - $incr = 0; - $k = 1024; - $unit = array('bytes', 'KB', 'MB', 'GB', 'TB', 'PB'); - while (($size / $k) >= 1){ - $incr++; - $size = round($size / $k, 2); - } - $ret = $size . " " . $unit[$incr]; - } - return $ret; - } - - /** - * Creates a form to add new folders and upload files. - * - * @param DAV\INode $node - * @param string &$output - */ - public function htmlActionsPanel(DAV\INode $node, &$output) { - - //Removed link to filestorage page - //if($this->auth->owner_id && $this->auth->owner_id == $this->auth->channel_id) { - // $channel = get_app()->get_channel(); - // if($channel) { - // $output .= '<tr><td colspan="2"><a href="filestorage/' . $channel['channel_address'] . '" >' . t('Edit File properties') . '</a></td></tr><tr><td> </td></tr>'; - // } - //} - - if (! $node instanceof DAV\ICollection) - return; - - // We also know fairly certain that if an object is a non-extended - // SimpleCollection, we won't need to show the panel either. - if (get_class($node) === 'Sabre\\DAV\\SimpleCollection') - return; - - $output .= replace_macros(get_markup_template('cloud_actionspanel.tpl'), array( - '$folder_header' => t('Create new folder'), - '$folder_submit' => t('Create'), - '$upload_header' => t('Upload file'), - '$upload_submit' => t('Upload') - )); - } - - /** - * This method takes a path/name of an asset and turns it into url - * suiteable for http access. - * - * @param string $assetName - * @return string - */ - protected function getAssetUrl($assetName) { - return z_root() . '/cloud/?sabreAction=asset&assetName=' . urlencode($assetName); - } - - protected function findAttachHash($owner, $parentHash, $attachName) { - $r = q("SELECT * FROM attach WHERE uid = %d AND folder = '%s' AND filename = '%s' ORDER BY edited desc LIMIT 1", - intval($owner), - dbesc($parentHash), - dbesc($attachName) - ); - $hash = ""; - if ($r) { - foreach ($r as $rr) { - $hash = $rr['hash']; - } - } - return $hash; - } - - protected function findAttachIdByHash($attachHash) { - $r = q("SELECT * FROM attach WHERE hash = '%s' ORDER BY edited DESC LIMIT 1", - dbesc($attachHash) - ); - $id = ""; - if ($r) { - foreach ($r as $rr) { - $id = $rr['id']; - } - } - return $id; - } - -} // class RedBrowser |