core (memory), plugin XEP-0329, plugin invitation: minor style improvments
This commit is contained in:
parent
6b83ef5c51
commit
5e2426c065
3 changed files with 126 additions and 98 deletions
|
|
@ -1417,27 +1417,11 @@ class Memory(object):
|
|||
|
||||
@defer.inlineCallbacks
|
||||
def setFile(
|
||||
self,
|
||||
client,
|
||||
name,
|
||||
file_id=None,
|
||||
version=u"",
|
||||
parent=None,
|
||||
path=None,
|
||||
type_=C.FILE_TYPE_FILE,
|
||||
file_hash=None,
|
||||
hash_algo=None,
|
||||
size=None,
|
||||
namespace=None,
|
||||
mime_type=None,
|
||||
created=None,
|
||||
modified=None,
|
||||
owner=None,
|
||||
access=None,
|
||||
extra=None,
|
||||
peer_jid=None,
|
||||
perms_to_check=(C.ACCESS_PERM_WRITE,),
|
||||
):
|
||||
self, client, name, file_id=None, version=u"", parent=None, path=None,
|
||||
type_=C.FILE_TYPE_FILE, file_hash=None, hash_algo=None, size=None,
|
||||
namespace=None, mime_type=None, created=None, modified=None, owner=None,
|
||||
access=None, extra=None, peer_jid=None, perms_to_check=(C.ACCESS_PERM_WRITE,)
|
||||
):
|
||||
"""set a file metadata
|
||||
|
||||
@param name(unicode): basename of the file
|
||||
|
|
@ -1446,32 +1430,38 @@ class Memory(object):
|
|||
empty string for current version or when there is no versioning
|
||||
@param parent(unicode, None): id of the directory containing the files
|
||||
@param path(unicode, None): virtual path of the file in the namespace
|
||||
if set, parent must be None. All intermediate directories will be created if needed,
|
||||
using current access.
|
||||
if set, parent must be None. All intermediate directories will be created
|
||||
if needed, using current access.
|
||||
@param file_hash(unicode): unique hash of the payload
|
||||
@param hash_algo(unicode): algorithm used for hashing the file (usually sha-256)
|
||||
@param size(int): size in bytes
|
||||
@param namespace(unicode, None): identifier (human readable is better) to group files
|
||||
for instance, namespace could be used to group files in a specific photo album
|
||||
@param namespace(unicode, None): identifier (human readable is better) to group
|
||||
files
|
||||
For instance, namespace could be used to group files in a specific photo album
|
||||
@param mime_type(unicode): MIME type of the file, or None if not known/guessed
|
||||
@param created(int): UNIX time of creation
|
||||
@param modified(int,None): UNIX time of last modification, or None to use created date
|
||||
@param owner(jid.JID, None): jid of the owner of the file (mainly useful for component)
|
||||
@param modified(int,None): UNIX time of last modification, or None to use
|
||||
created date
|
||||
@param owner(jid.JID, None): jid of the owner of the file (mainly useful for
|
||||
component)
|
||||
will be used to check permission (only bare jid is used, don't use with MUC).
|
||||
Use None to ignore permission (perms_to_check must be None too)
|
||||
@param access(dict, None): serialisable dictionary with access rules.
|
||||
None (or empty dict) to use private access, i.e. allow only profile's jid to access the file
|
||||
None (or empty dict) to use private access, i.e. allow only profile's jid to
|
||||
access the file
|
||||
key can be on on C.ACCESS_PERM_*,
|
||||
then a sub dictionary with a type key is used (one of C.ACCESS_TYPE_*).
|
||||
According to type, extra keys can be used:
|
||||
- C.ACCESS_TYPE_PUBLIC: the permission is granted for everybody
|
||||
- C.ACCESS_TYPE_WHITELIST: the permission is granted for jids (as unicode) in the 'jids' key
|
||||
- C.ACCESS_TYPE_WHITELIST: the permission is granted for jids (as unicode)
|
||||
in the 'jids' key
|
||||
will be encoded to json in database
|
||||
@param extra(dict, None): serialisable dictionary of any extra data
|
||||
will be encoded to json in database
|
||||
@param perms_to_check(tuple[unicode],None): permission to check
|
||||
must be a tuple of C.ACCESS_PERM_* or None
|
||||
if None, permission will no be checked (peer_jid must be None too in this case)
|
||||
if None, permission will no be checked (peer_jid must be None too in this
|
||||
case)
|
||||
@param profile(unicode): profile owning the file
|
||||
"""
|
||||
if "/" in name:
|
||||
|
|
|
|||
|
|
@ -17,20 +17,21 @@
|
|||
# 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/>.
|
||||
|
||||
import shortuuid
|
||||
from twisted.internet import defer
|
||||
from twisted.words.protocols.jabber import jid
|
||||
from twisted.words.protocols.jabber import error
|
||||
from sat.core.i18n import _, D_
|
||||
from sat.core.constants import Const as C
|
||||
from sat.core import exceptions
|
||||
from sat.core.log import getLogger
|
||||
log = getLogger(__name__)
|
||||
import shortuuid
|
||||
from sat.tools import utils
|
||||
from sat.tools.common import data_format
|
||||
from twisted.internet import defer
|
||||
from twisted.words.protocols.jabber import jid
|
||||
from twisted.words.protocols.jabber import error
|
||||
from sat.memory import persistent
|
||||
from sat.tools import email as sat_email
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
||||
|
||||
PLUGIN_INFO = {
|
||||
C.PI_NAME: "Invitations",
|
||||
|
|
@ -53,7 +54,8 @@ KEY_LAST_CONNECTION = u'last_connection'
|
|||
KEY_GUEST_PROFILE = u'guest_profile'
|
||||
KEY_PASSWORD = u'password'
|
||||
KEY_EMAILS_EXTRA = u'emails_extra'
|
||||
EXTRA_RESERVED = {KEY_ID, KEY_JID, KEY_CREATED, u'jid_', u'jid', KEY_LAST_CONNECTION, KEY_GUEST_PROFILE, KEY_PASSWORD, KEY_EMAILS_EXTRA}
|
||||
EXTRA_RESERVED = {KEY_ID, KEY_JID, KEY_CREATED, u'jid_', u'jid', KEY_LAST_CONNECTION,
|
||||
KEY_GUEST_PROFILE, KEY_PASSWORD, KEY_EMAILS_EXTRA}
|
||||
DEFAULT_SUBJECT = D_(u"You have been invited by {host_name} to {app_name}")
|
||||
DEFAULT_BODY = D_(u"""Hello {name}!
|
||||
|
||||
|
|
@ -74,26 +76,33 @@ class InvitationsPlugin(object):
|
|||
log.info(_(u"plugin Invitations initialization"))
|
||||
self.host = host
|
||||
self.invitations = persistent.LazyPersistentBinaryDict(u'invitations')
|
||||
host.bridge.addMethod("invitationCreate", ".plugin", in_sign='sasssssssssa{ss}s', out_sign='a{ss}',
|
||||
host.bridge.addMethod("invitationCreate", ".plugin", in_sign='sasssssssssa{ss}s',
|
||||
out_sign='a{ss}',
|
||||
method=self._create,
|
||||
async=True)
|
||||
host.bridge.addMethod("invitationGet", ".plugin", in_sign='s', out_sign='a{ss}',
|
||||
method=self.get,
|
||||
async=True)
|
||||
host.bridge.addMethod("invitationModify", ".plugin", in_sign='sa{ss}b', out_sign='',
|
||||
host.bridge.addMethod("invitationModify", ".plugin", in_sign='sa{ss}b',
|
||||
out_sign='',
|
||||
method=self._modify,
|
||||
async=True)
|
||||
host.bridge.addMethod("invitationList", ".plugin", in_sign='s', out_sign='a{sa{ss}}',
|
||||
host.bridge.addMethod("invitationList", ".plugin", in_sign='s',
|
||||
out_sign='a{sa{ss}}',
|
||||
method=self._list,
|
||||
async=True)
|
||||
|
||||
def checkExtra(self, extra):
|
||||
if EXTRA_RESERVED.intersection(extra):
|
||||
raise ValueError(_(u"You can't use following key(s) in extra, they are reserved: {}").format(
|
||||
u', '.join(EXTRA_RESERVED.intersection(extra))))
|
||||
raise ValueError(
|
||||
_(u"You can't use following key(s) in extra, they are reserved: {}")
|
||||
.format(u', '.join(EXTRA_RESERVED.intersection(extra))))
|
||||
|
||||
def _create(self, email=u'', emails_extra=None, jid_=u'', password=u'', name=u'', host_name=u'', language=u'', url_template=u'', message_subject=u'', message_body=u'', extra=None, profile=u''):
|
||||
# XXX: we don't use **kwargs here to keep arguments name for introspection with D-Bus bridge
|
||||
def _create(self, email=u'', emails_extra=None, jid_=u'', password=u'', name=u'',
|
||||
host_name=u'', language=u'', url_template=u'', message_subject=u'',
|
||||
message_body=u'', extra=None, profile=u''):
|
||||
# XXX: we don't use **kwargs here to keep arguments name for introspection with
|
||||
# D-Bus bridge
|
||||
if emails_extra is None:
|
||||
emails_extra = []
|
||||
|
||||
|
|
@ -106,8 +115,10 @@ class InvitationsPlugin(object):
|
|||
KEY_EMAILS_EXTRA: [unicode(e) for e in emails_extra]
|
||||
}
|
||||
|
||||
# we need to be sure that values are unicode, else they won't be pickled correctly with D-Bus
|
||||
for key in ("jid_", "password", "name", "host_name", "email", "language", "url_template", "message_subject", "message_body", "profile"):
|
||||
# we need to be sure that values are unicode, else they won't be pickled correctly
|
||||
# with D-Bus
|
||||
for key in ("jid_", "password", "name", "host_name", "email", "language",
|
||||
"url_template", "message_subject", "message_body", "profile"):
|
||||
value = locals()[key]
|
||||
if value:
|
||||
kwargs[key] = unicode(value)
|
||||
|
|
@ -120,32 +131,45 @@ class InvitationsPlugin(object):
|
|||
|
||||
@defer.inlineCallbacks
|
||||
def create(self, **kwargs):
|
||||
ur"""create an invitation
|
||||
ur"""Create an invitation
|
||||
|
||||
this will create an XMPP account and a profile, and use a UUID to retrieve them.
|
||||
the profile is automatically generated in the form guest@@[UUID], this way they can be retrieved easily
|
||||
**kwargs: keywords arguments which can have the following keys, unset values are equivalent to None:
|
||||
jid_(jid.JID, None): jid to use for invitation, the jid will be created using XEP-0077
|
||||
if the jid has no user part, an anonymous account will be used (no XMPP account created in this case)
|
||||
if None, automatically generate an account name (in the form "invitation-[random UUID]@domain.tld") (note that this UUID is not the
|
||||
same as the invitation one, as jid can be used publicly (leaking the UUID), and invitation UUID give access to account.
|
||||
in case of conflict, a suffix number is added to the account until a free one if found (with a failure if SUFFIX_MAX is reached)
|
||||
password(unicode, None): password to use (will be used for XMPP account and profile)
|
||||
This will create an XMPP account and a profile, and use a UUID to retrieve them.
|
||||
The profile is automatically generated in the form guest@@[UUID], this way they
|
||||
can be retrieved easily
|
||||
**kwargs: keywords arguments which can have the following keys, unset values are
|
||||
equivalent to None:
|
||||
jid_(jid.JID, None): jid to use for invitation, the jid will be created using
|
||||
XEP-0077
|
||||
if the jid has no user part, an anonymous account will be used (no XMPP
|
||||
account created in this case)
|
||||
if None, automatically generate an account name (in the form
|
||||
"invitation-[random UUID]@domain.tld") (note that this UUID is not the
|
||||
same as the invitation one, as jid can be used publicly (leaking the
|
||||
UUID), and invitation UUID give access to account.
|
||||
in case of conflict, a suffix number is added to the account until a free
|
||||
one if found (with a failure if SUFFIX_MAX is reached)
|
||||
password(unicode, None): password to use (will be used for XMPP account and
|
||||
profile)
|
||||
None to automatically generate one
|
||||
name(unicode, None): name of the invitee
|
||||
will be set as profile identity if present
|
||||
host_name(unicode, None): name of the host
|
||||
email(unicode, None): email to send the invitation to
|
||||
if None, no invitation email is sent, you can still associate email using extra
|
||||
if None, no invitation email is sent, you can still associate email using
|
||||
extra
|
||||
if email is used, extra can't have "email" key
|
||||
language(unicode): language of the invitee (used notabily to translate the invitation)
|
||||
language(unicode): language of the invitee (used notabily to translate the
|
||||
invitation)
|
||||
TODO: not used yet
|
||||
url_template(unicode, None): template to use to construct the invitation URL
|
||||
use {uuid} as a placeholder for identifier
|
||||
use None if you don't want to include URL (or if it is already specified in custom message)
|
||||
use None if you don't want to include URL (or if it is already specified
|
||||
in custom message)
|
||||
/!\ you must put full URL, don't forget https://
|
||||
/!\ the URL will give access to the invitee account, you should warn in message to not publish it publicly
|
||||
message_subject(unicode, None): customised message body for the invitation email
|
||||
/!\ the URL will give access to the invitee account, you should warn in
|
||||
message to not publish it publicly
|
||||
message_subject(unicode, None): customised message body for the invitation
|
||||
email
|
||||
None to use default subject
|
||||
uses the same substitution as for message_body
|
||||
message_body(unicode, None): customised message body for the invitation email
|
||||
|
|
@ -169,7 +193,8 @@ class InvitationsPlugin(object):
|
|||
## initial checks
|
||||
extra = kwargs.pop('extra', {})
|
||||
if set(kwargs).intersection(extra):
|
||||
raise ValueError(_(u"You can't use following key(s) in both args and extra: {}").format(
|
||||
raise ValueError(
|
||||
_(u"You can't use following key(s) in both args and extra: {}").format(
|
||||
u', '.join(set(kwargs).intersection(extra))))
|
||||
|
||||
self.checkExtra(extra)
|
||||
|
|
@ -177,10 +202,14 @@ class InvitationsPlugin(object):
|
|||
email = kwargs.pop(u'email', None)
|
||||
emails_extra = kwargs.pop(u'emails_extra', [])
|
||||
if not email and emails_extra:
|
||||
raise ValueError(_(u'You need to provide a main email address before using emails_extra'))
|
||||
raise ValueError(
|
||||
_(u'You need to provide a main email address before using emails_extra'))
|
||||
|
||||
if email is not None and not 'url_template' in kwargs and not 'message_body' in kwargs:
|
||||
raise ValueError(_(u"You need to provide url_template if you use default message body"))
|
||||
if (email is not None
|
||||
and not 'url_template' in kwargs
|
||||
and not 'message_body' in kwargs):
|
||||
raise ValueError(
|
||||
_(u"You need to provide url_template if you use default message body"))
|
||||
|
||||
## uuid
|
||||
log.info(_(u"creating an invitation"))
|
||||
|
|
@ -195,8 +224,8 @@ class InvitationsPlugin(object):
|
|||
# it is needed for invitation as the same password is used for profile
|
||||
# and SàT need to be able to automatically open the profile with the uuid
|
||||
# FIXME: we could add an extra encryption key which would be used with the uuid
|
||||
# when the invitee is connecting (e.g. with URL). This key would not be saved
|
||||
# and could be used to encrypt profile password.
|
||||
# when the invitee is connecting (e.g. with URL). This key would not be
|
||||
# saved and could be used to encrypt profile password.
|
||||
extra[KEY_PASSWORD] = password
|
||||
|
||||
jid_ = kwargs.pop(u'jid_', None)
|
||||
|
|
@ -205,10 +234,12 @@ class InvitationsPlugin(object):
|
|||
if not domain:
|
||||
# TODO: fallback to profile's domain
|
||||
raise ValueError(_(u"You need to specify xmpp_domain in sat.conf"))
|
||||
jid_ = u"invitation-{uuid}@{domain}".format(uuid=shortuuid.uuid(), domain=domain)
|
||||
jid_ = u"invitation-{uuid}@{domain}".format(uuid=shortuuid.uuid(),
|
||||
domain=domain)
|
||||
jid_ = jid.JID(jid_)
|
||||
if jid_.user:
|
||||
# we don't register account if there is no user as anonymous login is then used
|
||||
# we don't register account if there is no user as anonymous login is then
|
||||
# used
|
||||
try:
|
||||
yield self.host.plugins['XEP-0077'].registerNewAccount(jid_, password)
|
||||
except error.StanzaError as e:
|
||||
|
|
@ -218,9 +249,11 @@ class InvitationsPlugin(object):
|
|||
if idx >= SUFFIX_MAX:
|
||||
raise exceptions.ConflictError(_(u"Can't create XMPP account"))
|
||||
jid_.user = prefix + '_' + unicode(idx)
|
||||
log.info(_(u"requested jid already exists, trying with {}".format(jid_.full())))
|
||||
log.info(_(u"requested jid already exists, trying with {}".format(
|
||||
jid_.full())))
|
||||
try:
|
||||
yield self.host.plugins['XEP-0077'].registerNewAccount(jid_, password)
|
||||
yield self.host.plugins['XEP-0077'].registerNewAccount(jid_,
|
||||
password)
|
||||
except error.StanzaError as e:
|
||||
idx += 1
|
||||
else:
|
||||
|
|
@ -236,8 +269,10 @@ class InvitationsPlugin(object):
|
|||
# profile creation should not fail as we generate unique name ourselves
|
||||
yield self.host.memory.createProfile(guest_profile, password)
|
||||
yield self.host.memory.startSession(password, guest_profile)
|
||||
yield self.host.memory.setParam("JabberID", jid_.full(), "Connection", profile_key=guest_profile)
|
||||
yield self.host.memory.setParam("Password", password, "Connection", profile_key=guest_profile)
|
||||
yield self.host.memory.setParam("JabberID", jid_.full(), "Connection",
|
||||
profile_key=guest_profile)
|
||||
yield self.host.memory.setParam("Password", password, "Connection",
|
||||
profile_key=guest_profile)
|
||||
name = kwargs.pop(u'name', None)
|
||||
if name is not None:
|
||||
extra[u'name'] = name
|
||||
|
|
@ -288,7 +323,8 @@ class InvitationsPlugin(object):
|
|||
yield sat_email.sendEmail(
|
||||
self.host,
|
||||
[email] + emails_extra,
|
||||
(kwargs.pop(u'message_subject', None) or DEFAULT_SUBJECT).format(**format_args),
|
||||
(kwargs.pop(u'message_subject', None) or DEFAULT_SUBJECT).format(
|
||||
**format_args),
|
||||
(kwargs.pop(u'message_body', None) or DEFAULT_BODY).format(**format_args),
|
||||
)
|
||||
|
||||
|
|
@ -312,7 +348,8 @@ class InvitationsPlugin(object):
|
|||
return self.invitations[id_]
|
||||
|
||||
def _modify(self, id_, new_extra, replace):
|
||||
return self.modify(id_, {unicode(k): unicode(v) for k,v in new_extra.iteritems()}, replace)
|
||||
return self.modify(id_, {unicode(k): unicode(v) for k,v in new_extra.iteritems()},
|
||||
replace)
|
||||
|
||||
def modify(self, id_, new_extra, replace=False):
|
||||
"""Modify invitation data
|
||||
|
|
@ -366,6 +403,7 @@ class InvitationsPlugin(object):
|
|||
"""
|
||||
invitations = yield self.invitations.items()
|
||||
if profile != C.PROF_KEY_NONE:
|
||||
invitations = {id_:data for id_, data in invitations.iteritems() if data.get(u'profile') == profile}
|
||||
invitations = {id_:data for id_, data in invitations.iteritems()
|
||||
if data.get(u'profile') == profile}
|
||||
|
||||
defer.returnValue(invitations)
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ KEY_TYPE = u"type"
|
|||
|
||||
|
||||
class ShareNode(object):
|
||||
"""node containing directory or files to share, virtual or real"""
|
||||
"""Node containing directory or files to share, virtual or real"""
|
||||
|
||||
host = None
|
||||
|
||||
|
|
@ -112,7 +112,7 @@ class ShareNode(object):
|
|||
return self.children.itervalues()
|
||||
|
||||
def getOrCreate(self, name, type_=TYPE_VIRTUAL, access=None):
|
||||
"""get a node or create a virtual node and return it"""
|
||||
"""Get a node or create a virtual node and return it"""
|
||||
if access is None:
|
||||
access = {C.ACCESS_PERM_READ: {KEY_TYPE: C.ACCESS_TYPE_PUBLIC}}
|
||||
try:
|
||||
|
|
@ -157,11 +157,12 @@ class ShareNode(object):
|
|||
def checkPermissions(
|
||||
self, client, peer_jid, perms=(C.ACCESS_PERM_READ,), check_parents=True
|
||||
):
|
||||
"""check that peer_jid can access this node and all its parents
|
||||
"""Check that peer_jid can access this node and all its parents
|
||||
|
||||
@param peer_jid(jid.JID): entrity trying to access the node
|
||||
@param perms(unicode): permissions to check, iterable of C.ACCESS_PERM_*
|
||||
@param check_parents(bool): if True, access of all parents of this node will be checked too
|
||||
@param check_parents(bool): if True, access of all parents of this node will be
|
||||
checked too
|
||||
@return (bool): True if entity can access this node
|
||||
"""
|
||||
peer_jid = peer_jid.userhostJID()
|
||||
|
|
@ -191,11 +192,9 @@ class ShareNode(object):
|
|||
path_elts = filter(None, path.split(u"/"))
|
||||
|
||||
if u".." in path_elts:
|
||||
log.warning(
|
||||
_(
|
||||
u'parent dir ("..") found in path, hack attempt? path is {path} [{profile}]'
|
||||
).format(path=path, profile=client.profile)
|
||||
)
|
||||
log.warning(_(
|
||||
u'parent dir ("..") found in path, hack attempt? path is {path} '
|
||||
u'[{profile}]').format(path=path, profile=client.profile))
|
||||
raise exceptions.PermissionError(u"illegal path elements")
|
||||
|
||||
if not path_elts:
|
||||
|
|
@ -312,9 +311,10 @@ class XEP_0329(object):
|
|||
def _fileSendingRequestTrigger(
|
||||
self, client, session, content_data, content_name, file_data, file_elt
|
||||
):
|
||||
"""this trigger check that a requested file is available, and fill suitable data if so
|
||||
"""This trigger check that a requested file is available, and fill suitable data
|
||||
|
||||
path and name are used to retrieve the file. If path is missing, we try our luck with known names
|
||||
Path and name are used to retrieve the file. If path is missing, we try our luck
|
||||
with known names
|
||||
"""
|
||||
if client.is_component:
|
||||
return True, None
|
||||
|
|
@ -423,7 +423,8 @@ class XEP_0329(object):
|
|||
)
|
||||
|
||||
query_elt.addChild(file_elt)
|
||||
# we don't specify hash as it would be too resource intensive to calculate it for all files
|
||||
# we don't specify hash as it would be too resource intensive to calculate
|
||||
# it for all files.
|
||||
# we add file to name_data, so users can request it later
|
||||
name_data = client._XEP_0329_names_data.setdefault(name, {})
|
||||
if path not in name_data:
|
||||
|
|
@ -547,7 +548,9 @@ class XEP_0329(object):
|
|||
"""retrieve files from local files repository according to permissions
|
||||
|
||||
result stanza is then built and sent to requestor
|
||||
@trigger XEP-0329_compGetFilesFromNode(client, iq_elt, owner, node_path, files_data): can be used to add data/elements
|
||||
@trigger XEP-0329_compGetFilesFromNode(client, iq_elt, owner, node_path,
|
||||
files_data):
|
||||
can be used to add data/elements
|
||||
"""
|
||||
peer_jid = jid.JID(iq_elt["from"])
|
||||
try:
|
||||
|
|
@ -660,7 +663,8 @@ class XEP_0329(object):
|
|||
node = client._XEP_0329_root_node
|
||||
node_type = TYPE_PATH
|
||||
if os.path.isfile(path):
|
||||
# we have a single file, the workflow is diferrent as we store all single files in the same dir
|
||||
# we have a single file, the workflow is diferrent as we store all single
|
||||
# files in the same dir
|
||||
node = node.getOrCreate(SINGLE_FILES_DIR)
|
||||
|
||||
if not name:
|
||||
|
|
@ -675,13 +679,9 @@ class XEP_0329(object):
|
|||
idx += 1
|
||||
new_name = name + "_" + unicode(idx)
|
||||
name = new_name
|
||||
log.info(
|
||||
_(
|
||||
u"A directory with this name is already shared, renamed to {new_name} [{profile}]".format(
|
||||
new_name=new_name, profile=client.profile
|
||||
)
|
||||
)
|
||||
)
|
||||
log.info(_(
|
||||
u"A directory with this name is already shared, renamed to {new_name} "
|
||||
u"[{profile}]".format( new_name=new_name, profile=client.profile)))
|
||||
|
||||
ShareNode(name=name, parent=node, type_=node_type, access=access, path=path)
|
||||
self.host.bridge.FISSharedPathNew(path, name, client.profile)
|
||||
|
|
|
|||
Loading…
Reference in a new issue