Projects
Kolab:Winterfell
chwala
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 27
View file
chwala.spec
Changed
@@ -37,7 +37,7 @@ %global _ap_sysconfdir %{_sysconfdir}/%{httpd_name} Name: chwala -Version: 0.5.3 +Version: 0.5.4 Release: 1%{?dist} Summary: Glorified WebDAV, done right
View file
chwala-0.5.3.tar.gz/config/config.inc.php.dist -> chwala-0.5.4.tar.gz/config/config.inc.php.dist
Changed
@@ -52,6 +52,11 @@ ); */ +// Disables listing folders from the backend storage. +// This is useful when you configured an external source(s) and +// you want to use it exclusively, ignoring Kolab folders. +$config['fileapi_backend_storage_disabled'] = false; + // Manticore service URL. Enables use of WebODF collaborative editor. // Note: this URL should be accessible from Chwala host and Roundcube host as well. $config['fileapi_manticore'] = null; @@ -76,6 +81,26 @@ // possible units: s, m, h, d, w $config['fileapi_cache_ttl'] = '1d'; +// LDAP addressbook that would be searched for user names autocomplete. +// That should be an array refering to the Roundcube's $config['ldap_public'] +// array key or complete addressbook configuration array. +$config['fileapi_users_source'] = 'kolab_addressbook'; + +// The LDAP attribute which will be used as ACL user identifier +$config['fileapi_users_field'] = 'mail'; + +// The LDAP search filter will be combined with search queries +$config['fileapi_users_filter'] = ''; + +// Include groups in searching +$config['fileapi_groups'] = false; + +// Prefix added to the group name to build IMAP ACL identifier +$config['fileapi_group_prefix'] = 'group:'; + +// The LDAP attribute (or field name) which will be used as ACL group identifier +$config['fileapi_group_field'] = 'name'; + // ------------------------------------------------ // SeaFile driver settings // ------------------------------------------------
View file
chwala-0.5.4.tar.gz/lib/api/autocomplete.php
Added
@@ -0,0 +1,57 @@ +<?php +/* + +--------------------------------------------------------------------------+ + | This file is part of the Kolab File API | + | | + | Copyright (C) 2012-2018, Kolab Systems AG | + | | + | This program is free software: you can redistribute it and/or modify | + | it under the terms of the GNU Affero General Public License as published | + | by the Free Software Foundation, either version 3 of the License, or | + | (at your option) any later version. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU Affero General Public License for more details. | + | | + | You should have received a copy of the GNU Affero General Public License | + | along with this program. If not, see <http://www.gnu.org/licenses/> | + +--------------------------------------------------------------------------+ + | Author: Aleksander Machniak <machniak@kolabsys.com> | + +--------------------------------------------------------------------------+ +*/ + +class file_api_autocomplete extends file_api_common +{ + /** + * Request handler + */ + public function handle() + { + parent::handle(); + + if (!isset($this->args['search']) || $this->args['search'] === '') { + throw new Exception("Missing search keyword", file_api_core::ERROR_CODE); + } + + if (isset($this->args['folder']) && $this->args['folder'] !== '') { + list($driver, $path) = $this->api->get_driver($this->args['folder']); + } + else { + $driver = $this->api->get_backend(); + } + + if (!empty($this->args['mode'])) { + $mode = 0; + $mode += stripos($this->args['mode'], 'user') !== false ? file_storage::SEARCH_USER : 0; + $mode += stripos($this->args['mode'], 'group') !== false ? file_storage::SEARCH_GROUP : 0; + } + + if (empty($mode)) { + $mode = file_storage::SEARCH_USER; + } + + return $driver->autocomplete($this->args['search'], $mode); + } +}
View file
chwala-0.5.3.tar.gz/lib/api/folder_list.php -> chwala-0.5.4.tar.gz/lib/api/folder_list.php
Changed
@@ -57,7 +57,7 @@ return $folders; } - $drivers = $this->api->get_drivers(true); + $drivers = $this->api->get_drivers(true, $admin_drivers); $has_more = false; $errors = array(); @@ -114,8 +114,13 @@ } catch (Exception $e) { if ($e->getCode() == file_storage::ERROR_NOAUTH) { - // inform UI about to ask user for credentials - $errors[$title] = $this->parse_metadata($driver->driver_metadata()); + if (!in_array($title, $admin_drivers)) { + // inform UI about to ask user for credentials + $errors[$title] = $this->parse_metadata($driver->driver_metadata()); + } + else { + $errors[$title] = array('error' => file_storage::ERROR_NOAUTH); + } } } }
View file
chwala-0.5.4.tar.gz/lib/api/sharing.php
Added
@@ -0,0 +1,84 @@ +<?php +/* + +--------------------------------------------------------------------------+ + | This file is part of the Kolab File API | + | | + | Copyright (C) 2012-2018, Kolab Systems AG | + | | + | This program is free software: you can redistribute it and/or modify | + | it under the terms of the GNU Affero General Public License as published | + | by the Free Software Foundation, either version 3 of the License, or | + | (at your option) any later version. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU Affero General Public License for more details. | + | | + | You should have received a copy of the GNU Affero General Public License | + | along with this program. If not, see <http://www.gnu.org/licenses/> | + +--------------------------------------------------------------------------+ + | Author: Aleksander Machniak <machniak@kolabsys.com> | + +--------------------------------------------------------------------------+ +*/ + +class file_api_sharing extends file_api_common +{ + /** + * Request handler + */ + public function handle() + { + parent::handle(); + + if (!isset($this->args['folder']) || $this->args['folder'] === '') { + throw new Exception("Missing folder name", file_api_core::ERROR_CODE); + } + + list($driver, $path) = $this->api->get_driver($this->args['folder']); + + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + // Required arguments: + // - action: 'submit', 'update', 'delete' + // - mode: depends on the storage driver + return $driver->sharing($path, file_storage::SHARING_MODE_UPDATE, $this->args); + } + + $form = $driver->sharing($path, file_storage::SHARING_MODE_FORM); + + if (empty($form)) { + throw new Exception("Sharing not supported", file_api_core::ERROR_UNSUPPORTED); + } + + $rights = $driver->sharing($path, file_storage::SHARING_MODE_RIGHTS); + + $this->localize_form_data($form); + + $result = array( + 'form' => $form, + 'rights' => (array) $rights, + ); + + return $result; + } + + /** + * Recurrent function to localize form data entries + */ + protected function localize_form_data(&$data, $key = null, $self = null) + { + if (!$self) { + $self = $this; + } + + if (in_array($key, array('title', 'placeholder', 'label', 'list_column_label'))) { + $data = $self->api->translate($data); + } + else if ($key == 'options') { + $data = array_map(array($self->api, 'translate'), $data); + } + else if (is_array($data)) { + array_walk($data, array($self, 'localize_form_data'), $self); + } + } +}
View file
chwala-0.5.4.tar.gz/lib/drivers/kolab/kolab_file_autocomplete.php
Added
@@ -0,0 +1,171 @@ +<?php +/* + +--------------------------------------------------------------------------+ + | This file is part of the Kolab File API | + | | + | Copyright (C) 2012-2018, Kolab Systems AG | + | | + | This program is free software: you can redistribute it and/or modify | + | it under the terms of the GNU Affero General Public License as published | + | by the Free Software Foundation, either version 3 of the License, or | + | (at your option) any later version. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU Affero General Public License for more details. | + | | + | You should have received a copy of the GNU Affero General Public License | + | along with this program. If not, see <http://www.gnu.org/licenses/> | + +--------------------------------------------------------------------------+ + | Author: Aleksander Machniak <machniak@kolabsys.com> | + +--------------------------------------------------------------------------+ +*/ + +class kolab_file_autocomplete +{ + private $rc; + private $ldap; + + /** + * Class constructor + */ + public function __construct() + { + $this->rc = rcube::get_instance(); + } + + /** + * Search users/groups + */ + public function search($search, $with_groups = false) + { + if (!$this->init_ldap()) { + return false; + } + + $max = (int) $this->rc->config->get('autocomplete_max', 15); + $mode = (int) $this->rc->config->get('addressbook_search_mode'); + $me = $this->rc->get_user_name(); + + $this->ldap->set_pagesize($max); + + $result = $this->ldap->search('*', $search, $mode); + $users = array(); + $index = array(); + + foreach ($result->records as $record) { + $user = $record['uid']; + + if (is_array($user)) { + $user = array_filter($user); + $user = $user[0]; + } + + if (in_array($me, rcube_addressbook::get_col_values('email', $record, true))) { + continue; + } + + if ($user) { + $display = rcube_addressbook::compose_search_name($record); + $user = array('name' => $user, 'display' => $display); + $users[] = $user; + $index[] = $display ?: $user['name']; + } + } + + $group_support = $this->rc->config->get('fileapi_groups'); + $group_prefix = $this->rc->config->get('fileapi_group_prefix'); + $group_field = $this->rc->config->get('fileapi_group_field', 'name'); + + if ($with_groups && $group_support && $group_field) { + $result = $this->ldap->list_groups($search, $mode); + + foreach ($result as $record) { + $group = $record['name']; + $group_id = is_array($record[$group_field]) ? $record[$group_field][0] : $record[$group_field]; + + if ($group) { + $users[] = array('name' => ($group_prefix ? $group_prefix : '') . $group_id, 'display' => $group, 'type' => 'group'); + $index[] = $group; + } + } + } + + if (count($users)) { + array_multisort($index, SORT_ASC, SORT_LOCALE_STRING, $users); + } + + if (count($users) > $max) { + $users = array_slice($users, 0, $max); + } + + return $users; + } + + /** + * Initializes autocomplete LDAP backend + */ + private function init_ldap() + { + if ($this->ldap) { + return $this->ldap->ready; + } + + // get LDAP config + $config = $this->rc->config->get('fileapi_users_source'); + + if (empty($config)) { + return false; + } + + // not an array, use configured ldap_public source + if (!is_array($config)) { + $ldap_config = (array) $this->rc->config->get('ldap_public'); + $config = $ldap_config[$config]; + } + + $uid_field = $this->rc->config->get('fileapi_users_field', 'mail'); + $filter = $this->rc->config->get('fileapi_users_filter'); + $debug = $this->rc->config->get('ldap_debug'); + $domain = $this->rc->config->mail_domain($_SESSION['imap_host']); + + if (empty($uid_field) || empty($config)) { + return false; + } + + // get name attribute + if (!empty($config['fieldmap'])) { + $name_field = $config['fieldmap']['name']; + } + // ... no fieldmap, use the old method + if (empty($name_field)) { + $name_field = $config['name_field']; + } + + // add UID field to fieldmap, so it will be returned in a record with name + $config['fieldmap']['name'] = $name_field; + $config['fieldmap']['uid'] = $uid_field; + + // search in UID and name fields + // $name_field can be in a form of <field>:<modifier> (#1490591) + $name_field = preg_replace('/:.*$/', '', $name_field); + $search = array_unique(array($name_field, $uid_field)); + + $config['search_fields'] = $search; + $config['required_fields'] = array($uid_field); + + // set search filter + if ($filter) { + $config['filter'] = $filter; + } + + // disable vlv + $config['vlv'] = false; + + // Initialize LDAP connection + $this->ldap = new rcube_ldap($config, $debug, $domain); + + return $this->ldap->ready; + } +}
View file
chwala-0.5.3.tar.gz/lib/drivers/kolab/kolab_file_storage.php -> chwala-0.5.4.tar.gz/lib/drivers/kolab/kolab_file_storage.php
Changed
@@ -321,6 +321,7 @@ file_storage::CAPS_QUOTA => $quota, file_storage::CAPS_LOCKS => true, file_storage::CAPS_SUBSCRIPTIONS => true, + file_storage::CAPS_ACL => true, ); } @@ -1202,6 +1203,258 @@ } /** + * Sharing interface + * + * @param string $folder_name Name of a folder with full path + * @param int $mode Sharing action mode + * @param array $args POST/GET parameters + * + * @return mixed Sharing response + * @throws Exception + */ + public function sharing($folder, $mode, $args = array()) + { + $folder_name = rcube_charset::convert($folder, RCUBE_CHARSET, 'UTF7-IMAP'); + $storage = $this->rc->get_storage(); + $folder_info = $storage->folder_info($folder_name); + + if (!is_array($folder_info['rights'])) { + throw new Exception("Storage error. Failed to get folder permissions.", file_storage::ERROR); + } + + if (!in_array('a', $folder_info['rights'])) { + throw new Exception("No permissions to administer this folder.", file_storage::ERROR_FORBIDDEN); + } + + if ($mode == file_storage::SHARING_MODE_FORM) { + $form = array( + 'shares' => array( + 'title' => 'share.permissions', + 'form' => array( + 'user' => array( + 'title' => 'share.usergroup', + 'type' => 'input', + 'autocomplete' => 'user,group', + ), + 'right' => array( + 'title' => 'share.permission', + 'type' => 'select', + 'options' => array( + 'r' => 'share.readonly', + 'rw' => 'share.readwrite', + 'a' => 'share.admin', + ), + ), + ), + 'extra_fields' => array( + 'type' => 'user', + 'id' => '', + ), + ), + ); + + return $form; + } + + if ($mode == file_storage::SHARING_MODE_RIGHTS) { + $result = array(); + $acl_list = $storage->get_acl($folder_name); + + foreach ((array) $acl_list as $name => $acl) { + if ($name == $_SESSION['username']) { + continue; + } + + if (in_array('a', $acl)) { + $right = 'a'; + } + else if (in_array('i', $acl)) { + $right = 'rw'; + } + else if (in_array('r', $acl)) { + $right = 'r'; + } + else { + continue; + } + + $type = strpos($name, 'group:') === 0 ? 'group' : 'user'; + $id = $name; + + if ($type == 'group') { + $name = substr($name, 6); + } + + $result[] = array( + 'mode' => 'shares', + 'type' => $type, + 'right' => $right, + 'user' => $name, + 'id' => $id, + ); + } + + return $result; + } + + if ($mode == file_storage::SHARING_MODE_UPDATE) { + if ($args['mode'] == 'shares') { + $user = $args['id']; + if (!$user) { + $user = ($args['type'] == 'group' ? 'group:' : '') . preg_replace('/^group:/', '', $args['user']); + } + + switch ($args['right']) { + case 'r': $acl = 'lrs'; break; + case 'rw': $acl = 'lrswite'; break; + case 'a': $acl = 'lrswiteax'; break; + } + + if (empty($user) || (empty($acl) && $args['action'] != 'delete')) { + throw new Exception("Invalid input.", file_storage::ERROR); + } + + switch ($args['action']) { + case 'submit': + case 'update': + $result = $storage->set_acl($folder_name, $user, $acl); + break; + + case 'delete': + $result = $storage->delete_acl($folder_name, $user); + break; + } + } + else { + throw new Exception("Invalid input.", file_storage::ERROR); + } + + if (empty($result)) { + throw new Exception("Storage error. Failed to update share.", file_storage::ERROR); + } + + return true; + } + } + + /** + * User/group search (autocompletion) + * + * @param string $search Search string + * @param int $mode Search mode + * + * @return array Users/Groups list + * @throws Exception + */ + public function autocomplete($search, $mode) + { + $ac = new kolab_file_autocomplete($this); + + $result = $ac->search($search, $mode & file_storage::SEARCH_GROUP); + + if ($result === false) { + throw new Exception("Failed to search users", file_storage::ERROR); + } + + return $result; + } + + /** + * Convert file/folder path into a global URI. + * + * @param string $path File/folder path + * + * @return string URI + * @throws Exception + */ + public function path2uri($path) + { + $storage = $this->rc->get_storage(); + $namespace = $storage->get_namespace(); + $separator = $storage->get_hierarchy_delimiter(); + $_path = str_replace(file_storage::SEPARATOR, $separator, $path); + $owner = $this->rc->get_user_name(); + + // find the owner and remove namespace prefix + foreach (array_filter($namespace) as $type => $ns) { + foreach ($ns as $root) { + if (is_array($root) && $root[0] && strpos($_path, $root[0]) === 0) { + $path = substr($path, strlen($root[0])); + + switch ($type) { + case 'shared': + // in theory there can be more than one shared root + // we add it to dummy user name, so we can revert conversion + $owner = "shared({$root[0]})"; + break; + + case 'other': + list($user, $path) = explode(file_storage::SEPARATOR, $path, 2); + + if (strpos($user, '@') === false) { + $domain = strstr($owner, '@'); + if (!empty($domain)) { + $user .= $domain; + } + } + + $owner = $user; + break; + } + + break 2; + } + } + } + + return 'imap://' . rawurlencode($owner) . '@' . $storage->options['host'] + . '/' . file_utils::encode_path($path); + } + + /** + * Convert global URI into file/folder path. + * + * @param string $uri URI + * + * @return string File/folder path + * @throws Exception + */ + public function uri2path($uri) + { + if (!preg_match('|^imap://([^@]+)@([^/]+)/(.*)$|', $uri, $matches)) { + throw new Exception("Internal storage error. Unexpected data format.", file_storage::ERROR); + } + + $storage = $this->rc->get_storage(); + $separator = $storage->get_hierarchy_delimiter(); + $owner = $this->rc->get_user_name(); + + $user = rawurldecode($matches[1]); + $path = file_utils::decode_path($matches[3]); + + // personal namespace + if ($user == $owner) { + // do nothing + // Note: that might not work if personal namespace uses e.g. INBOX/ prefix. + } + // shared namespace + else if (preg_match('/^shared\((.*)\)$/', $user, $matches)) { + $path = $matches[1] . $path; + } + // other users namespace + else { + $namespace = $storage->get_namespace('other'); + + list($local, $domain) = explode('@', $user); + + // here we assume there's only one other users namespace root + $path = $namespace[0][0] . $local . file_storage::SEPARATOR . $path; + } + + return $path; + } + + /** * Get files from a folder (with performance fix) */ protected function get_files($folder, $filter, $all = true) @@ -1348,101 +1601,6 @@ } /** - * Convert file/folder path into a global URI. - * - * @param string $path File/folder path - * - * @return string URI - * @throws Exception - */ - public function path2uri($path) - { - $storage = $this->rc->get_storage(); - $namespace = $storage->get_namespace(); - $separator = $storage->get_hierarchy_delimiter(); - $path = str_replace(file_storage::SEPARATOR, $separator, $path); - $owner = $this->rc->get_user_name(); - - // find the owner and remove namespace prefix - foreach (array_filter($namespace) as $type => $ns) { - foreach ($ns as $root) { - if (is_array($root) && $root[0] && strpos($path, $root[0]) === 0) { - $path = substr($path, strlen($root[0])); - - switch ($type) { - case 'shared': - // in theory there can be more than one shared root - // we add it to dummy user name, so we can revert conversion - $owner = "shared({$root[0]})"; - break; - - case 'other': - list($user, $path) = explode($separator, $path, 2); - - if (strpos($user, '@') === false) { - $domain = strstr($owner, '@'); - if (!empty($domain)) { - $user .= $domain; - } - } - - $owner = $user; - break; - } - - break 2; - } - } - } - - return 'imap://' . rawurlencode($owner) . '@' . $storage->options['host'] - . '/' . file_utils::encode_path($path); - } - - /** - * Convert global URI into file/folder path. - * - * @param string $uri URI - * - * @return string File/folder path - * @throws Exception - */ - public function uri2path($uri) - { - if (!preg_match('|^imap://([^@]+)@([^/]+)/(.*)$|', $uri, $matches)) { - throw new Exception("Internal storage error. Unexpected data format.", file_storage::ERROR); - } - - $storage = $this->rc->get_storage(); - $separator = $storage->get_hierarchy_delimiter(); - $owner = $this->rc->get_user_name(); - - $user = rawurldecode($matches[1]); - $path = file_utils::decode_path($matches[3]); - - // personal namespace - if ($user == $owner) { - // do nothing - // Note: that might not work if personal namespace uses e.g. INBOX/ prefix. - } - // shared namespace - else if (preg_match('/^shared\((.*)\)$/', $user, $matches)) { - $path = $matches[1] . $path; - } - // other users namespace - else { - $namespace = $storage->get_namespace('other'); - - list($local, $domain) = explode('@', $user); - - // here we assume there's only one other users namespace root - $path = $namespace[0][0] . $local . $separator . $path; - } - - return str_replace($separator, file_storage::SEPARATOR, $path); - } - - /** * Initializes file_locks object */ protected function init_lock_db()
View file
chwala-0.5.3.tar.gz/lib/drivers/seafile/seafile_api.php -> chwala-0.5.4.tar.gz/lib/drivers/seafile/seafile_api.php
Changed
@@ -142,15 +142,16 @@ /** * Send HTTP request * - * @param string $method Request method ('OPTIONS','GET','HEAD','POST','PUT','DELETE','TRACE','CONNECT') - * @param string $url Request API URL - * @param array $get GET parameters - * @param array $post POST parameters - * @param array $upload Uploaded files data + * @param string $method Request method ('OPTIONS','GET','HEAD','POST','PUT','DELETE','TRACE','CONNECT') + * @param string $url Request API URL + * @param array $get GET parameters + * @param array $post POST parameters + * @param array $upload Uploaded files data + * @param string $version API version (to replace "api2" with "api/v$version" in the URL * * @return string|array Server response */ - protected function request($method, $url, $get = null, $post = null, $upload = null) + protected function request($method, $url, $get = null, $post = null, $upload = null, $version = null) { if (!preg_match('/^https?:\/\//', $url)) { $url = $this->url . $url; @@ -161,6 +162,10 @@ $url = $this->mod_url($url); } + if ($version && $version != 2) { + $url = str_replace('/api2/', "/api/v$version/", $url); + } + if (!$this->request) { $this->config['store_body'] = true; // some methods respond with 301 redirect, we'll not follow them @@ -881,6 +886,459 @@ } /** + * Share a directory (or library) + * + * @param string $repo_id Library identifier + * @param string $dir Directory name (with path) + * @param string $right Permission ('r' or 'rw' or 'admin') + * @param string $mode Mode ('user' or 'group') + * @param string $who Username or Group ID + * @param bool $update Update an existing entry + * + * @return bool True on success, False on failure + */ + public function shared_item_add($repo_id, $dir, $right, $mode, $who, $update = false) + { + // sanity checks + if (!is_string($dir)) { + $this->status = self::BAD_REQUEST; + return false; + } + + if ($repo_id === '' || !is_string($repo_id)) { + $this->status = self::BAD_REQUEST; + return false; + } + + if ($mode != 'user' && $mode != 'group') { + $this->status = self::BAD_REQUEST; + return false; + } + + if ($right != 'r' && $right != 'rw' && $right != 'admin') { + $this->status = self::BAD_REQUEST; + return false; + } + + if ($dir === '') { + $dir = '/'; + } + + $post = array( + 'permission' => $right, + 'share_type' => $mode, + ); + + $post[$mode == 'group' ? 'group_id' : 'username'] = $who; + + $this->request($update ? 'POST' : 'PUT', "repos/$repo_id/dir/shared_items", array('p' => $dir), $post); + + return $this->is_error() === false; + } + + /** + * Update shared item permissions + * + * @param string $repo_id Library identifier + * @param string $dir Directory name (with path) + * @param string $right Permission ('r' or 'rw' or 'admin') + * @param string $mode Mode ('user' or 'group') + * @param string $who Username or Group ID + * + * @return bool True on success, False on failure + */ + public function shared_item_update($repo_id, $dir, $right, $mode, $who) + { + return $this->shared_item_add($repo_id, $dir, $right, $mode, $who, true); + } + + /** + * Un-Share a directory (or library) + * + * @param string $repo_id Library identifier + * @param string $dir Directory name (with path) + * @param string $mode Mode ('user' or 'group') + * @param string $who Username or Group ID + * + * @return bool True on success, False on failure + */ + public function shared_item_delete($repo_id, $dir, $mode, $who) + { + // sanity checks + if (!is_string($dir)) { + $this->status = self::BAD_REQUEST; + return false; + } + + if ($repo_id === '' || !is_string($repo_id)) { + $this->status = self::BAD_REQUEST; + return false; + } + + if ($mode != 'user' && $mode != 'group') { + $this->status = self::BAD_REQUEST; + return false; + } + + if ($dir === '') { + $dir = '/'; + } + + $get = array( + 'share_type' => $mode, + 'p' => $dir + ); + + $get[$mode == 'group' ? 'group_id' : 'username'] = $who; + + $this->request('DELETE', "repos/$repo_id/dir/shared_items", $get); + + return $this->is_error() === false; + } + + /** + * List directory permissions (shares) + * + * @param string $repo_id Library identifier + * @param string $dir Directory name (with path) + * + * @return bool|array List of user/group info on success, False on failure + */ + public function shared_item_list($repo_id, $dir) + { + // sanity checks + if (!is_string($dir)) { + $this->status = self::BAD_REQUEST; + return false; + } + + if ($repo_id === '' || !is_string($repo_id)) { + $this->status = self::BAD_REQUEST; + return false; + } + + if ($dir === '') { + $dir = '/'; + } + + // Example result: + // [ + // { + // "group_info": { "id": 17, "name": "Group Name" }, + // "is_admin": false, + // "share_type": "group", + // "permission": "rw" + // }, + // { + // "user_info": { "nickname": "user", "name": "user@domain.com" }, + // "share_type": "user", + // "permission": "r" + // } + // ] + + return $this->request('GET', "repos/$repo_id/dir/shared_items", array('p' => $dir)); + } + + /** + * List share (download) links + * + * @param string $repo_id Library identifier + * @param string $dir Directory name (with path) + * + * @return bool|array List of shared links on success, False on failure + */ + public function share_link_list($repo_id, $dir) + { + // sanity checks + if (!is_string($dir)) { + $this->status = self::BAD_REQUEST; + return false; + } + + if ($repo_id === '' || !is_string($repo_id)) { + $this->status = self::BAD_REQUEST; + return false; + } + + if ($dir === '') { + $dir = '/'; + } + + // Example result: + // [ + // { + // "username": "lian@lian.com", + // "repo_id": "104f6537-b3a5-4d42-b8b5-8e47e494e4cf", + // "ctime": "2017-04-01T02:35:29+00:00", + // "expire_date": "", + // "token": "0c4eb0cb104a43caaeef", + // "view_cnt": 0, + // "link": "https://cloud.seafile.com/d/0c4eb0cb104a43caaeef/", + // "obj_name": "folder", + // "path": "/folder/", + // "is_dir": true, + // "is_expired": false, + // "repo_name": "for-test-web-api" + // } + // ] + + return $this->request('GET', "share-links", array('repo_id' => $repo_id, 'path' => $dir), null, null, '2.1'); + } + + /** + * Create a shared (download) link + * + * @param string $repo_id Library identifier + * @param string $dir Directory name (with path) + * @param string $password Password + * @param string $expire Days to expire + * + * @return bool Link info on success, False on failure + */ + public function share_link_add($repo_id, $dir, $password = '', $expire = 0) + { + // sanity checks + if (!is_string($dir)) { + $this->status = self::BAD_REQUEST; + return false; + } + + if ($repo_id === '' || !is_string($repo_id)) { + $this->status = self::BAD_REQUEST; + return false; + } + + if (!empty($expire) && !is_numeric($expire)) { + $this->status = self::BAD_REQUEST; + return false; + } + + if ($dir === '') { + $dir = '/'; + } + + $post = array( + 'repo_id' => $repo_id, + 'path' => $dir, + ); + + if (strlen($password)) { + $post['password'] = $password; + } + if ($expire > 0) { + $post['expire_days'] = $expire; + } + + $result = $this->request('POST', "share-links", array(), $post, null, '2.1'); + + // Sample response: + // { + // "token": "0c4eb0cb104a43caaeef", + // "link": "https://cloud.seafile.com/d/db1a50e686/", + // ... + // } + + if (is_array($result) && !empty($result['link'])) { + return $result; + } + + return false; + } + + /** + * Delete a share (download) link + * + * @param string $token Link identifier (token) + * + * @return bool True on success, False on failure + */ + public function share_link_delete($token) + { + // sanity checks + if ($token === '' || !is_string($token)) { + $this->status = self::BAD_REQUEST; + return false; + } + + $this->request('DELETE', "share-links/{$token}", null, null, null, '2.1'); + + return $this->is_error() === false; + } + + /** + * List upload links + * + * @param string $repo_id Library identifier + * @param string $dir Directory name (with path) + * + * @return bool|array List of upload links on success, False on failure + */ + public function upload_link_list($repo_id, $dir) + { + // sanity checks + if (!is_string($dir)) { + $this->status = self::BAD_REQUEST; + return false; + } + + if ($repo_id === '' || !is_string($repo_id)) { + $this->status = self::BAD_REQUEST; + return false; + } + + if ($dir === '') { + $dir = '/'; + } + + // Example result: + // [ + // { + // "username": "lian@lian.com", + // "repo_id": "104f6537-b3a5-4d42-b8b5-8e47e494e4cf", + // "ctime": "2017-04-01T02:35:29+00:00", + // "expire_date": "", + // "token": "0c4eb0cb104a43caaeef", + // "view_cnt": 0, + // "link": "https://cloud.seafile.com/d/0c4eb0cb104a43caaeef/", + // "obj_name": "folder", + // "path": "/folder/", + // "is_dir": true, + // "is_expired": false, + // "repo_name": "for-test-web-api" + // } + // ] + + return $this->request('GET', "upload-links", array('repo_id' => $repo_id, 'path' => $dir), null, null, '2.1'); + } + + /** + * Create an upload link + * + * @param string $repo_id Library identifier + * @param string $dir Directory name (with path) + * @param string $password Password + * + * @return bool Link info on success, False on failure + */ + public function upload_link_add($repo_id, $dir, $password = '') + { + // sanity checks + if (!is_string($dir)) { + $this->status = self::BAD_REQUEST; + return false; + } + + if ($repo_id === '' || !is_string($repo_id)) { + $this->status = self::BAD_REQUEST; + return false; + } + + if ($dir === '') { + $dir = '/'; + } + + $post = array( + 'repo_id' => $repo_id, + 'path' => $dir, + ); + + if (strlen($password)) { + $post['password'] = $password; + } + + $result = $this->request('POST', "upload-links", array(), $post, null, '2.1'); + + // Sample response: + // { + // "token": "0c4eb0cb104a43caaeef", + // "link": "https://cloud.seafile.com/d/db1a50e686/", + // ... + // } + + if (is_array($result) && !empty($result['link'])) { + return $result; + } + + return false; + } + + /** + * Delete an upload link + * + * @param string $token Link identifier (token) + * + * @return bool True on success, False on failure + */ + public function upload_link_delete($token) + { + // sanity checks + if ($token === '' || !is_string($token)) { + $this->status = self::BAD_REQUEST; + return false; + } + + $this->request('DELETE', "upload-links/{$token}", null, null, null, '2.1'); + + return $this->is_error() === false; + } + + /** + * List user groups + * + * @return bool|array List of groups on success, False on failure + */ + public function group_list() + { + // Sample result: + // [ + // { + // "ctime": 1398134171327948, + // "creator": "user@example.com", + // "msgnum": 0, + // "mtime": 1398231100, + // "id": 1, + // "name": "lian" + // } + // ] }, + + $result = $this->request('GET', "groups"); + + if (is_array($result)) { + $result = (array) $result['groups']; + } + + return $result; + } + + /** + * List users + * + * @param string $search Search keyword + * + * @return bool|array List of users on success, False on failure + */ + public function user_search($search = null) + { + // Sample response: + // [ + // { + // 'avatar_url': 'https://cloud.seafile.com/media/avatars/default.png', + // 'contact_email': 'foo@foo.com', + // 'email': 'foo@foo.com', + // 'name': 'foo' + // } + // ] + + $result = $this->request('GET', "search-user", array('q' => $search)); + + if (is_array($result)) { + $result = (array) $result['users']; + } + + return $result; + } + + /** * Parse and fix API request URLs */ public function mod_url($url)
View file
chwala-0.5.3.tar.gz/lib/drivers/seafile/seafile_file_storage.php -> chwala-0.5.4.tar.gz/lib/drivers/seafile/seafile_file_storage.php
Changed
@@ -205,6 +205,7 @@ file_storage::CAPS_MAX_UPLOAD => $max_filesize, file_storage::CAPS_QUOTA => true, file_storage::CAPS_LOCKS => true, + file_storage::CAPS_ACL => true, ); } @@ -1106,6 +1107,312 @@ } /** + * Sharing interface + * + * @param string $folder_name Name of a folder with full path + * @param int $mode Sharing action mode + * @param array $args POST/GET parameters + * + * @return mixed Sharing response + * @throws Exception + */ + public function sharing($folder, $mode, $args = array()) + { + if ($mode == file_storage::SHARING_MODE_FORM) { + $form = array( + 'shares' => array( + 'title' => 'share.permissions', + 'form' => array( + 'user' => array( + 'title' => 'share.usergroup', + 'type' => 'input', + 'autocomplete' => 'user,group', + ), + 'right' => array( + 'title' => 'share.permission', + 'type' => 'select', + 'options' => array( + 'r' => 'share.readonly', + 'rw' => 'share.readwrite', + ), + ), + ), + 'extra_fields' => array( + 'type' => 'user', + 'id' => '', + ), + ), + 'download-link' => array( + 'title' => 'share.download-link', + 'label' => 'share.generate', + 'single' => true, + 'list_column' => 'link', + 'list_column_label' => 'share.link', + 'form' => array( + 'password' => array( + 'title' => 'share.password', + 'type' => 'password', + ), + 'expire' => array( + 'title' => 'share.expire', + 'placeholder' => 'share.expiredays', + 'type' => 'input', + ), + ), + 'extra_fields' => array( + 'id' => '', + ), + ), + 'upload-link' => array( + 'title' => 'share.upload-link', + 'label' => 'share.generate', + 'single' => true, + 'list_column' => 'link', + 'list_column_label' => 'share.link', + 'form' => array( + 'password' => array( + 'title' => 'share.password', + 'type' => 'password', + ), + ), + 'extra_fields' => array( + 'id' => '', + ), + ), + ); + + return $form; + } + + if ($mode == file_storage::SHARING_MODE_RIGHTS) { + if (!$this->init()) { + throw new Exception("Storage error. Unable to get shares of SeaFile folder/lib.", file_storage::ERROR); + } + + list($path, $repo_id) = $this->find_library($folder); + + $result = array(); + + if ($shares = $this->api->shared_item_list($repo_id, $path)) { + foreach ($shares as $share) { + if (!empty($share['group_info'])) { + $name = $share['group_info']['name']; + $name = $share['group_info']['id']; + } + else { + $name = $share['user_info']['name']; + $id = $share['user_info']['name']; + } + + $result[] = array( + 'mode' => 'shares', + 'type' => $share['share_type'], + 'right' => $share['permission'], + 'user' => $name, + 'id' => $id, + ); + } + } + + if ($links = $this->api->share_link_list($repo_id, $path)) { + foreach ($links as $link) { + $result[] = array( + 'mode' => 'download-link', + 'id' => $link['token'], + 'link' => $link['link'], + ); + } + } + + if ($links = $this->api->upload_link_list($repo_id, $path)) { + foreach ($links as $link) { + $result[] = array( + 'mode' => 'upload-link', + 'id' => $link['token'], + 'link' => $link['link'], + ); + } + } + + return $result; + } + + if ($mode == file_storage::SHARING_MODE_UPDATE) { + if (!$this->init()) { + throw new Exception("Storage error. Unable to update shares of SeaFile folder/lib.", file_storage::ERROR); + } + + list($path, $repo_id) = $this->find_library($folder); + + if ($args['mode'] == 'shares') { + switch ($args['action']) { + case 'submit': + $result = $this->api->shared_item_add($repo_id, $path, $args['right'], $args['type'], $args['user']); + break; + + case 'update': + $result = $this->api->shared_item_update($repo_id, $path, $args['right'], $args['type'], $args['id'] ?: $args['user']); + break; + + case 'delete': + $result = $this->api->shared_item_delete($repo_id, $path, $args['type'], $args['user']); + break; + } + } + else if ($args['mode'] == 'download-link') { + switch ($args['action']) { + case 'submit': + $result = $this->api->share_link_add($repo_id, $path, $args['password'], $args['expire']); + + if ($result) { + $result['id'] = $result['token']; + $result['mode'] = 'download-link'; + } + break; + + case 'delete': + $result = $this->api->share_link_delete($args['id']); + break; + } + } + else if ($args['mode'] == 'upload-link') { + switch ($args['action']) { + case 'submit': + $result = $this->api->upload_link_add($repo_id, $path, $args['password']); + + if ($result) { + $result['id'] = $result['token']; + $result['mode'] = 'upload-link'; + } + break; + + case 'delete': + $result = $this->api->upload_link_delete($args['id']); + break; + } + } + else { + throw new Exception("Invalid input.", file_storage::ERROR); + } + + if (empty($result)) { + throw new Exception("Storage error. Failed to update share.", file_storage::ERROR); + } + + return $result; + } + } + + /** + * User/group search (autocompletion) + * + * @param string $search Search string + * @param int $mode Search mode + * + * @return array Users/Groups list + * @throws Exception + */ + public function autocomplete($search, $mode) + { + if (!$this->init()) { + throw new Exception("Storage error. Failed to init Seafile storage connection.", file_storage::ERROR); + } + + $limit = (int) $this->rc->config->get('autocomplete_max', 15); + $result = array(); + $index = array(); + + if ($mode & file_storage::SEARCH_USER) { + $users = $this->api->user_search($search); + + if (!is_array($users)) { + throw new Exception("Storage error. Failed to search users.", file_storage::ERROR); + } + + foreach ($users as $user) { + $index[] = $user['name']; + $result[] = array( + 'name' => $user['name'], + 'id' => $user['email'], + 'type' => 'user', + ); + } + } + + if (count($result) < $limit && ($mode & file_storage::SEARCH_GROUP)) { + if ($groups = $this->api->group_list()) { + $search = mb_strtoupper($search); + foreach ($groups as $group) { + if (strpos(mb_strtoupper($group['name']), $search) !== false) { + $index[] = $group['name']; + $result[] = array( + 'name' => $group['name'], + 'id' => $group['id'], + 'type' => 'group', + ); + } + } + } + } + + if (count($result)) { + array_multisort($index, SORT_ASC, SORT_LOCALE_STRING, $result); + } + + if (count($result) > $limit) { + $result = array_slice($result, 0, $limit); + } + + return $result; + } + + /** + * Convert file/folder path into a global URI. + * + * @param string $path File/folder path + * + * @return string URI + * @throws Exception + */ + public function path2uri($path) + { + list($file, $repo_id, $library) = $this->find_library($path); + + // Remove protocol prefix and path, we work with host only + $host = preg_replace('#(^https?://|/.*$)#i', '', $this->config['host']); + + return 'seafile://' . rawurlencode($library['owner']) . '@' . $host . '/' . file_utils::encode_path($path); + } + + /** + * Convert global URI into file/folder path. + * + * @param string $uri URI + * + * @return string File/folder path + * @throws Exception + */ + public function uri2path($uri) + { + if (!preg_match('|^seafile://([^@]+)@([^/]+)/(.*)$|', $uri, $matches)) { + throw new Exception("Internal storage error. Unexpected data format.", file_storage::ERROR); + } + + $user = rawurldecode($matches[1]); + $host = $matches[2]; + $path = file_utils::decode_path($matches[3]); + $c_host = preg_replace('#(^https?://|/.*$)#i', '', $this->config['host']); + + list($file, $repo_id, $library) = $this->find_library($path, true); + + if (empty($library) || $host != $c_host || $user != $library['owner']) { + throw new Exception("Internal storage error. Unresolvable URI.", file_storage::ERROR); + } + + return $path; + } + + /** * Get folders tree in the Seafile library */ protected function folders_tree($library) @@ -1300,51 +1607,6 @@ } /** - * Convert file/folder path into a global URI. - * - * @param string $path File/folder path - * - * @return string URI - * @throws Exception - */ - public function path2uri($path) - { - list($file, $repo_id, $library) = $this->find_library($path); - - $host = preg_replace('|https?://|i', '', $this->config['host']); - - return 'seafile://' . rawurlencode($library['owner']) . '@' . $host . '/' . file_utils::encode_path($path); - } - - /** - * Convert global URI into file/folder path. - * - * @param string $uri URI - * - * @return string File/folder path - * @throws Exception - */ - public function uri2path($uri) - { - if (!preg_match('|^seafile://([^@]+)@([^/]+)/(.*)$|', $uri, $matches)) { - throw new Exception("Internal storage error. Unexpected data format.", file_storage::ERROR); - } - - $user = rawurldecode($matches[1]); - $host = $matches[2]; - $path = file_utils::decode_path($matches[3]); - $c_host = preg_replace('|https?://|i', '', $this->config['host']); - - list($file, $repo_id, $library) = $this->find_library($path, true); - - if (empty($library) || $host != $c_host || $user != $library['owner']) { - throw new Exception("Internal storage error. Unresolvable URI.", file_storage::ERROR); - } - - return $path; - } - - /** * Initializes file_locks object */ protected function init_lock_db()
View file
chwala-0.5.3.tar.gz/lib/drivers/webdav/webdav_file_storage.php -> chwala-0.5.4.tar.gz/lib/drivers/webdav/webdav_file_storage.php
Changed
@@ -917,6 +917,80 @@ } /** + * Sharing interface + * + * @param string $folder_name Name of a folder with full path + * @param int $mode Sharing action mode + * @param array $args POST/GET parameters + * + * @return mixed Sharing response + * @throws Exception + */ + public function sharing($folder, $mode, $args = array()) + { + // TODO + throw new Exception("Search not implemented", file_storage::ERROR_UNSUPPORTED); + } + + /** + * User/group search (autocompletion) + * + * @param string $search Search string + * @param int $mode Search mode + * + * @return array Users/Groups list + * @throws Exception + */ + public function autocomplete($search, $mode) + { + // TODO + throw new Exception("Search not implemented", file_storage::ERROR_UNSUPPORTED); + } + + /** + * Convert file/folder path into a global URI. + * + * @param string $path File/folder path + * + * @return string URI + * @throws Exception + */ + public function path2uri($path) + { + $base = preg_replace('|^[a-zA-Z]+://|', '', $this->config['baseuri']); + + return 'webdav://' . rawurlencode($this->config['username']) . '@' . $base + . '/' . file_utils::encode_path($path); + } + + /** + * Convert global URI into file/folder path. + * + * @param string $uri URI + * + * @return string File/folder path + * @throws Exception + */ + public function uri2path($uri) + { + if (!preg_match('|^webdav://([^@]+)@(.*)$|', $uri, $matches)) { + throw new Exception("Internal storage error. Unexpected data format.", file_storage::ERROR); + } + + $user = rawurldecode($matches[1]); + $base = preg_replace('|^[a-zA-Z]+://|', '', $this->config['baseuri']); + $uri = $matches[2]; + + if ($user != $this->config['username'] || strpos($uri, $base) !== 0) { + throw new Exception("Internal storage error. Unresolvable URI.", file_storage::ERROR); + } + + $uri = substr($matches[2], strlen($base) + 1); + + return file_utils::decode_path($uri); + } + + /** * Gets the relative URL of a resource * * @param string $url WebDAV URL @@ -976,49 +1050,6 @@ } /** - * Convert file/folder path into a global URI. - * - * @param string $path File/folder path - * - * @return string URI - * @throws Exception - */ - public function path2uri($path) - { - $base = preg_replace('|^[a-zA-Z]+://|', '', $this->config['baseuri']); - - return 'webdav://' . rawurlencode($this->config['username']) . '@' . $base - . '/' . file_utils::encode_path($path); - } - - /** - * Convert global URI into file/folder path. - * - * @param string $uri URI - * - * @return string File/folder path - * @throws Exception - */ - public function uri2path($uri) - { - if (!preg_match('|^webdav://([^@]+)@(.*)$|', $uri, $matches)) { - throw new Exception("Internal storage error. Unexpected data format.", file_storage::ERROR); - } - - $user = rawurldecode($matches[1]); - $base = preg_replace('|^[a-zA-Z]+://|', '', $this->config['baseuri']); - $uri = $matches[2]; - - if ($user != $this->config['username'] || strpos($uri, $base) !== 0) { - throw new Exception("Internal storage error. Unresolvable URI.", file_storage::ERROR); - } - - $uri = substr($matches[2], strlen($base) + 1); - - return file_utils::decode_path($uri); - } - - /** * Initializes file_locks object */ protected function init_lock_db()
View file
chwala-0.5.3.tar.gz/lib/file_api.php -> chwala-0.5.4.tar.gz/lib/file_api.php
Changed
@@ -188,9 +188,12 @@ $mem = memory_get_usage(); } - $request = ($this instanceof file_api_wopi ? 'wopi/' : '') - . $this->request - . (!empty($this->path) ? '/' . implode($this->path, '/') : ''); + $path = !empty($this->path) ? '/' . implode($this->path, '/') : ''; + $request = ($this instanceof file_api_wopi ? 'wopi/' : '') . $this->request; + + if ($path !== '' && substr_compare($this->request, $path, -1 * strlen($path), strlen($path), true) != 0) { + $request .= $path; + } $log = trim(sprintf('%s: %s %s', $this->method ?: $_SERVER['REQUEST_METHOD'],
View file
chwala-0.5.3.tar.gz/lib/file_api_core.php -> chwala-0.5.4.tar.gz/lib/file_api_core.php
Changed
@@ -24,7 +24,7 @@ class file_api_core extends file_locale { - const API_VERSION = 3; + const API_VERSION = 4; const ERROR_UNAUTHORIZED = 401; const ERROR_NOT_FOUND = 404; @@ -80,11 +80,12 @@ /** * Return supported/enabled external storage instances * - * @param bool $as_objects Return drivers as objects not config data + * @param bool $as_objects Return drivers as objects not config data + * @param array &$admin_drivers List of admin-configured drivers * * @return array List of storage drivers */ - public function get_drivers($as_objects = false) + public function get_drivers($as_objects = false, &$admin_drivers = null) { $rcube = rcube::get_instance(); $backend = $this->get_backend(); @@ -117,6 +118,8 @@ } } + $admin_drivers = array(); + if (empty($result) && !empty($preconf)) { foreach ((array) $preconf as $title => $item) { if (!in_array($title, $all)) { @@ -124,6 +127,8 @@ $item['admin'] = true; $result[] = $as_objects ? $this->get_driver_object($item) : $item; + + $admin_drivers[] = $title; } } } @@ -152,6 +157,11 @@ } if (empty($selected)) { + $rcube = rcube::get_instance(); + if ($rcube->config->get('fileapi_backend_storage_disabled')) { + throw new Exception("Failed to find a driver for specified folder/file.", self::ERROR_NOT_FOUND); + } + return array($this->get_backend(), $path); } @@ -218,7 +228,7 @@ { $rcube = rcube::get_instance(); $backend = $this->get_backend(); - $caps = array(); + $caps = array('VERSION' => self::API_VERSION); // check support for upload progress if (($progress_sec = $rcube->config->get('upload_progress')) @@ -246,6 +256,10 @@ $caps['WOPI'] = true; } + if ($rcube->config->get('fileapi_backend_storage_disabled')) { + $caps['NOROOT'] = true; + } + if (!$full) { return $caps; }
View file
chwala-0.5.3.tar.gz/lib/file_storage.php -> chwala-0.5.4.tar.gz/lib/file_storage.php
Changed
@@ -58,6 +58,16 @@ const ACL_READ = 1; const ACL_WRITE = 2; + // sharing interface modes + const SHARING_MODE_FORM = 1; + const SHARING_MODE_RIGHTS = 2; + const SHARING_MODE_UPDATE = 3; + + // search modes + const SEARCH_USER = 1; + const SEARCH_GROUP = 2; + + /** * Authenticates a user * @@ -349,7 +359,31 @@ public function quota($folder); /** + * Sharing interface + * + * @param string $folder_name Name of a folder with full path + * @param int $mode Sharing action mode + * @param array $args POST/GET parameters + * + * @return mixed Sharing response + * @throws Exception + */ + public function sharing($folder, $mode, $args = array()); + + /** + * User/group search (autocompletion) + * + * @param string $search Search string + * @param int $mode Search mode + * + * @return array Users/Groups list + * @throws Exception + */ + public function autocomplete($search, $mode); + + /** * Convert file/folder path into a global URI. + * Note: We're using self::SEPARATOR as a hierarchy delimiter * * @param string $path File/folder path * @@ -360,6 +394,7 @@ /** * Convert global URI into file/folder path. + * Note: We're using self::SEPARATOR as a hierarchy delimiter * * @param string $uri URI *
View file
chwala-0.5.3.tar.gz/lib/locale/en_US.php -> chwala-0.5.4.tar.gz/lib/locale/en_US.php
Changed
@@ -75,6 +75,20 @@ $LANG['search.in_all_folders'] = 'in all folders'; $LANG['search.in_current_folder'] = 'in current folder'; +$LANG['share.permissions'] = 'Permissions'; +$LANG['share.readonly'] = 'read-only'; +$LANG['share.readwrite'] = 'read-write'; +$LANG['share.admin'] = 'administrator'; +$LANG['share.usergroup'] = 'User or Group'; +$LANG['share.permission'] = 'Permission'; +$LANG['share.upload-link'] = 'Upload Link'; +$LANG['share.download-link'] = 'Download Link'; +$LANG['share.password'] = 'Password'; +$LANG['share.expire'] = 'Expiration'; +$LANG['share.expiredays'] = 'Expiration time in days'; +$LANG['share.generate'] = 'Generate'; +$LANG['share.link'] = 'Link'; + $LANG['size.B'] = 'B'; $LANG['size.KB'] = 'KB'; $LANG['size.MB'] = 'MB';
View file
chwala-0.5.3.tar.gz/lib/wopi/files.php -> chwala-0.5.4.tar.gz/lib/wopi/files.php
Changed
@@ -104,7 +104,7 @@ 'Version' => $info['modified'], 'OwnerId' => $info['owner'], 'UserId' => $info['user'], - 'UserFriendlyName' => $info['user_name'], + 'UserFriendlyName' => $info['user_name'] ?: preg_replace('/@.*$/', '', $info['user']), 'UserCanWrite' => empty($info['readonly']), 'PostMessageOrigin' => $info['origin'], // Tell the client we do not support PutRelativeFile yet @@ -114,9 +114,27 @@ 'HideExportOption' => true, 'HidePrintOption' => true, 'EnableOwnerTermination' => true, - // TODO: 'UserExtraInfo' => ['avatar' => 'http://url/to/user/avatar', 'mail' => 'user@server.com'] + 'WatermarkText' => '', // ?? + 'DisablePrint' => false, + 'DisableExport' => false, + 'DisableCopy' => false, + 'DisableInactiveMessages' => true, + 'DisableChangeTrackingRecord' => true, + 'DisableChangeTrackingShow' => true, + 'HideChangeTrackingControls' => true, + // TODO: 'UserExtraInfo' => ['avatar' => 'http://url/to/user/avatar', 'mail' => $info['user']] + 'UserExtraInfo' => array(), ); + if ($info['modified']) { + try { + $dt = new DateTime('@' . $info['modified'], new DateTimeZone('UTC')); + $result['LastModifiedTime'] = $dt->format('Y-m-dTH:i:s') . '.0000000Z'; + } + catch (Exception $e) { + } + } + return $result; }
View file
chwala-0.5.3.tar.gz/public_html/.htaccess -> chwala-0.5.4.tar.gz/public_html/.htaccess
Changed
@@ -2,4 +2,4 @@ php_flag display_errors Off php_flag log_errors On php_flag suhosin.session.encrypt Off -php_value error_log ../logs/errors +php_value error_log ../logs/errors.log
View file
chwala-0.5.3.tar.gz/public_html/api/.htaccess -> chwala-0.5.4.tar.gz/public_html/api/.htaccess
Changed
@@ -2,5 +2,5 @@ php_flag session.use_cookies Off php_flag display_errors Off php_flag log_errors On -php_value error_log ../../logs/errors +php_value error_log ../../logs/errors.log
View file
chwala-0.5.3.tar.gz/public_html/js/files_api.js -> chwala-0.5.4.tar.gz/public_html/js/files_api.js
Changed
@@ -72,7 +72,7 @@ /********************************************************/ // send a http POST request to the API service - this.post = function(action, data, func) + this.post = function(action, data, func, error_func) { var url = this.env.url + '?method=' + action; @@ -83,15 +83,15 @@ return $.ajax({ type: 'POST', url: url, data: JSON.stringify(data), dataType: 'json', contentType: 'application/json; charset=utf-8', - success: function(response) { if (typeof func == 'function') func(response); else ref[func](response); }, - error: function(o, status, err) { ref.http_error(o, status, err, data); }, + success: function(response) { if (typeof func == 'function') func(response, data); else ref[func](response, data); }, + error: function(o, status, err) { (error_func || ref.http_error)(o, status, err, data); }, cache: false, beforeSend: function(xmlhttp) { xmlhttp.setRequestHeader('X-Session-Token', ref.env.token); } }); }; // send a http GET request to the API service - this.get = function(action, data, func) + this.get = function(action, data, func, error_func) { var url = this.env.url; @@ -102,19 +102,19 @@ return $.ajax({ type: 'GET', url: url, data: data, dataType: 'json', - success: function(response) { if (typeof func == 'function') func(response); else ref[func](response); }, - error: function(o, status, err) { ref.http_error(o, status, err, data); }, + success: function(response) { if (typeof func == 'function') func(response, data); else ref[func](response, data); }, + error: function(o, status, err) { (error_func || ref.http_error)(o, status, err, data); }, cache: false, beforeSend: function(xmlhttp) { xmlhttp.setRequestHeader('X-Session-Token', ref.env.token); } }); }; // send request with auto-selection of POST/GET method - this.request = function(action, data, func) + this.request = function(action, data, func, error_func) { // Use POST for modification actions with probable big request size var method = /_(create|delete|move|copy|update|auth|subscribe|unsubscribe|invite|decline|request|accept|remove)$/.test(action) ? 'post' : 'get'; - return this[method](action, data, func); + return this[method](action, data, func, error_func); }; // handle HTTP request errors @@ -795,12 +795,9 @@ var color = value.Color; // make sure color is in css hex format - if (color) { - if ($.type(color) == 'string' && color.charAt(0) != '#') - color = '#' + color; - else if ($.type(color) == 'number') - color = ((color)>>>0).toString(16).slice(-6); - color = '#' + ("000000").substring(0, 6 - color.length) + color; + if (color && $.type(color) == 'number') { + color = ((color)>>>0).toString(16).slice(-6); + color = '#' + ("000000").substring(0, 6 - color.length) + color; } return { @@ -1186,6 +1183,24 @@ conf.invitationSaved(invitation); }; + this.domain_compare = function(origin, domain) + { + if (!origin && !domain) + return true; + + if (!origin || !domain) + return false; + + // Sometimes Collabora adds port to the URL + // We'll remove it for proper comparison + if (origin.match(/^https:\/\/(.*):443$/)) + origin = origin.replace(/:443$/, ''); + if (domain.match(/^https:\/\/(.*):443$/)) + domain = domain.replace(/:443$/, ''); + + return origin == domain; + }; + if (!conf) conf = {}; @@ -1204,7 +1219,7 @@ // Register 'message' event to receive messages from the editor iframe window.addEventListener('message', function(event) { - if (event.source == editor && event.origin == domain) { + if (event.source == editor && self.domain_compare(event.origin, domain)) { self.message_handler(event.data); } });
View file
chwala.dsc
Changed
@@ -2,7 +2,7 @@ Source: chwala Binary: chwala Architecture: all -Version: 0.5.3-0~kolab1 +Version: 0.5.4-0~kolab1 Maintainer: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Uploaders: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com>, Paul Klos <kolab@klos2day.nl> Homepage: http://kolab.org/about/chwala/ @@ -11,5 +11,5 @@ Package-List: roundcubemail deb web extra Files: - 00000000000000000000000000000000 0 chwala-0.5.3.tar.gz + 00000000000000000000000000000000 0 chwala-0.5.4.tar.gz 00000000000000000000000000000000 0 debian.tar.gz
View file
debian.changelog
Changed
@@ -1,3 +1,9 @@ +chwala (0.5.4-0~kolab1) unsable; urgency=low + + * Release version 0.5.4 + + -- Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabys.com> Fri, 26 Oct 2018 12:12:12 +0100 + chwala (0.5.3-0~kolab1) unsable; urgency=low * Release version 0.5.3
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.