aboutsummaryrefslogtreecommitdiffstats
path: root/include/RedDAV
diff options
context:
space:
mode:
authorKlaus Weidenbach <Klaus.Weidenbach@gmx.net>2014-10-06 23:20:17 +0200
committerKlaus Weidenbach <Klaus.Weidenbach@gmx.net>2014-10-06 23:38:33 +0200
commit33394f58ff8cc54aebbd35a53b93ec3fa1bfdf57 (patch)
tree04c74c04454f0b79d329255742dd3903e35d4492 /include/RedDAV
parentcef8955d572636e313623e363aed6bc23b732e4f (diff)
downloadvolse-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/RedDAV')
-rw-r--r--include/RedDAV/RedBrowser.php361
1 files changed, 361 insertions, 0 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&#37;)');
+ $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