component file sharing: add/parse affiliation when possible
This commit is contained in:
parent
cb49365811
commit
d7844a98b4
5 changed files with 110 additions and 39 deletions
|
|
@ -23,7 +23,6 @@ from functools import partial
|
|||
import shortuuid
|
||||
import unicodedata
|
||||
from urllib.parse import urljoin, urlparse, quote, unquote
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from sat.core.i18n import _
|
||||
from sat.core.constants import Const as C
|
||||
|
|
@ -69,6 +68,7 @@ PLUGIN_INFO = {
|
|||
|
||||
HASH_ALGO = "sha-256"
|
||||
NS_COMMENTS = "org.salut-a-toi.comments"
|
||||
NS_FS_AFFILIATION = "org.salut-a-toi.file-sharing-affiliation"
|
||||
COMMENT_NODE_PREFIX = "org.salut-a-toi.file_comments/"
|
||||
# Directory used to buffer request body (i.e. file in case of PUT) we use more than one @
|
||||
# there, to be sure than it's not conflicting with a JID
|
||||
|
|
@ -270,7 +270,7 @@ class FileSharingRequest(server.Request):
|
|||
try:
|
||||
upload_id, filename = self.upload_data
|
||||
except exceptions.DataError as e:
|
||||
log.warning("Invalid PUT request, we stop here: {e}")
|
||||
log.warning(f"Invalid PUT request, we stop here: {e}")
|
||||
return self.refuseRequest()
|
||||
try:
|
||||
client, upload_request, timer = self.file_sharing.expected_uploads.pop(upload_id)
|
||||
|
|
@ -343,9 +343,15 @@ class FileSharing:
|
|||
self.host.trigger.add(
|
||||
"XEP-0234_fileSendingRequest", self._fileSendingRequestTrigger, priority=1000
|
||||
)
|
||||
self.host.trigger.add("XEP-0234_buildFileElement", self._addFileComments)
|
||||
self.host.trigger.add("XEP-0234_parseFileElement", self._getFileComments)
|
||||
self.host.trigger.add("XEP-0329_compGetFilesFromNode", self._addCommentsData)
|
||||
self.host.trigger.add("XEP-0234_buildFileElement", self._addFileMetadataElts)
|
||||
self.host.trigger.add("XEP-0234_parseFileElement", self._getFileMetadataElts)
|
||||
self.host.trigger.add("XEP-0329_compGetFilesFromNode", self._addFileMetadata)
|
||||
self.host.trigger.add(
|
||||
"XEP-0329_compGetFilesFromNode_build_directory",
|
||||
self._addDirectoryMetadataElts)
|
||||
self.host.trigger.add(
|
||||
"XEP-0329_parseResult_directory",
|
||||
self._getDirectoryMetadataElts)
|
||||
self.files_path = self.host.getLocalPath(None, C.FILES_DIR, profile=False)
|
||||
self.http_port = self.host.memory.getConfig(
|
||||
'component file_sharing', 'http_upload_port', 8888)
|
||||
|
|
@ -353,19 +359,19 @@ class FileSharing:
|
|||
'component file_sharing', 'http_upload_connection_type', 'https')
|
||||
if connection_type not in ('http', 'https'):
|
||||
raise exceptions.ConfigError(
|
||||
f'bad http_upload_connection_type, you must use one of "http" or "https"'
|
||||
'bad http_upload_connection_type, you must use one of "http" or "https"'
|
||||
)
|
||||
self.server = FileSharingSite(self)
|
||||
self.expected_uploads = {}
|
||||
if connection_type == 'http':
|
||||
reactor.listenTCP(self.http_port, self.server)
|
||||
else:
|
||||
options = tls.getOptionsFromConfig(self.host.memory.config, "component file_sharing")
|
||||
options = tls.getOptionsFromConfig(
|
||||
self.host.memory.config, "component file_sharing")
|
||||
tls.TLSOptionsCheck(options)
|
||||
context_factory = tls.getTLSContextFactory(options)
|
||||
reactor.listenSSL(self.http_port, self.server, context_factory)
|
||||
|
||||
|
||||
def getHandler(self, client):
|
||||
return Comments_handler(self)
|
||||
|
||||
|
|
@ -581,9 +587,15 @@ class FileSharing:
|
|||
)
|
||||
return slot
|
||||
|
||||
## comments triggers ##
|
||||
## metadata triggers ##
|
||||
|
||||
def _addFileComments(self, file_elt, extra_args):
|
||||
def _addFileMetadataElts(self, client, file_elt, extra_args):
|
||||
# affiliation
|
||||
affiliation = extra_args.get('affiliation')
|
||||
if affiliation is not None:
|
||||
file_elt.addElement((NS_FS_AFFILIATION, "affiliation"), content=affiliation)
|
||||
|
||||
# comments
|
||||
try:
|
||||
comments_url = extra_args.pop("comments_url")
|
||||
except KeyError:
|
||||
|
|
@ -599,16 +611,27 @@ class FileSharing:
|
|||
comment_elt["count"] = str(count)
|
||||
return True
|
||||
|
||||
def _getFileComments(self, file_elt, file_data):
|
||||
def _getFileMetadataElts(self, client, file_elt, file_data):
|
||||
# affiliation
|
||||
try:
|
||||
affiliation_elt = next(file_elt.elements(NS_FS_AFFILIATION, "affiliation"))
|
||||
except StopIteration:
|
||||
pass
|
||||
else:
|
||||
file_data["affiliation"] = str(affiliation_elt)
|
||||
|
||||
# comments
|
||||
try:
|
||||
comments_elt = next(file_elt.elements(NS_COMMENTS, "comments"))
|
||||
except StopIteration:
|
||||
return
|
||||
file_data["comments_url"] = str(comments_elt)
|
||||
file_data["comments_count"] = comments_elt["count"]
|
||||
pass
|
||||
else:
|
||||
file_data["comments_url"] = str(comments_elt)
|
||||
file_data["comments_count"] = comments_elt["count"]
|
||||
return True
|
||||
|
||||
def _addCommentsData(self, client, iq_elt, owner, node_path, files_data):
|
||||
def _addFileMetadata(
|
||||
self, client, iq_elt, iq_result_elt, owner, node_path, files_data):
|
||||
for file_data in files_data:
|
||||
file_data["comments_url"] = uri.buildXMPPUri(
|
||||
"pubsub",
|
||||
|
|
@ -617,12 +640,30 @@ class FileSharing:
|
|||
)
|
||||
return True
|
||||
|
||||
def _addDirectoryMetadataElts(
|
||||
self, client, file_data, directory_elt, owner, node_path):
|
||||
affiliation = file_data.get('affiliation')
|
||||
if affiliation is not None:
|
||||
directory_elt.addElement(
|
||||
(NS_FS_AFFILIATION, "affiliation"),
|
||||
content=affiliation
|
||||
)
|
||||
|
||||
def _getDirectoryMetadataElts(
|
||||
self, client, elt, file_data):
|
||||
try:
|
||||
affiliation_elt = next(elt.elements((NS_FS_AFFILIATION, "affiliation")))
|
||||
except StopIteration:
|
||||
pass
|
||||
else:
|
||||
file_data['affiliation'] = str(affiliation_elt)
|
||||
|
||||
|
||||
class Comments_handler(pubsub.PubSubService):
|
||||
"""This class is a minimal Pubsub service handling virtual nodes for comments"""
|
||||
|
||||
def __init__(self, plugin_parent):
|
||||
super(Comments_handler, self).__init__() # PubsubVirtualResource())
|
||||
super(Comments_handler, self).__init__()
|
||||
self.host = plugin_parent.host
|
||||
self.plugin_parent = plugin_parent
|
||||
self.discoIdentity = {
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ class FileSharingInvitation(object):
|
|||
type_human = _("file sharing")
|
||||
log.info(_(
|
||||
'{profile} has received an invitation for a files repository ({type_human}) '
|
||||
'with namespace "{namespace}" at path [{path}]').format(
|
||||
'with namespace {namespace!r} at path [{path}]').format(
|
||||
profile=client.profile, type_human=type_human, namespace=namespace, path=path)
|
||||
)
|
||||
return defer.ensureDeferred(
|
||||
|
|
|
|||
|
|
@ -105,7 +105,8 @@ class XEP_0234(object):
|
|||
|
||||
# generic methods
|
||||
|
||||
def buildFileElement(self, name=None, file_hash=None, hash_algo=None, size=None,
|
||||
def buildFileElement(
|
||||
self, client, name=None, file_hash=None, hash_algo=None, size=None,
|
||||
mime_type=None, desc=None, modified=None, transfer_range=None, path=None,
|
||||
namespace=None, file_elt=None, **kwargs):
|
||||
"""Generate a <file> element with available metadata
|
||||
|
|
@ -113,17 +114,20 @@ class XEP_0234(object):
|
|||
@param file_hash(unicode, None): hash of the file
|
||||
empty string to set <hash-used/> element
|
||||
@param hash_algo(unicode, None): hash algorithm used
|
||||
if file_hash is None and hash_algo is set, a <hash-used/> element will be generated
|
||||
if file_hash is None and hash_algo is set, a <hash-used/> element will be
|
||||
generated
|
||||
@param transfer_range(Range, None): where transfer must start/stop
|
||||
@param modified(int, unicode, None): date of last modification
|
||||
0 to use current date
|
||||
int to use an unix timestamp
|
||||
else must be an unicode string which will be used as it (it must be an XMPP time)
|
||||
else must be an unicode string which will be used as it (it must be an XMPP
|
||||
time)
|
||||
@param file_elt(domish.Element, None): element to use
|
||||
None to create a new one
|
||||
@param **kwargs: data for plugin extension (ignored by default)
|
||||
@return (domish.Element): generated element
|
||||
@trigger XEP-0234_buildFileElement(file_elt, extra_args): can be used to extend elements to add
|
||||
@trigger XEP-0234_buildFileElement(file_elt, extra_args): can be used to extend
|
||||
elements to add
|
||||
"""
|
||||
if file_elt is None:
|
||||
file_elt = domish.Element((NS_JINGLE_FT, "file"))
|
||||
|
|
@ -159,13 +163,14 @@ class XEP_0234(object):
|
|||
file_elt.addChild(self._hash.buildHashElt(file_hash, hash_algo))
|
||||
elif hash_algo is not None:
|
||||
file_elt.addChild(self._hash.buildHashUsedElt(hash_algo))
|
||||
self.host.trigger.point("XEP-0234_buildFileElement", file_elt, extra_args=kwargs)
|
||||
self.host.trigger.point(
|
||||
"XEP-0234_buildFileElement", client, file_elt, extra_args=kwargs)
|
||||
if kwargs:
|
||||
for kw in kwargs:
|
||||
log.debug("ignored keyword: {}".format(kw))
|
||||
return file_elt
|
||||
|
||||
def buildFileElementFromDict(self, file_data, **kwargs):
|
||||
def buildFileElementFromDict(self, client, file_data, **kwargs):
|
||||
"""like buildFileElement but get values from a file_data dict
|
||||
|
||||
@param file_data(dict): metadata to use
|
||||
|
|
@ -174,10 +179,11 @@ class XEP_0234(object):
|
|||
if kwargs:
|
||||
file_data = file_data.copy()
|
||||
file_data.update(kwargs)
|
||||
return self.buildFileElement(**file_data)
|
||||
return self.buildFileElement(client, **file_data)
|
||||
|
||||
def parseFileElement(self, file_elt, file_data=None, given=False, parent_elt=None,
|
||||
keep_empty_range=False,):
|
||||
def parseFileElement(
|
||||
self, client, file_elt, file_data=None, given=False, parent_elt=None,
|
||||
keep_empty_range=False):
|
||||
"""Parse a <file> element and file dictionary accordingly
|
||||
|
||||
@param file_data(dict, None): dict where the data will be set
|
||||
|
|
@ -268,7 +274,7 @@ class XEP_0234(object):
|
|||
except exceptions.NotFound:
|
||||
pass
|
||||
|
||||
self.host.trigger.point("XEP-0234_parseFileElement", file_elt, file_data)
|
||||
self.host.trigger.point("XEP-0234_parseFileElement", client, file_elt, file_data)
|
||||
|
||||
return file_data
|
||||
|
||||
|
|
@ -423,7 +429,8 @@ class XEP_0234(object):
|
|||
file_data["namespace"] = extra["namespace"]
|
||||
if "path" in extra:
|
||||
file_data["path"] = extra["path"]
|
||||
self.buildFileElementFromDict(file_data, file_elt=file_elt, file_hash="")
|
||||
self.buildFileElementFromDict(
|
||||
client, file_data, file_elt=file_elt, file_hash="")
|
||||
else:
|
||||
# we request a file
|
||||
file_hash = extra.pop("file_hash", "")
|
||||
|
|
@ -440,7 +447,7 @@ class XEP_0234(object):
|
|||
file_data["namespace"] = extra["namespace"]
|
||||
if "path" in extra:
|
||||
file_data["path"] = extra["path"]
|
||||
self.buildFileElementFromDict(file_data, file_elt=file_elt)
|
||||
self.buildFileElementFromDict(client, file_data, file_elt=file_elt)
|
||||
|
||||
return desc_elt
|
||||
|
||||
|
|
@ -474,7 +481,7 @@ class XEP_0234(object):
|
|||
self, client, session, content_data, content_name, file_data, file_elt
|
||||
):
|
||||
"""parse file_elt, and handle file retrieving/permission checking"""
|
||||
self.parseFileElement(file_elt, file_data)
|
||||
self.parseFileElement(client, file_elt, file_data)
|
||||
content_data["application_data"]["file_data"] = file_data
|
||||
finished_d = content_data["finished_d"] = defer.Deferred()
|
||||
|
||||
|
|
@ -504,7 +511,7 @@ class XEP_0234(object):
|
|||
self, client, session, content_data, content_name, file_data, file_elt
|
||||
):
|
||||
"""parse file_elt, and handle user permission/file opening"""
|
||||
self.parseFileElement(file_elt, file_data, given=True)
|
||||
self.parseFileElement(client, file_elt, file_data, given=True)
|
||||
try:
|
||||
hash_algo, file_data["given_file_hash"] = self._hash.parseHashElt(file_elt)
|
||||
except exceptions.NotFound:
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ class XEP_0264(object):
|
|||
|
||||
## triggers ##
|
||||
|
||||
def _addFileThumbnails(self, file_elt, extra_args):
|
||||
def _addFileThumbnails(self, client, file_elt, extra_args):
|
||||
try:
|
||||
thumbnails = extra_args["extra"][C.KEY_THUMBNAILS]
|
||||
except KeyError:
|
||||
|
|
@ -98,7 +98,7 @@ class XEP_0264(object):
|
|||
thumbnail_elt["height"] = str(height)
|
||||
return True
|
||||
|
||||
def _getFileThumbnails(self, file_elt, file_data):
|
||||
def _getFileThumbnails(self, client, file_elt, file_data):
|
||||
thumbnails = []
|
||||
for thumbnail_elt in file_elt.elements(NS_THUMBS, "thumbnail"):
|
||||
uri = thumbnail_elt["uri"]
|
||||
|
|
|
|||
|
|
@ -417,7 +417,8 @@ class XEP_0329(object):
|
|||
size = os.path.getsize(path)
|
||||
mime_type = mimetypes.guess_type(path, strict=False)[0]
|
||||
file_elt = self._jf.buildFileElement(
|
||||
name=name, size=size, mime_type=mime_type, modified=os.path.getmtime(path)
|
||||
client=client, name=name, size=size, mime_type=mime_type,
|
||||
modified=os.path.getmtime(path)
|
||||
)
|
||||
|
||||
query_elt.addChild(file_elt)
|
||||
|
|
@ -590,16 +591,32 @@ class XEP_0329(object):
|
|||
query_elt = iq_result_elt.addElement((NS_FIS, "query"))
|
||||
query_elt["node"] = node_path
|
||||
if not self.host.trigger.point(
|
||||
"XEP-0329_compGetFilesFromNode", client, iq_elt, owner, node_path, files_data
|
||||
"XEP-0329_compGetFilesFromNode",
|
||||
client,
|
||||
iq_elt,
|
||||
iq_result_elt,
|
||||
owner,
|
||||
node_path,
|
||||
files_data
|
||||
):
|
||||
return
|
||||
for file_data in files_data:
|
||||
if file_data['type'] == C.FILE_TYPE_DIRECTORY:
|
||||
directory_elt = query_elt.addElement("directory")
|
||||
directory_elt['name'] = file_data['name']
|
||||
self.host.trigger.point(
|
||||
"XEP-0329_compGetFilesFromNode_build_directory",
|
||||
client,
|
||||
file_data,
|
||||
directory_elt,
|
||||
owner,
|
||||
node_path,
|
||||
)
|
||||
else:
|
||||
file_elt = self._jf.buildFileElementFromDict(
|
||||
file_data, modified=file_data.get("modified", file_data["created"])
|
||||
client,
|
||||
file_data,
|
||||
modified=file_data.get("modified", file_data["created"])
|
||||
)
|
||||
query_elt.addChild(file_elt)
|
||||
client.send(iq_result_elt)
|
||||
|
|
@ -609,7 +626,7 @@ class XEP_0329(object):
|
|||
client, iq_elt, self._compGetRootNodesCb, self._compGetFilesFromNodeCb
|
||||
)
|
||||
|
||||
def _parseResult(self, iq_elt):
|
||||
def _parseResult(self, iq_elt, client):
|
||||
query_elt = next(iq_elt.elements(NS_FIS, "query"))
|
||||
files = []
|
||||
|
||||
|
|
@ -617,7 +634,7 @@ class XEP_0329(object):
|
|||
if elt.name == "file":
|
||||
# we have a file
|
||||
try:
|
||||
file_data = self._jf.parseFileElement(elt)
|
||||
file_data = self._jf.parseFileElement(client, elt)
|
||||
except exceptions.DataError:
|
||||
continue
|
||||
file_data["type"] = C.FILE_TYPE_FILE
|
||||
|
|
@ -625,6 +642,12 @@ class XEP_0329(object):
|
|||
# we have a directory
|
||||
|
||||
file_data = {"name": elt["name"], "type": C.FILE_TYPE_DIRECTORY}
|
||||
self.host.trigger.point(
|
||||
"XEP-0329_parseResult_directory",
|
||||
client,
|
||||
elt,
|
||||
file_data,
|
||||
)
|
||||
else:
|
||||
log.warning(
|
||||
_(f"unexpected element, ignoring: {elt.toXml()}")
|
||||
|
|
@ -665,7 +688,7 @@ class XEP_0329(object):
|
|||
if path:
|
||||
query_elt["node"] = path
|
||||
d = iq_elt.send()
|
||||
d.addCallback(self._parseResult)
|
||||
d.addCallback(self._parseResult, client)
|
||||
return d
|
||||
|
||||
def _localSharesGet(self, profile):
|
||||
|
|
|
|||
Loading…
Reference in a new issue