2014-11-13 18:31:36 +01:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* EGroupware API: VFS sharing
|
|
|
|
*
|
|
|
|
* @link http://www.egroupware.org
|
|
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
|
|
* @package api
|
2016-03-20 17:19:53 +01:00
|
|
|
* @subpackage Vfs
|
2014-11-13 18:31:36 +01:00
|
|
|
* @author Ralf Becker <rb@stylite.de>
|
2016-03-20 17:19:53 +01:00
|
|
|
* @copyright (c) 2014-16 by Ralf Becker <rb@stylite.de>
|
2014-11-13 18:31:36 +01:00
|
|
|
* @version $Id$
|
|
|
|
*/
|
|
|
|
|
2016-03-20 17:19:53 +01:00
|
|
|
namespace EGroupware\Api\Vfs;
|
|
|
|
|
|
|
|
use EGroupware\Api;
|
|
|
|
use EGroupware\Api\Vfs;
|
2018-03-22 13:04:28 +01:00
|
|
|
use EGroupware\Collabora\Wopi;
|
2016-03-20 17:19:53 +01:00
|
|
|
|
|
|
|
use filemanager_ui;
|
|
|
|
|
2014-11-13 18:31:36 +01:00
|
|
|
/**
|
|
|
|
* VFS sharing
|
|
|
|
*
|
|
|
|
* Token generation uses openssl_random_pseudo_bytes, if available, otherwise
|
2016-03-20 17:19:53 +01:00
|
|
|
* mt_rand based Api\Auth::randomstring is used.
|
2014-11-18 13:55:32 +01:00
|
|
|
*
|
2015-01-29 14:54:34 +01:00
|
|
|
* Existing user sessions are kept whenever possible by an additional mount into regular VFS:
|
|
|
|
* - share owner is current user (no problems with rights, they simply match)
|
|
|
|
* - share owner has owner-right for share: we create a temp. eACL for current user
|
|
|
|
* --> in all other cases session will be replaced with one of the anonymous user,
|
|
|
|
* as we dont support mounting with rights of share owner (VFS uses Vfs::$user!)
|
|
|
|
*
|
|
|
|
* @todo handle mounts of an entry directory /apps/$app/$id
|
2014-11-18 13:55:32 +01:00
|
|
|
* @todo handle mounts inside shared directory (they get currently lost)
|
|
|
|
* @todo handle absolute symlinks (wont work as we use share as root)
|
2014-11-13 18:31:36 +01:00
|
|
|
*/
|
2016-03-20 17:19:53 +01:00
|
|
|
class Sharing
|
2014-11-13 18:31:36 +01:00
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Length of base64 encoded token (real length is only 3/4 of it)
|
2014-12-05 12:27:22 +01:00
|
|
|
*
|
|
|
|
* Dropbox uses just 15 chars (letters/numbers 5-6 bit), php sessions use 32 chars (hex = 4bits),
|
|
|
|
* so 32 chars of base64 = 6bits should be plenty.
|
2014-11-13 18:31:36 +01:00
|
|
|
*/
|
2014-12-05 12:27:22 +01:00
|
|
|
const TOKEN_LENGTH = 32;
|
2014-11-13 18:31:36 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Name of table used for storing tokens
|
|
|
|
*/
|
|
|
|
const TABLE = 'egw_sharing';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reference to global db object
|
|
|
|
*
|
2016-05-06 13:13:19 +02:00
|
|
|
* @var Api\Db
|
2014-11-13 18:31:36 +01:00
|
|
|
*/
|
2014-11-18 13:55:32 +01:00
|
|
|
protected static $db;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Share we are instanciated for
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
protected $share;
|
2014-11-13 18:31:36 +01:00
|
|
|
|
2014-12-03 17:25:10 +01:00
|
|
|
/**
|
|
|
|
* Modes ATTACH is NOT a sharing mode, but it is traditional mode in email
|
|
|
|
*/
|
|
|
|
const ATTACH = 'attach';
|
|
|
|
const LINK = 'link';
|
|
|
|
const READONLY = 'share_ro';
|
|
|
|
const WRITABLE = 'share_rw';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Modes for sharing files
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
static $modes = array(
|
|
|
|
self::ATTACH => array(
|
|
|
|
'label' => 'Attachment',
|
|
|
|
'title' => 'Works reliable for total size up to 1-2 MB, might work for 5-10 MB, most likely to fail for >10MB',
|
|
|
|
),
|
|
|
|
self::LINK => array(
|
|
|
|
'label' => 'Download link',
|
|
|
|
'title' => 'Link is appended to mail allowing recipients to download currently attached version of files',
|
|
|
|
),
|
|
|
|
self::READONLY => array(
|
|
|
|
'label' => 'Readonly share',
|
|
|
|
'title' => 'Link is appended to mail allowing recipients to download up to date version of files',
|
|
|
|
),
|
|
|
|
self::WRITABLE => array(
|
|
|
|
'label' => 'Writable share',
|
|
|
|
'title' => 'Link is appended to mail allowing recipients to download or modify up to date version of files (EPL only)'
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
2014-11-13 18:31:36 +01:00
|
|
|
/**
|
2014-11-18 13:55:32 +01:00
|
|
|
* Protected constructor called via self::create_session
|
|
|
|
*
|
|
|
|
* @param string $token
|
|
|
|
* @param array $share
|
2014-11-13 18:31:36 +01:00
|
|
|
*/
|
2014-11-18 13:55:32 +01:00
|
|
|
protected function __construct(array $share)
|
2014-11-13 18:31:36 +01:00
|
|
|
{
|
2014-11-18 13:55:32 +01:00
|
|
|
self::$db = $GLOBALS['egw']->db;
|
|
|
|
$this->share = $share;
|
2014-11-13 18:31:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-01-20 23:11:35 +01:00
|
|
|
* Get token from url
|
2014-11-13 18:31:36 +01:00
|
|
|
*/
|
2015-01-20 23:11:35 +01:00
|
|
|
public static function get_token()
|
2014-11-13 18:31:36 +01:00
|
|
|
{
|
|
|
|
// WebDAV has no concept of a query string and clients (including cadaver)
|
|
|
|
// seem to pass '?' unencoded, so we need to extract the path info out
|
|
|
|
// of the request URI ourselves
|
|
|
|
// if request URI contains a full url, remove schema and domain
|
|
|
|
$matches = null;
|
|
|
|
if (preg_match('|^https?://[^/]+(/.*)$|', $path_info=$_SERVER['REQUEST_URI'], $matches))
|
|
|
|
{
|
|
|
|
$path_info = $matches[1];
|
|
|
|
}
|
|
|
|
$path_info = substr($path_info, strlen($_SERVER['SCRIPT_NAME']));
|
2014-12-09 13:08:00 +01:00
|
|
|
list(, $token/*, $path*/) = preg_split('|[/?]|', $path_info, 3);
|
2014-11-13 18:31:36 +01:00
|
|
|
|
2014-11-18 13:55:32 +01:00
|
|
|
return $token;
|
|
|
|
}
|
|
|
|
|
2015-01-20 23:11:35 +01:00
|
|
|
/**
|
|
|
|
* Get root of share
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function get_root()
|
|
|
|
{
|
|
|
|
return $this->share['share_root'];
|
|
|
|
}
|
|
|
|
|
2014-11-18 13:55:32 +01:00
|
|
|
/**
|
|
|
|
* Create sharing session
|
|
|
|
*
|
2015-02-28 23:08:23 +01:00
|
|
|
* Certain cases:
|
|
|
|
* a) there is not session $keep_session === null
|
|
|
|
* --> create new anon session with just filemanager rights and share as fstab
|
|
|
|
* b) there is a session $keep_session === true
|
|
|
|
* b1) current user is share owner (eg. checking the link)
|
|
|
|
* --> mount share under token additionally
|
|
|
|
* b2) current user not share owner
|
|
|
|
* b2a) need/use filemanager UI (eg. directory)
|
|
|
|
* --> destroy current session and continue with a)
|
|
|
|
* b2b) single file or WebDAV
|
|
|
|
* --> modify EGroupware enviroment for that request only, no change in session
|
|
|
|
*
|
|
|
|
* @param boolean $keep_session =null null: create a new session, true: try mounting it into existing (already verified) session
|
2014-11-18 13:55:32 +01:00
|
|
|
* @return string with sessionid, does NOT return if no session created
|
|
|
|
*/
|
2015-02-28 23:08:23 +01:00
|
|
|
public static function create_session($keep_session=null)
|
2014-11-18 13:55:32 +01:00
|
|
|
{
|
|
|
|
self::$db = $GLOBALS['egw']->db;
|
|
|
|
|
2017-06-27 23:37:34 +02:00
|
|
|
$token = static::get_token();
|
2017-08-30 18:09:54 +02:00
|
|
|
|
2015-01-20 23:11:35 +01:00
|
|
|
// are we called from header include, because session did not verify
|
|
|
|
// --> check if it verifys for our token
|
|
|
|
if ($token && !$keep_session)
|
|
|
|
{
|
|
|
|
$_SERVER['PHP_AUTH_USER'] = $token;
|
|
|
|
if (!isset($_SERVER['PHP_AUTH_PW'])) $_SERVER['PHP_AUTH_PW'] = '';
|
|
|
|
|
|
|
|
unset($GLOBALS['egw_info']['flags']['autocreate_session_callback']);
|
2018-03-05 11:13:54 +01:00
|
|
|
if (isset($GLOBALS['egw']->session) && $GLOBALS['egw']->session->verify()
|
|
|
|
&& isset($GLOBALS['egw']->sharing) && $GLOBALS['egw']->sharing->share['share_token'] === $token)
|
2015-01-20 23:11:35 +01:00
|
|
|
{
|
|
|
|
return $GLOBALS['egw']->session->sessionid;
|
|
|
|
}
|
|
|
|
}
|
2014-11-18 13:55:32 +01:00
|
|
|
|
|
|
|
if (empty($token) || !($share = self::$db->select(self::TABLE, '*', array(
|
2014-11-13 18:31:36 +01:00
|
|
|
'share_token' => $token,
|
2014-11-18 13:55:32 +01:00
|
|
|
'(share_expires IS NULL OR share_expires > '.self::$db->quote(time(), 'date').')',
|
|
|
|
), __LINE__, __FILE__)->fetch()) ||
|
|
|
|
!$GLOBALS['egw']->accounts->exists($share['share_owner']))
|
2014-11-13 18:31:36 +01:00
|
|
|
{
|
|
|
|
sleep(1);
|
|
|
|
$status = '404 Not Found';
|
|
|
|
header("HTTP/1.1 $status");
|
|
|
|
header("X-WebDAV-Status: $status", true);
|
2014-11-18 13:55:32 +01:00
|
|
|
echo "Requested resource '/".htmlspecialchars($token)."' does NOT exist!\n";
|
2016-03-20 17:30:01 +01:00
|
|
|
exit;
|
2014-11-18 13:55:32 +01:00
|
|
|
}
|
2014-12-04 12:25:56 +01:00
|
|
|
|
|
|
|
// check password, if required
|
|
|
|
if ($share['share_passwd'] && (empty($_SERVER['PHP_AUTH_PW']) ||
|
2016-03-20 17:19:53 +01:00
|
|
|
!(Api\Auth::compare_password($_SERVER['PHP_AUTH_PW'], $share['share_passwd'], 'crypt') ||
|
|
|
|
Api\Header\Authenticate::decode_password($_SERVER['PHP_AUTH_PW']) &&
|
|
|
|
Api\Auth::compare_password($_SERVER['PHP_AUTH_PW'], $share['share_passwd'], 'crypt'))))
|
2014-12-04 12:25:56 +01:00
|
|
|
{
|
|
|
|
$realm = 'EGroupware share '.$share['share_token'];
|
|
|
|
header('WWW-Authenticate: Basic realm="'.$realm.'"');
|
|
|
|
$status = '401 Unauthorized';
|
|
|
|
header("HTTP/1.1 $status");
|
|
|
|
header("X-WebDAV-Status: $status", true);
|
|
|
|
echo "<html>\n<head>\n<title>401 Unauthorized</title>\n<body>\nAuthorization failed.\n</body>\n</html>\n";
|
2016-03-20 17:30:01 +01:00
|
|
|
exit;
|
2014-12-04 12:25:56 +01:00
|
|
|
}
|
2014-11-18 13:55:32 +01:00
|
|
|
|
2015-03-02 22:09:08 +01:00
|
|
|
// need to reset fs_tab, as resolve_url does NOT work with just share mounted
|
|
|
|
if (count($GLOBALS['egw_info']['server']['vfs_fstab']) <= 1)
|
|
|
|
{
|
|
|
|
unset($GLOBALS['egw_info']['server']['vfs_fstab']); // triggers reset of fstab in mount()
|
2016-03-20 17:19:53 +01:00
|
|
|
$GLOBALS['egw_info']['server']['vfs_fstab'] = Vfs::mount();
|
|
|
|
Vfs::clearstatcache();
|
2015-03-02 22:09:08 +01:00
|
|
|
}
|
2016-03-20 17:19:53 +01:00
|
|
|
$share['resolve_url'] = Vfs::resolve_url($share['share_path'], true, true, true, true); // true = fix evtl. contained url parameter
|
2014-11-14 09:50:05 +01:00
|
|
|
// if share not writable append ro=1 to mount url to make it readonly
|
2018-03-21 12:43:25 +01:00
|
|
|
if (!($share['share_writable'] & 1))
|
2014-11-14 09:50:05 +01:00
|
|
|
{
|
|
|
|
$share['resolve_url'] .= (strpos($share['resolve_url'], '?') ? '&' : '?').'ro=1';
|
|
|
|
}
|
2014-11-13 18:31:36 +01:00
|
|
|
//_debug_array($share);
|
|
|
|
|
2015-01-20 23:11:35 +01:00
|
|
|
if ($keep_session) // add share to existing session
|
|
|
|
{
|
|
|
|
$share['share_root'] = '/'.$share['share_token'];
|
|
|
|
|
2015-02-28 23:08:23 +01:00
|
|
|
// if current user is not the share owner, we cant just mount share
|
2016-03-20 17:19:53 +01:00
|
|
|
if (Vfs::$user != $share['share_owner'])
|
2015-01-20 23:11:35 +01:00
|
|
|
{
|
2015-02-28 23:08:23 +01:00
|
|
|
$keep_session = false;
|
2015-01-20 23:11:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!$keep_session) // do NOT change to else, as we might have set $keep_session=false!
|
|
|
|
{
|
|
|
|
// only allow filemanager app
|
|
|
|
$GLOBALS['egw_info']['user']['apps'] = array(
|
|
|
|
'filemanager' => $GLOBALS['egw_info']['apps']['filemanager']
|
|
|
|
);
|
|
|
|
|
|
|
|
$share['share_root'] = '/';
|
2016-03-20 17:19:53 +01:00
|
|
|
Vfs::$user = $share['share_owner'];
|
2018-03-12 20:50:37 +01:00
|
|
|
|
|
|
|
// Need to re-init stream wrapper, as some of them look at
|
|
|
|
// preferences or permissions
|
|
|
|
$scheme = Vfs\StreamWrapper::scheme2class(Vfs::parse_url($share['resolve_url'],PHP_URL_SCHEME));
|
|
|
|
if($scheme && method_exists($scheme, 'init_static'))
|
|
|
|
{
|
|
|
|
$scheme::init_static();
|
|
|
|
}
|
2015-01-20 23:11:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// mounting share
|
2016-03-20 17:19:53 +01:00
|
|
|
Vfs::$is_root = true;
|
|
|
|
if (!Vfs::mount($share['resolve_url'], $share['share_root'], false, false, !$keep_session))
|
2014-11-13 18:31:36 +01:00
|
|
|
{
|
|
|
|
sleep(1);
|
|
|
|
$status = '404 Not Found';
|
|
|
|
header("HTTP/1.1 $status");
|
|
|
|
header("X-WebDAV-Status: $status", true);
|
2014-11-18 13:55:32 +01:00
|
|
|
echo "Requested resource '/".htmlspecialchars($token)."' does NOT exist!\n";
|
2016-03-20 17:30:01 +01:00
|
|
|
exit;
|
2014-11-13 18:31:36 +01:00
|
|
|
}
|
2016-03-20 17:19:53 +01:00
|
|
|
Vfs::$is_root = false;
|
|
|
|
Vfs::clearstatcache();
|
2017-10-31 10:51:59 +01:00
|
|
|
// clear link-cache and load link registry without permission check to access /apps
|
|
|
|
Api\Link::init_static(true);
|
2014-11-13 18:31:36 +01:00
|
|
|
|
2014-11-18 13:55:32 +01:00
|
|
|
// update accessed timestamp
|
|
|
|
self::$db->update(self::TABLE, array(
|
|
|
|
'share_last_accessed' => $share['share_last_accessed']=time(),
|
|
|
|
), array(
|
|
|
|
'share_id' => $share['share_id'],
|
|
|
|
), __LINE__, __FILE__);
|
|
|
|
|
|
|
|
// store sharing object in egw object and therefore in session
|
2016-03-20 17:19:53 +01:00
|
|
|
$GLOBALS['egw']->sharing = new Sharing($share);
|
2014-11-18 13:55:32 +01:00
|
|
|
|
2015-02-28 23:08:23 +01:00
|
|
|
// we have a session we want to keep, but share owner is different from current user and we need filemanager UI, or no session
|
|
|
|
// --> create a new anon session
|
|
|
|
if ($keep_session === false && $GLOBALS['egw']->sharing->use_filemanager() || is_null($keep_session))
|
|
|
|
{
|
|
|
|
// create session without checking auth: create(..., false, false)
|
2015-03-03 22:28:05 +01:00
|
|
|
if (!($sessionid = $GLOBALS['egw']->session->create('anonymous@'.$GLOBALS['egw_info']['user']['domain'],
|
|
|
|
'', 'text', false, false)))
|
2015-02-28 23:08:23 +01:00
|
|
|
{
|
|
|
|
sleep(1);
|
|
|
|
$status = '500 Internal Server Error';
|
|
|
|
header("HTTP/1.1 $status");
|
|
|
|
header("X-WebDAV-Status: $status", true);
|
|
|
|
echo "Failed to create session: ".$GLOBALS['egw']->session->reason."\n";
|
2016-03-20 17:30:01 +01:00
|
|
|
exit;
|
2015-02-28 23:08:23 +01:00
|
|
|
}
|
|
|
|
// only allow filemanager app (gets overwritten by session::create)
|
|
|
|
$GLOBALS['egw_info']['user']['apps'] = array(
|
|
|
|
'filemanager' => $GLOBALS['egw_info']['apps']['filemanager']
|
|
|
|
);
|
2017-11-03 12:43:09 +01:00
|
|
|
// check if sharee has Collabora run rights --> give is to share too
|
|
|
|
$apps = $GLOBALS['egw']->acl->get_user_applications($share['share_owner']);
|
|
|
|
if (!empty($apps['collabora']))
|
|
|
|
{
|
|
|
|
$GLOBALS['egw_info']['user']['apps']['collabora'] = $GLOBALS['egw_info']['apps']['collabora'];
|
|
|
|
}
|
2015-02-28 23:08:23 +01:00
|
|
|
}
|
|
|
|
// we have a session we want to keep, but share owner is different from current user and we dont need filemanager UI
|
|
|
|
// --> we dont need session and close it, to not modifiy it
|
|
|
|
elseif ($keep_session === false)
|
|
|
|
{
|
|
|
|
$GLOBALS['egw']->session->commit_session();
|
|
|
|
}
|
2015-03-02 22:09:08 +01:00
|
|
|
// need to store new fstab and vfs_user in session to allow GET requests / downloads via WebDAV
|
2016-03-20 17:19:53 +01:00
|
|
|
$GLOBALS['egw_info']['user']['vfs_user'] = Vfs::$user;
|
|
|
|
$GLOBALS['egw_info']['server']['vfs_fstab'] = Vfs::mount();
|
2015-02-28 23:08:23 +01:00
|
|
|
|
|
|
|
// update modified egw and egw_info again in session, if neccessary
|
|
|
|
if ($keep_session || $sessionid)
|
2015-01-20 23:11:35 +01:00
|
|
|
{
|
2016-03-20 17:19:53 +01:00
|
|
|
$_SESSION[Api\Session::EGW_INFO_CACHE] = $GLOBALS['egw_info'];
|
|
|
|
unset($_SESSION[Api\Session::EGW_INFO_CACHE]['flags']); // dont save the flags, they change on each request
|
2015-01-20 23:11:35 +01:00
|
|
|
|
2016-03-20 17:19:53 +01:00
|
|
|
$_SESSION[Api\Session::EGW_OBJECT_CACHE] = serialize($GLOBALS['egw']);
|
2015-01-20 23:11:35 +01:00
|
|
|
}
|
|
|
|
|
2014-11-18 13:55:32 +01:00
|
|
|
return $sessionid;
|
|
|
|
}
|
|
|
|
|
2015-02-28 23:08:23 +01:00
|
|
|
/**
|
|
|
|
* Check if we use filemanager UI
|
|
|
|
*
|
|
|
|
* Only for directories, if browser supports it and filemanager is installed
|
|
|
|
*
|
|
|
|
* @return boolean
|
|
|
|
*/
|
|
|
|
public function use_filemanager()
|
|
|
|
{
|
2016-03-20 17:19:53 +01:00
|
|
|
return !(!Vfs::is_dir($this->share['share_root']) || $_SERVER['REQUEST_METHOD'] != 'GET' ||
|
2015-02-28 23:08:23 +01:00
|
|
|
// or unsupported browsers like ie < 10
|
2016-03-20 17:19:53 +01:00
|
|
|
Api\Header\UserAgent::type() == 'msie' && Api\Header\UserAgent::version() < 10.0 ||
|
2015-02-28 23:08:23 +01:00
|
|
|
// or if no filemanager installed (WebDAV has own autoindex)
|
2016-05-30 12:39:23 +02:00
|
|
|
!file_exists(__DIR__.'/../../../filemanager/inc/class.filemanager_ui.inc.php'));
|
2015-02-28 23:08:23 +01:00
|
|
|
}
|
|
|
|
|
2014-11-18 13:55:32 +01:00
|
|
|
/**
|
|
|
|
* Server a request on a share specified in REQUEST_URI
|
|
|
|
*/
|
|
|
|
public function ServeRequest()
|
|
|
|
{
|
2015-01-20 23:11:35 +01:00
|
|
|
// sharing is for a different share, change to current share
|
|
|
|
if ($this->share['share_token'] !== self::get_token())
|
|
|
|
{
|
|
|
|
self::create_session($GLOBALS['egw']->session->session_flags === 'N');
|
|
|
|
|
|
|
|
return $GLOBALS['egw']->sharing->ServeRequest();
|
|
|
|
}
|
2017-11-03 17:28:14 +01:00
|
|
|
|
|
|
|
// No extended ACL for readonly shares, disable eacl by setting session cache
|
2018-03-22 13:04:28 +01:00
|
|
|
if(!($this->share['share_writable'] & 1))
|
2017-11-03 17:28:14 +01:00
|
|
|
{
|
|
|
|
Api\Cache::setSession(Api\Vfs\Sqlfs\StreamWrapper::EACL_APPNAME, 'extended_acl', array(
|
|
|
|
'/' => 1,
|
|
|
|
$this->share['share_path'] => 1
|
|
|
|
));
|
|
|
|
}
|
2014-11-18 13:55:32 +01:00
|
|
|
// use pure WebDAV for everything but GET requests to directories
|
2018-03-21 12:43:25 +01:00
|
|
|
if (!$this->use_filemanager())
|
2014-11-18 13:55:32 +01:00
|
|
|
{
|
2014-12-14 13:33:42 +01:00
|
|
|
// send a content-disposition header, so browser knows how to name downloaded file
|
2016-03-20 17:19:53 +01:00
|
|
|
if (!Vfs::is_dir($this->share['share_root']))
|
2014-12-14 13:33:42 +01:00
|
|
|
{
|
2016-03-20 17:19:53 +01:00
|
|
|
Api\Header\Content::disposition(Vfs::basename($this->share['share_path']), false);
|
2014-12-14 13:33:42 +01:00
|
|
|
}
|
2014-11-18 13:55:32 +01:00
|
|
|
//$GLOBALS['egw']->session->commit_session();
|
2016-03-20 17:19:53 +01:00
|
|
|
$webdav_server = new Vfs\WebDAV();
|
|
|
|
$webdav_server->ServeRequest(Vfs::concat($this->share['share_root'], $this->share['share_token']));
|
2014-11-18 13:55:32 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
// run full eTemplate2 UI for directories
|
2015-01-20 23:11:35 +01:00
|
|
|
$_GET['path'] = $this->share['share_root'];
|
2015-01-21 20:45:46 +01:00
|
|
|
$GLOBALS['egw_info']['user']['preferences']['filemanager']['nm_view'] = 'tile';
|
2014-12-09 13:08:00 +01:00
|
|
|
$_GET['cd'] = 'no';
|
|
|
|
$GLOBALS['egw_info']['flags']['js_link_registry'] = true;
|
2016-04-07 22:42:06 +02:00
|
|
|
Api\Framework::includeCSS('filemanager', 'sharing');
|
2016-03-20 17:19:53 +01:00
|
|
|
$ui = new SharingUi();
|
2014-11-18 13:55:32 +01:00
|
|
|
$ui->index();
|
2014-11-13 18:31:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate a new token
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public static function token()
|
|
|
|
{
|
2016-03-20 17:19:53 +01:00
|
|
|
// generate random token (using oppenssl if available otherwise mt_rand based Api\Auth::randomstring)
|
2014-12-05 12:27:22 +01:00
|
|
|
do {
|
|
|
|
$token = function_exists('openssl_random_pseudo_bytes') ?
|
|
|
|
base64_encode(openssl_random_pseudo_bytes(3*self::TOKEN_LENGTH/4)) :
|
2016-03-20 17:19:53 +01:00
|
|
|
Api\Auth::randomstring(self::TOKEN_LENGTH);
|
2014-12-05 12:27:22 +01:00
|
|
|
// base64 can contain chars not allowed in our vfs-urls eg. / or #
|
2017-08-30 18:09:54 +02:00
|
|
|
} while ($token != urlencode($token));
|
2014-11-13 18:31:36 +01:00
|
|
|
|
|
|
|
return $token;
|
|
|
|
}
|
2014-12-01 21:14:18 +01:00
|
|
|
|
2018-03-22 13:04:28 +01:00
|
|
|
/**
|
|
|
|
* Name of the async job for cleaning up shares
|
|
|
|
*/
|
|
|
|
const ASYNC_JOB_ID = 'egw_sharing-tmp_cleanup';
|
|
|
|
|
2014-12-01 21:14:18 +01:00
|
|
|
/**
|
|
|
|
* Create a new share
|
|
|
|
*
|
|
|
|
* @param string $path either path in temp_dir or vfs with optional vfs scheme
|
2014-12-03 17:25:10 +01:00
|
|
|
* @param string $mode self::LINK: copy file in users tmp-dir or self::READABLE share given vfs file,
|
|
|
|
* if no vfs behave as self::LINK
|
|
|
|
* @param string $name filename to use for $mode==self::LINK, default basename of $path
|
2014-12-01 21:14:18 +01:00
|
|
|
* @param string|array $recipients one or more recipient email addresses
|
|
|
|
* @param array $extra =array() extra data to store
|
2016-05-02 18:57:50 +02:00
|
|
|
* @throw Api\Exception\NotFound if $path not found
|
|
|
|
* @throw Api\Exception\AssertionFailed if user temp. directory does not exist and can not be created
|
2014-12-01 21:14:18 +01:00
|
|
|
* @return array with share data, eg. value for key 'share_token'
|
|
|
|
*/
|
|
|
|
public static function create($path, $mode, $name, $recipients, $extra=array())
|
|
|
|
{
|
|
|
|
if (!isset(self::$db)) self::$db = $GLOBALS['egw']->db;
|
|
|
|
|
|
|
|
if (empty($name)) $name = $path;
|
|
|
|
|
2016-03-20 17:19:53 +01:00
|
|
|
$path2tmp =& Api\Cache::getSession(__CLASS__, 'path2tmp');
|
2014-12-01 21:14:18 +01:00
|
|
|
|
|
|
|
// allow filesystem path only for temp_dir
|
|
|
|
$temp_dir = $GLOBALS['egw_info']['server']['temp_dir'].'/';
|
|
|
|
if (substr($path, 0, strlen($temp_dir)) == $temp_dir)
|
|
|
|
{
|
2014-12-03 17:25:10 +01:00
|
|
|
$mode = self::LINK;
|
2014-12-08 13:51:33 +01:00
|
|
|
$exists = file_exists($path) && is_readable($path);
|
2014-12-01 21:14:18 +01:00
|
|
|
}
|
2014-12-08 13:51:33 +01:00
|
|
|
else
|
2014-12-01 21:14:18 +01:00
|
|
|
{
|
2018-03-21 12:43:25 +01:00
|
|
|
if(parse_url($path, PHP_URL_SCHEME) !== 'vfs')
|
|
|
|
{
|
|
|
|
$path = 'vfs://default'.($path[0] == '/' ? '' : '/').$path;
|
|
|
|
}
|
|
|
|
|
2018-04-09 19:33:42 +02:00
|
|
|
// We don't allow sharing links, share target instead
|
|
|
|
if($target = Vfs::readlink($path))
|
|
|
|
{
|
|
|
|
$path = $target;
|
|
|
|
}
|
|
|
|
|
2018-03-21 12:43:25 +01:00
|
|
|
if (($exists = ($stat = Vfs::stat($path)) && Vfs::check_access($path, Vfs::READABLE, $stat)))
|
|
|
|
{
|
2018-03-22 18:09:09 +01:00
|
|
|
$vfs_path = $path;
|
2018-03-21 12:43:25 +01:00
|
|
|
}
|
2014-12-01 21:14:18 +01:00
|
|
|
}
|
|
|
|
// check if file exists and is readable
|
2014-12-08 13:51:33 +01:00
|
|
|
if (!$exists)
|
2014-12-01 21:14:18 +01:00
|
|
|
{
|
2016-03-20 17:19:53 +01:00
|
|
|
throw new Api\Exception\NotFound("'$path' NOT found!");
|
2014-12-01 21:14:18 +01:00
|
|
|
}
|
2015-02-11 22:51:59 +01:00
|
|
|
// check if file has been shared before, with identical attributes
|
2014-12-03 17:25:10 +01:00
|
|
|
if (($mode != self::LINK || isset($path2tmp[$path])) &&
|
2015-02-11 22:51:59 +01:00
|
|
|
($share = self::$db->select(self::TABLE, '*', $extra+array(
|
2014-12-08 13:51:33 +01:00
|
|
|
'share_path' => $mode == 'link' ? $path2tmp[$path] : $vfs_path,
|
2017-11-03 12:43:09 +01:00
|
|
|
'share_owner' => Vfs::$user,
|
2015-02-11 22:51:59 +01:00
|
|
|
'share_expires' => null,
|
|
|
|
'share_passwd' => null,
|
|
|
|
'share_writable'=> false,
|
|
|
|
), __LINE__, __FILE__)->fetch()))
|
2014-12-01 21:14:18 +01:00
|
|
|
{
|
|
|
|
// if yes, just add additional recipients
|
2014-12-03 17:25:10 +01:00
|
|
|
$share['share_with'] = $share['share_with'] ? explode(',', $share['share_with']) : array();
|
2014-12-01 21:14:18 +01:00
|
|
|
$need_save = false;
|
|
|
|
foreach((array)$recipients as $recipient)
|
|
|
|
{
|
2014-12-03 17:25:10 +01:00
|
|
|
if (!in_array($recipient, $share['share_with']))
|
2014-12-01 21:14:18 +01:00
|
|
|
{
|
2014-12-03 17:25:10 +01:00
|
|
|
$share['share_with'][] = $recipient;
|
2014-12-01 21:14:18 +01:00
|
|
|
$need_save = true;
|
|
|
|
}
|
|
|
|
}
|
2014-12-03 17:25:10 +01:00
|
|
|
$share['share_with'] = implode(',', $share['share_with']);
|
2014-12-01 21:14:18 +01:00
|
|
|
if ($need_save)
|
|
|
|
{
|
|
|
|
self::$db->update(self::TABLE, array(
|
2014-12-03 17:25:10 +01:00
|
|
|
'share_with' => $share['share_with'],
|
2014-12-01 21:14:18 +01:00
|
|
|
), array(
|
|
|
|
'share_id' => $share['share_id'],
|
|
|
|
), __LINE__, __FILE__);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// if not create new share
|
|
|
|
if ($mode == 'link')
|
|
|
|
{
|
|
|
|
$user_tmp = '/home/'.$GLOBALS['egw_info']['user']['account_lid'].'/.tmp';
|
2016-03-29 13:01:34 +02:00
|
|
|
if (!Vfs::file_exists($user_tmp) && !Vfs::mkdir($user_tmp, null, STREAM_MKDIR_RECURSIVE))
|
2014-12-01 21:14:18 +01:00
|
|
|
{
|
2016-03-20 17:19:53 +01:00
|
|
|
throw new Api\Exception\AssertionFailed("Could NOT create temp. directory '$user_tmp'!");
|
2014-12-01 21:14:18 +01:00
|
|
|
}
|
|
|
|
$n = 0;
|
|
|
|
do {
|
2016-03-20 17:19:53 +01:00
|
|
|
$tmp_file = Vfs::concat($user_tmp, ($n?$n.'.':'').Vfs::basename($name));
|
2014-12-03 17:25:10 +01:00
|
|
|
}
|
2016-03-29 13:01:34 +02:00
|
|
|
while(!(is_dir($path) && Vfs::mkdir($tmp_file, null, STREAM_MKDIR_RECURSIVE) ||
|
2016-03-20 17:19:53 +01:00
|
|
|
!is_dir($path) && (!Vfs::file_exists($tmp_file) && ($fp = Vfs::fopen($tmp_file, 'x')) ||
|
2014-12-05 12:27:22 +01:00
|
|
|
// do not copy identical files again to users tmp dir, just re-use them
|
2016-03-20 17:19:53 +01:00
|
|
|
Vfs::file_exists($tmp_file) && Vfs::compare(Vfs::PREFIX.$tmp_file, $path))) && $n++ < 100);
|
2014-12-01 21:14:18 +01:00
|
|
|
|
|
|
|
if ($n >= 100)
|
|
|
|
{
|
2016-03-20 17:19:53 +01:00
|
|
|
throw new Api\Exception\AssertionFailed("Could NOT create temp. file '$tmp_file'!");
|
2014-12-01 21:14:18 +01:00
|
|
|
}
|
2014-12-03 17:25:10 +01:00
|
|
|
if ($fp) fclose($fp);
|
2014-12-01 21:14:18 +01:00
|
|
|
|
2016-03-20 17:19:53 +01:00
|
|
|
if (is_dir($path) && !Vfs::copy_files(array($path), $tmp_file) ||
|
|
|
|
!is_dir($path) && !copy($path, Vfs::PREFIX.$tmp_file))
|
2014-12-01 21:14:18 +01:00
|
|
|
{
|
2016-03-20 17:19:53 +01:00
|
|
|
throw new Api\Exception\AssertionFailed("Could NOT create temp. file '$tmp_file'!");
|
2014-12-01 21:14:18 +01:00
|
|
|
}
|
|
|
|
// store temp. path in session, to be able to add more recipients
|
|
|
|
$path2tmp[$path] = $tmp_file;
|
|
|
|
|
2018-01-17 16:32:21 +01:00
|
|
|
$vfs_path = $tmp_file;
|
2014-12-01 21:14:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$i = 0;
|
|
|
|
while(true) // self::token() can return an existing value
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
self::$db->insert(self::TABLE, $share = array(
|
|
|
|
'share_token' => self::token(),
|
2017-11-03 12:43:09 +01:00
|
|
|
'share_path' => $vfs_path,
|
|
|
|
'share_owner' => Vfs::$user,
|
2014-12-01 21:14:18 +01:00
|
|
|
'share_with' => implode(',', (array)$recipients),
|
|
|
|
'share_created' => time(),
|
|
|
|
)+$extra, false, __LINE__, __FILE__);
|
|
|
|
|
|
|
|
$share['share_id'] = self::$db->get_last_insert_id(self::TABLE, 'share_id');
|
|
|
|
break;
|
|
|
|
}
|
2016-03-20 17:19:53 +01:00
|
|
|
catch(Api\Db\Exception $e) {
|
2014-12-01 21:14:18 +01:00
|
|
|
if ($i++ > 3) throw $e;
|
|
|
|
unset($e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-03-22 13:04:28 +01:00
|
|
|
|
|
|
|
// if not already installed, install periodic cleanup of shares
|
|
|
|
$async = new Api\Asyncservice();
|
|
|
|
if (!($job = $async->read(self::ASYNC_JOB_ID)) || $job[self::ASYNC_JOB_ID]['method'] === 'egw_sharing::tmp_cleanup')
|
|
|
|
{
|
|
|
|
if ($job) $async->delete(self::ASYNC_JOB_ID); // update not working old class-name
|
|
|
|
|
|
|
|
$async->set_timer(array('day' => 28), self::ASYNC_JOB_ID, 'EGroupware\\Api\\Vfs\\Sharing::tmp_cleanup',null);
|
|
|
|
}
|
|
|
|
|
2014-12-01 21:14:18 +01:00
|
|
|
return $share;
|
|
|
|
}
|
|
|
|
|
2014-12-09 13:46:38 +01:00
|
|
|
/**
|
2016-03-20 17:19:53 +01:00
|
|
|
* Api\Storage\Base instance for egw_sharing table
|
2014-12-09 13:46:38 +01:00
|
|
|
*
|
2016-03-20 17:19:53 +01:00
|
|
|
* @var Api\Storage\Base
|
2014-12-09 13:46:38 +01:00
|
|
|
*/
|
|
|
|
protected static $so;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a so_sql instance initialised for shares
|
|
|
|
*/
|
|
|
|
public static function so()
|
|
|
|
{
|
|
|
|
if (!isset(self::$so))
|
|
|
|
{
|
2016-03-20 17:19:53 +01:00
|
|
|
self::$so = new Api\Storage\Base('phpgwapi', self::TABLE, null, '', true);
|
2014-12-09 13:46:38 +01:00
|
|
|
self::$so->set_times('string');
|
|
|
|
}
|
|
|
|
return self::$so;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete specified shares and unlink temp. files
|
|
|
|
*
|
|
|
|
* @param int|array $keys
|
|
|
|
* @return int number of deleted shares
|
|
|
|
*/
|
|
|
|
public static function delete($keys)
|
|
|
|
{
|
|
|
|
self::$db = $GLOBALS['egw']->db;
|
|
|
|
|
|
|
|
if (is_scalar($keys)) $keys = array('share_id' => $keys);
|
|
|
|
|
|
|
|
// get all temp. files, to be able to delete them
|
|
|
|
$tmp_paths = array();
|
|
|
|
foreach(self::$db->select(self::TABLE, 'share_path', array(
|
|
|
|
"share_path LIKE '/home/%/.tmp/%'")+$keys, __LINE__, __FILE__, false) as $row)
|
|
|
|
{
|
|
|
|
$tmp_paths[] = $row['share_path'];
|
|
|
|
}
|
|
|
|
|
|
|
|
// delete specified shares
|
|
|
|
self::$db->delete(self::TABLE, $keys, __LINE__, __FILE__);
|
|
|
|
$deleted = self::$db->affected_rows();
|
|
|
|
|
|
|
|
// check if temp. files are used elsewhere
|
|
|
|
if ($tmp_paths)
|
|
|
|
{
|
|
|
|
foreach(self::$db->select(self::TABLE, 'share_path,COUNT(*) AS cnt', array(
|
|
|
|
'share_path' => $tmp_paths,
|
|
|
|
), __LINE__, __FILE__, false, 'GROUP BY share_path') as $row)
|
|
|
|
{
|
|
|
|
if (($key = array_search($row['share_path'], $tmp_paths)))
|
|
|
|
{
|
|
|
|
unset($tmp_paths[$key]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// if not delete them
|
|
|
|
foreach($tmp_paths as $path)
|
|
|
|
{
|
2016-03-20 17:19:53 +01:00
|
|
|
Vfs::remove($path);
|
2014-12-09 13:46:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return $deleted;
|
|
|
|
}
|
|
|
|
|
2014-12-05 12:27:22 +01:00
|
|
|
/**
|
2014-12-05 21:18:51 +01:00
|
|
|
* Home long to keep temp. files: 100 day
|
|
|
|
*/
|
|
|
|
const TMP_KEEP = 8640000;
|
2018-03-22 13:04:28 +01:00
|
|
|
/**
|
|
|
|
* How long to keep automatic created Wopi shares
|
|
|
|
*/
|
|
|
|
const WOPI_KEEP = '-3month';
|
2014-12-05 21:18:51 +01:00
|
|
|
|
|
|
|
/**.
|
|
|
|
* Periodic (monthly) cleanup of temporary sharing files (download link)
|
|
|
|
*
|
|
|
|
* Exlicit expireds shares are delete, as ones created over 100 days ago and last accessed over 100 days ago.
|
2014-12-05 12:27:22 +01:00
|
|
|
*/
|
|
|
|
public static function tmp_cleanup()
|
|
|
|
{
|
2014-12-05 21:18:51 +01:00
|
|
|
if (!isset(self::$db)) self::$db = $GLOBALS['egw']->db;
|
2016-03-20 17:19:53 +01:00
|
|
|
Vfs::$is_root = true;
|
2014-12-05 21:18:51 +01:00
|
|
|
|
2014-12-05 12:27:22 +01:00
|
|
|
try {
|
2014-12-05 21:18:51 +01:00
|
|
|
$cols = array(
|
|
|
|
'share_path',
|
|
|
|
'MAX(share_expires) AS share_expires',
|
|
|
|
'MAX(share_created) AS share_created',
|
|
|
|
'MAX(share_last_accessed) AS share_last_accessed',
|
|
|
|
);
|
|
|
|
if (($group_concat = self::$db->group_concat('share_id'))) $cols[] = $group_concat.' AS share_id';
|
|
|
|
// remove expired tmp-files unconditionally
|
2017-01-23 15:22:03 +01:00
|
|
|
$having = 'HAVING MAX(share_expires) < '.self::$db->quote(self::$db->to_timestamp(time())).' OR '.
|
2014-12-05 21:18:51 +01:00
|
|
|
// remove without expiration date, when created over 100 days ago AND
|
2017-01-23 15:22:03 +01:00
|
|
|
'MAX(share_expires) IS NULL AND MAX(share_created) < '.self::$db->quote(self::$db->to_timestamp(time()-self::TMP_KEEP)). ' AND '.
|
2014-12-05 21:18:51 +01:00
|
|
|
// (last accessed over 100 days ago OR never)
|
2017-01-23 15:22:03 +01:00
|
|
|
'(MAX(share_last_accessed) IS NULL OR MAX(share_last_accessed) < '.self::$db->quote(self::$db->to_timestamp(time()-self::TMP_KEEP)).')';
|
2014-12-05 21:18:51 +01:00
|
|
|
|
|
|
|
foreach(self::$db->select(self::TABLE, $cols, array(
|
|
|
|
"share_path LIKE '/home/%/.tmp/%'",
|
|
|
|
), __LINE__, __FILE__, false, 'GROUP BY share_path '.$having) as $row)
|
2014-12-05 12:27:22 +01:00
|
|
|
{
|
2016-03-20 17:19:53 +01:00
|
|
|
Vfs::remove($row['share_path']);
|
2014-12-05 21:18:51 +01:00
|
|
|
|
|
|
|
if ($group_concat)
|
|
|
|
{
|
|
|
|
$share_ids = $row['share_id'] ? explode(',', $row['share_id']) : array();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$share_ids = array();
|
|
|
|
foreach(self::$db->selec(self::TABLE, 'share_id', array(
|
|
|
|
'share_path' => $row['share_path'],
|
|
|
|
), __LINE__, __FILE__) as $id)
|
|
|
|
{
|
|
|
|
$share_ids[] = $id['share_id'];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ($share_ids)
|
|
|
|
{
|
|
|
|
self::$db->delete(self::TABLE, array('share_id' => $share_ids), __LINE__, __FILE__);
|
|
|
|
}
|
|
|
|
}
|
2018-03-22 13:04:28 +01:00
|
|
|
|
|
|
|
// delete automatic created and expired Collabora shares older then 3 month
|
|
|
|
if (class_exists('EGroupware\\Collabora\\Wopi'))
|
|
|
|
{
|
|
|
|
self::$db->delete(self::TABLE, array(
|
|
|
|
'share_expires < '.self::$db->quote(Api\DateTime::to(self::WOPI_KEEP, 'Y-m-d')),
|
|
|
|
'share_writable IN ('.Wopi::WOPI_WRITABLE.','.Wopi::WOPI_READONLY.')',
|
|
|
|
), __LINE__, __FILE__);
|
|
|
|
}
|
2014-12-05 12:27:22 +01:00
|
|
|
}
|
2016-03-20 17:19:53 +01:00
|
|
|
catch (\Exception $e) {
|
2017-01-23 15:22:03 +01:00
|
|
|
_egw_log_exception($e);
|
2014-12-05 12:27:22 +01:00
|
|
|
}
|
2016-03-20 17:19:53 +01:00
|
|
|
Vfs::$is_root = false;
|
2014-12-05 12:27:22 +01:00
|
|
|
}
|
|
|
|
|
2014-12-01 21:14:18 +01:00
|
|
|
/**
|
|
|
|
* Generate link from share or share-token
|
|
|
|
*
|
|
|
|
* @param string|array $share share or share-token
|
2018-03-21 12:40:59 +01:00
|
|
|
* @return string full Url incl. schema and host
|
2014-12-01 21:14:18 +01:00
|
|
|
*/
|
|
|
|
public static function share2link($share)
|
|
|
|
{
|
|
|
|
if (is_array($share)) $share = $share['share_token'];
|
|
|
|
|
2018-03-21 12:40:59 +01:00
|
|
|
return Api\Framework::getUrl(Api\Framework::link('/share.php')).'/'.$share;
|
2014-12-01 21:14:18 +01:00
|
|
|
}
|
2014-11-18 13:55:32 +01:00
|
|
|
}
|
|
|
|
|
2016-05-30 16:00:20 +02:00
|
|
|
if (file_exists(__DIR__.'/../../../filemanager/inc/class.filemanager_ui.inc.php'))
|
2014-11-18 13:55:32 +01:00
|
|
|
{
|
2016-05-30 16:00:20 +02:00
|
|
|
require_once __DIR__.'/../../../filemanager/inc/class.filemanager_ui.inc.php';
|
2014-11-18 13:55:32 +01:00
|
|
|
|
2016-03-20 17:19:53 +01:00
|
|
|
class SharingUi extends filemanager_ui
|
2014-11-18 13:55:32 +01:00
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Get the configured start directory for the current user
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
static function get_home_dir()
|
|
|
|
{
|
2015-01-20 23:11:35 +01:00
|
|
|
return $GLOBALS['egw']->sharing->get_root();
|
2014-11-18 13:55:32 +01:00
|
|
|
}
|
2017-11-02 16:43:08 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Context menu
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public static function get_actions()
|
|
|
|
{
|
|
|
|
$actions = parent::get_actions();
|
|
|
|
$group = 1;
|
|
|
|
if(Vfs::is_writable($GLOBALS['egw']->sharing->get_root()))
|
|
|
|
{
|
|
|
|
return $actions;
|
|
|
|
}
|
|
|
|
$actions+= array(
|
|
|
|
'egw_copy' => array(
|
|
|
|
'enabled' => false,
|
|
|
|
'group' => $group + 0.5,
|
|
|
|
'hideOnDisabled' => true
|
|
|
|
),
|
|
|
|
'egw_copy_add' => array(
|
|
|
|
'enabled' => false,
|
|
|
|
'group' => $group + 0.5,
|
|
|
|
'hideOnDisabled' => true
|
|
|
|
),
|
|
|
|
'paste' => array(
|
|
|
|
'enabled' => false,
|
|
|
|
'group' => $group + 0.5,
|
|
|
|
'hideOnDisabled' => true
|
|
|
|
),
|
|
|
|
);
|
|
|
|
return $actions;
|
|
|
|
}
|
2014-11-18 13:55:32 +01:00
|
|
|
}
|
2014-11-13 18:31:36 +01:00
|
|
|
}
|