array( 'label' => 'Hidden upload', 'title' => 'Share as readonly, but allow uploads. Uploads are hidden, and only accessable by those with an account', ) ); /** * Create sharing session * * 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 * @return string with sessionid, does NOT return if no session created */ public static function setup_share($keep_session, &$share) { // Get these before parent is mounted to make sure we get the right path unset($GLOBALS['egw_info']['server']['vfs_fstab']); // triggers reset of fstab in mount() $GLOBALS['egw_info']['server']['vfs_fstab'] = Vfs::mount(); Vfs::clearstatcache(); $resolve_url = Vfs::resolve_url($share['share_path'], true, true, true, true); // Parent mounts the root read-only parent::setup_share(true, $share); // upload dir needs the full url incl. share owner, but not readonly $upload_dir = Vfs::concat(str_replace('?ro=1', '',Vfs::mount()[$share['share_root']] ?: $resolve_url), self::HIDDEN_UPLOAD_DIR); $upload_mount = Vfs::concat($share['share_root'], self::HIDDEN_UPLOAD_DIR); // Mounting upload dir, has original share owner access (write) Vfs::$is_root = true; if (!Vfs::mount($upload_dir, $upload_mount, false, false, false)) { sleep(1); return static::share_fail( '404 Not Found', "Requested resource '/" . htmlspecialchars($share['share_token']) . "' does NOT exist!\n" ); } static::session_mount($upload_mount, $upload_dir); Vfs::$is_root = false; Vfs::clearstatcache(); } /** * Create a new share * * @param string $action_id Name of the action used to create the share. Allows for customization. * @param string $path either path in temp_dir or vfs with optional vfs scheme * @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 * @param string|array $recipients one or more recipient email addresses * @param array $extra =array() extra data to store * @return array with share data, eg. value for key 'share_token' * @throw Api\Exception\NotFound if $path not found * @throw Api\Exception\AssertionFailed if user temp. directory does not exist and can not be created */ public static function create(string $action_id, $path, $mode, $name, $recipients, $extra = array()) { if (!isset(self::$db)) { self::$db = $GLOBALS['egw']->db; } $path = parent::validate_path($path, $mode); // Set up anonymous upload directory static::create_hidden_upload($path, $extra); return parent::create($action_id, $path, $mode, $name, $recipients, $extra); } /** * Check the given path for an anonymous upload directory, and create it if it does not * exist yet. Anon upload directory is not visible over the share, and any files uploaded * to the share are placed inside it instead. * * @param string $path Target path in the VFS * @param string[] $extra Extra settings * * @throws Api\Exception\AssertionFailed * @throws Api\Exception\NoPermission * @throws Api\Exception\WrongParameter */ protected static function create_hidden_upload(string $path, &$extra) { $upload_dir = Vfs::concat($path, self::HIDDEN_UPLOAD_DIR); if (($stat = stat($upload_dir)) && !Vfs::check_access($upload_dir, Vfs::WRITABLE, $stat)) { throw new Api\Exception\NoPermission("Upload directory exists, but you have no write permission"); } if (!($stat = stat($upload_dir))) { // Directory is not there, create it if (!mkdir($upload_dir)) { throw new Api\Exception\NoPermission("Could not make upload directory"); } } // Set flags so things work $extra['share_writable'] = self::HIDDEN_UPLOAD; } /** * Get actions for sharing an entry from filemanager * * @param string $appname * @param int $group Current menu group * * @return array Actions */ public static function get_actions($appname, $group = 6) { $actions = parent::get_actions('filemanager', $group); // In filemanager, only collabora sharing is EPL Only unset($actions['share']['children'][0]); foreach($actions['share']['children'] as $action_id => &$action) { // Take care not to re-enble shareDocuments if it has no documents $action['enabled'] = $action_id == 'shareDocuments' && $action['enabled'] || $action_id != 'shareDocuments'; } // Add in a hidden upload directory $actions['share']['children']['shareUploadDir'] = array( 'caption' => lang('Hidden uploads'), 'group' => 2, 'order' => 30, 'enabled' => 'javaScript:app.filemanager.hidden_upload_enabled', 'onExecute' => 'javaScript:app.filemanager.share_link', 'data' => ['share_writable' => self::HIDDEN_UPLOAD], 'icon' => 'upload', 'hideOnDisabled' => true ); return $actions; } /** * Server a request on a share specified in REQUEST_URI */ public function get_ui() { // run full eTemplate2 UI for directories $_GET['path'] = $this->share['share_root']; $GLOBALS['egw_info']['user']['preferences']['filemanager']['nm_view'] = 'tile'; $_GET['cd'] = 'no'; $GLOBALS['egw_info']['flags']['js_link_registry'] = true; $GLOBALS['egw_info']['flags']['currentapp'] = 'filemanager'; Api\Framework::includeCSS('filemanager', 'sharing'); $ui = new HiddenUpload(); $ui->index(); } /** * Does this share have a hidden upload directory */ public function has_hidden_upload() { return (int)$this->share['share_writable'] == self::HIDDEN_UPLOAD; } public function redo() { static::setup_share(true, $this->share); } }