2016-02-19 08:06:10 +00:00
< ? php
namespace Zotlabs\Storage ;
use Sabre\DAV ;
/**
* @ brief Provides a DAV frontend for the webbrowser .
*
2016-10-01 22:41:25 +00:00
* Browser is a SabreDAV server - plugin to provide a view to the DAV storage
2016-02-19 08:06:10 +00:00
* for the webbrowser .
*
2016-10-01 22:41:25 +00:00
* @ extends \\Sabre\\DAV\\Browser\\Plugin
2016-02-19 08:06:10 +00:00
*
* @ link http :// github . com / friendica / red
* @ license http :// opensource . org / licenses / mit - license . php The MIT License ( MIT )
*/
class Browser extends DAV\Browser\Plugin {
2017-06-26 08:36:33 +00:00
public $build_page = false ;
2016-02-19 08:06:10 +00:00
/**
* @ see set_writeable ()
2016-10-01 22:41:25 +00:00
* @ see \\Sabre\\DAV\\Auth\\Backend\\BackendInterface
* @ var BasicAuth $auth
2016-02-19 08:06:10 +00:00
*/
private $auth ;
/**
2016-10-01 22:41:25 +00:00
* @ brief Constructor for Browser class .
2016-02-19 08:06:10 +00:00
*
* $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 .
* @ todo disable enablePost by default and only activate if permissions
* grant edit rights .
*
* Disable assets with $enableAssets = false . Should get some thumbnail views
* anyway .
*
2016-10-01 22:41:25 +00:00
* @ param BasicAuth & $auth
2016-02-19 08:06:10 +00:00
*/
public function __construct ( & $auth ) {
$this -> auth = $auth ;
parent :: __construct ( true , 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 .
*
* @ fixme It only disable / enable the visible parts . Not the POST handler
* which handels the actual requests when uploading files or creating folders .
*
* @ todo Maybe this whole way of doing 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_channel () && $this -> auth -> owner_id == local_channel ()) ? true : false );
if ( $this -> auth -> getTimezone ())
date_default_timezone_set ( $this -> auth -> getTimezone ());
require_once ( 'include/conversation.php' );
require_once ( 'include/text.php' );
if ( $this -> auth -> owner_nick ) {
2017-03-20 16:24:48 +00:00
$html = '' ;
2016-02-19 08:06:10 +00:00
}
$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 ();
2016-09-28 19:53:16 +00:00
// only show parent if not leaving /cloud/; TODO how to improve this?
2016-02-19 08:06:10 +00:00
if ( $path && $path != " cloud " ) {
2016-09-28 19:53:16 +00:00
list ( $parentUri ) = \Sabre\Uri\split ( $path );
$fullPath = \Sabre\HTTP\encodePath ( $this -> server -> getBaseUri () . $parentUri );
2016-02-19 08:06:10 +00:00
$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
2016-09-28 19:53:16 +00:00
if ( rtrim ( $file [ 'href' ], '/' ) == $path ) continue ;
2016-02-19 08:06:10 +00:00
2016-09-28 19:53:16 +00:00
list (, $name ) = \Sabre\Uri\split ( $file [ 'href' ]);
2016-02-19 08:06:10 +00:00
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' ) : '' );
2016-09-28 19:53:16 +00:00
$fullPath = \Sabre\HTTP\encodePath ( '/' . trim ( $this -> server -> getBaseUri () . ( $path ? $path . '/' : '' ) . $name , '/' ));
2016-02-19 08:06:10 +00:00
$displayName = isset ( $file [ 200 ][ '{DAV:}displayname' ]) ? $file [ 200 ][ '{DAV:}displayname' ] : $name ;
$displayName = $this -> escapeHTML ( $displayName );
$type = $this -> escapeHTML ( $type );
2017-10-31 01:49:18 +00:00
2016-02-19 08:06:10 +00:00
$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 = explode ( '/' , $fullPath );
if ( count ( $splitPath ) > 3 ) {
for ( $i = 3 ; $i < count ( $splitPath ); $i ++ ) {
$attachName = urldecode ( $splitPath [ $i ]);
$attachHash = $this -> findAttachHash ( $owner , $parentHash , $attachName );
$parentHash = $attachHash ;
}
}
2017-10-31 01:49:18 +00:00
$photo_icon = '' ;
if ( strpos ( $type , 'image/' ) === 0 && $attachHash ) {
$r = q ( " select resource_id, imgscale from photo where resource_id = '%s' and imgscale in ( %d, %d ) order by imgscale asc limit 1 " ,
dbesc ( $attachHash ),
intval ( PHOTO_RES_320 ),
intval ( PHOTO_RES_PROFILE_80 )
);
if ( $r ) {
$photo_icon = $r [ 0 ][ 'resource_id' ] . '-' . $r [ 0 ][ 'imgscale' ];
}
}
2016-04-30 18:13:30 +00:00
$attachIcon = " " ; // "<a href=\"attach/".$attachHash."\" title=\"".$displayName."\"><i class=\"fa fa-arrow-circle-o-down\"></i></a>";
2016-02-19 08:06:10 +00:00
// 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 ;
2017-10-31 01:49:18 +00:00
$ft [ 'photo_icon' ] = $photo_icon ;
2016-02-19 08:06:10 +00:00
$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' ] = userReadableSize ( $size );
$ft [ 'lastmodified' ] = (( $lastmodified ) ? datetime_convert ( 'UTC' , date_default_timezone_get (), $lastmodified ) : '' );
$ft [ 'iconFromType' ] = getIconFromType ( $type );
$f [] = $ft ;
}
2017-11-06 03:47:44 +00:00
2016-02-19 08:06:10 +00:00
$output = '' ;
if ( $this -> enablePost ) {
2016-05-28 15:46:24 +00:00
$this -> server -> emit ( 'onHTMLActionsPanel' , array ( $parent , & $output , $path ));
2016-02-19 08:06:10 +00:00
}
2017-10-31 01:49:18 +00:00
2016-02-19 08:06:10 +00:00
$html .= replace_macros ( get_markup_template ( 'cloud.tpl' ), array (
'$header' => t ( 'Files' ) . " : " . $this -> escapeHTML ( $path ) . " / " ,
'$total' => t ( 'Total' ),
'$actionspanel' => $output ,
'$shared' => t ( 'Shared' ),
'$create' => t ( 'Create' ),
'$upload' => t ( 'Upload' ),
'$is_owner' => $is_owner ,
'$parentpath' => $parentpath ,
2017-10-31 01:49:18 +00:00
'$cpath' => bin2hex ( \App :: $query_string ),
'$tiles' => intval ( $_SESSION [ 'cloud_tiles' ]),
2016-02-19 08:06:10 +00:00
'$entries' => $f ,
'$name' => t ( 'Name' ),
'$type' => t ( 'Type' ),
'$size' => t ( 'Size' ),
'$lastmod' => t ( 'Last Modified' ),
'$parent' => t ( 'parent' ),
'$edit' => t ( 'Edit' ),
'$delete' => t ( 'Delete' ),
'$nick' => $this -> auth -> getCurrentUser ()
));
2017-03-28 23:02:47 +00:00
$a = false ;
2017-10-09 19:22:57 +00:00
nav_set_selected ( 'Files' );
2017-07-21 20:36:47 +00:00
2016-03-31 23:06:03 +00:00
\App :: $page [ 'content' ] = $html ;
2017-03-28 23:07:22 +00:00
load_pdl ();
2016-02-19 08:06:10 +00:00
2016-05-18 00:46:30 +00:00
$current_theme = \Zotlabs\Render\Theme :: current ();
2016-09-28 19:53:16 +00:00
$theme_info_file = 'view/theme/' . $current_theme [ 0 ] . '/php/theme.php' ;
if ( file_exists ( $theme_info_file )) {
2016-02-19 08:06:10 +00:00
require_once ( $theme_info_file );
2016-05-18 00:46:30 +00:00
if ( function_exists ( str_replace ( '-' , '_' , $current_theme [ 0 ]) . '_init' )) {
$func = str_replace ( '-' , '_' , $current_theme [ 0 ]) . '_init' ;
2016-02-19 08:06:10 +00:00
$func ( $a );
}
}
2016-05-25 12:18:41 +00:00
$this -> server -> httpResponse -> setHeader ( 'Content-Security-Policy' , " script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' " );
2017-06-26 08:36:33 +00:00
$this -> build_page = true ;
2016-02-19 08:06:10 +00:00
}
/**
* @ brief Creates a form to add new folders and upload files .
*
* @ param \Sabre\DAV\INode $node
2016-10-01 22:41:25 +00:00
* @ param [ in , out ] string & $output
* @ param string $path
2016-02-19 08:06:10 +00:00
*/
2016-05-28 15:46:24 +00:00
public function htmlActionsPanel ( DAV\INode $node , & $output , $path ) {
2016-10-01 22:41:25 +00:00
if ( ! $node instanceof DAV\ICollection )
2016-02-19 08:06:10 +00:00
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 ;
2016-07-25 03:27:59 +00:00
require_once ( 'include/acl_selectors.php' );
$aclselect = null ;
$lockstate = '' ;
2016-10-01 22:41:25 +00:00
if ( $this -> auth -> owner_id ) {
2016-07-25 03:27:59 +00:00
$channel = channelx_by_n ( $this -> auth -> owner_id );
2016-10-01 22:41:25 +00:00
if ( $channel ) {
2016-07-25 03:27:59 +00:00
$acl = new \Zotlabs\Access\AccessList ( $channel );
$channel_acl = $acl -> get ();
$lockstate = (( $acl -> is_private ()) ? 'lock' : 'unlock' );
$aclselect = (( local_channel () == $this -> auth -> owner_id ) ? populate_acl ( $channel_acl , false , \Zotlabs\Lib\PermissionDescription :: fromGlobalPermission ( 'view_storage' )) : '' );
}
}
2016-02-19 08:06:10 +00:00
// Storage and quota for the account (all channels of the owner of this directory)!
2016-04-29 04:02:27 +00:00
$limit = engr_units_to_bytes ( service_class_fetch ( $owner , 'attach_upload_limit' ));
2016-02-19 08:06:10 +00:00
$r = q ( " SELECT SUM(filesize) AS total FROM attach WHERE aid = %d " ,
intval ( $this -> auth -> channel_account_id )
);
$used = $r [ 0 ][ 'total' ];
2016-10-01 22:41:25 +00:00
if ( $used ) {
2016-02-19 08:06:10 +00:00
$quotaDesc = t ( 'You are using %1$s of your available file storage.' );
$quotaDesc = sprintf ( $quotaDesc ,
userReadableSize ( $used ));
}
2016-10-01 22:41:25 +00:00
if ( $limit && $used ) {
2016-02-19 08:06:10 +00:00
$quotaDesc = t ( 'You are using %1$s of %2$s available file storage. (%3$s%)' );
$quotaDesc = sprintf ( $quotaDesc ,
userReadableSize ( $used ),
userReadableSize ( $limit ),
round ( $used / $limit , 1 ) * 100 );
}
// prepare quota for template
$quota = array ();
$quota [ 'used' ] = $used ;
$quota [ 'limit' ] = $limit ;
$quota [ 'desc' ] = $quotaDesc ;
$quota [ 'warning' ] = ((( $limit ) && (( round ( $used / $limit , 1 ) * 100 ) >= 90 )) ? t ( 'WARNING:' ) : '' ); // 10485760 bytes = 100MB
2017-02-21 07:03:48 +00:00
// strip 'cloud/nickname', but only at the beginning of the path
$special = 'cloud/' . $this -> auth -> owner_nick ;
$count = strlen ( $special );
if ( strpos ( $path , $special ) === 0 )
$path = trim ( substr ( $path , $count ), '/' );
2016-08-10 14:17:09 +00:00
2017-02-27 23:13:49 +00:00
$info = t ( 'Please use DAV to upload large (video, audio) files.<br>See <a class="zrl" href="help/member/member_guide#Cloud_Desktop_Clients">Cloud Desktop Clients</a>' );
2016-02-19 08:06:10 +00:00
$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' ),
2016-07-24 11:41:53 +00:00
'$quota' => $quota ,
2017-02-27 23:13:49 +00:00
'$info' => $info ,
2016-07-25 03:27:59 +00:00
'$channick' => $this -> auth -> owner_nick ,
'$aclselect' => $aclselect ,
2016-08-04 08:36:45 +00:00
'$allow_cid' => acl2json ( $channel_acl [ 'allow_cid' ]),
'$allow_gid' => acl2json ( $channel_acl [ 'allow_gid' ]),
'$deny_cid' => acl2json ( $channel_acl [ 'deny_cid' ]),
'$deny_gid' => acl2json ( $channel_acl [ 'deny_gid' ]),
2016-07-25 03:27:59 +00:00
'$lockstate' => $lockstate ,
'$return_url' => \App :: $cmd ,
2016-08-10 14:17:09 +00:00
'$path' => $path ,
'$folder' => find_folder_hash_by_path ( $this -> auth -> owner_id , $path ),
2017-07-29 21:32:48 +00:00
'$dragdroptext' => t ( 'Drop files here to immediately upload' ),
'$notify' => [ 'notify' , t ( 'Show in your contacts shared folder' ), 0 , '' , [ t ( 'No' ), t ( 'Yes' )]]
2016-02-19 08:06:10 +00:00
));
}
/**
* 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 .
2016-09-28 19:53:16 +00:00
*
2016-02-19 08:06:10 +00:00
* @ param int $owner
* The owner_id
2016-10-01 22:41:25 +00:00
* @ param string $parentHash
2016-02-19 08:06:10 +00:00
* 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 )
);
2016-09-28 19:53:16 +00:00
$hash = '' ;
2016-02-19 08:06:10 +00:00
if ( $r ) {
foreach ( $r as $rr ) {
$hash = $rr [ 'hash' ];
}
}
2016-10-01 22:41:25 +00:00
2016-02-19 08:06:10 +00:00
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 ;
}
}