plugin file, XEP-0234: registering is now done by class + use of async:
- instead of registering a callback, a file sending manager now register itself and must implement some well known method (`fileSend`, `canHandleFileSend`) and optionally a `name` attribute - `utils.asDeferred` is now used for callbacks, so all type of methods including coroutines can be used. - feature checking is now handled by `canHandleFileSend` method instead of simple namespace check, this allows to use a method when namespace can't be checked (this is the case when a file is sent to a bare jid with jingle)
This commit is contained in:
parent
afacc8d4c1
commit
46c763cbf7
1 changed files with 102 additions and 79 deletions
|
|
@ -17,18 +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 os
|
||||
import os.path
|
||||
from functools import partial
|
||||
from twisted.internet import defer
|
||||
from twisted.words.protocols.jabber import jid
|
||||
from sat.core.i18n import _, D_
|
||||
from sat.core.constants import Const as C
|
||||
from sat.core.log import getLogger
|
||||
|
||||
log = getLogger(__name__)
|
||||
from sat.core import exceptions
|
||||
from sat.tools import xml_tools
|
||||
from sat.tools import stream
|
||||
from twisted.internet import defer
|
||||
from twisted.words.protocols.jabber import jid
|
||||
import os
|
||||
import os.path
|
||||
from sat.tools import utils
|
||||
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
||||
|
||||
PLUGIN_INFO = {
|
||||
|
|
@ -73,7 +76,7 @@ class FilePlugin:
|
|||
method=self._fileSend,
|
||||
async_=True,
|
||||
)
|
||||
self._file_callbacks = []
|
||||
self._file_managers = []
|
||||
host.importMenu(
|
||||
(D_("Action"), D_("send file")),
|
||||
self._fileSendMenu,
|
||||
|
|
@ -85,12 +88,11 @@ class FilePlugin:
|
|||
def _fileSend(self, peer_jid_s, filepath, name="", file_desc="", extra=None,
|
||||
profile=C.PROF_KEY_NONE):
|
||||
client = self.host.getClient(profile)
|
||||
return self.fileSend(
|
||||
return defer.ensureDeferred(self.fileSend(
|
||||
client, jid.JID(peer_jid_s), filepath, name or None, file_desc or None, extra
|
||||
)
|
||||
))
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def fileSend(
|
||||
async def fileSend(
|
||||
self, client, peer_jid, filepath, filename=None, file_desc=None, extra=None
|
||||
):
|
||||
"""Send a file using best available method
|
||||
|
|
@ -107,29 +109,45 @@ class FilePlugin:
|
|||
raise exceptions.DataError("The given path doesn't link to a file")
|
||||
if not filename:
|
||||
filename = os.path.basename(filepath) or "_"
|
||||
for namespace, callback, priority, method_name in self._file_callbacks:
|
||||
has_feature = yield self.host.hasFeature(client, namespace, peer_jid)
|
||||
if has_feature:
|
||||
for manager, priority in self._file_managers:
|
||||
if await utils.asDeferred(manager.canHandleFileSend,
|
||||
client, peer_jid, filepath):
|
||||
try:
|
||||
method_name = manager.name
|
||||
except AttributeError:
|
||||
method_name = manager.__class__.__name__
|
||||
log.info(
|
||||
"{name} method will be used to send the file".format(
|
||||
_("{name} method will be used to send the file").format(
|
||||
name=method_name
|
||||
)
|
||||
)
|
||||
progress_id = yield callback(
|
||||
client, peer_jid, filepath, filename, file_desc, extra
|
||||
)
|
||||
defer.returnValue({"progress": progress_id})
|
||||
try:
|
||||
progress_id = await utils.asDeferred(
|
||||
manager.fileSend, client, peer_jid, filepath, filename, file_desc,
|
||||
extra
|
||||
)
|
||||
except Exception as e:
|
||||
log.warning(
|
||||
_("Can't send {filepath} to {peer_jid} with {method_name}: "
|
||||
"{reason}").format(
|
||||
filepath=filepath,
|
||||
peer_jid=peer_jid,
|
||||
method_name=method_name,
|
||||
reason=e
|
||||
)
|
||||
)
|
||||
continue
|
||||
return {"progress": progress_id}
|
||||
msg = "Can't find any method to send file to {jid}".format(jid=peer_jid.full())
|
||||
log.warning(msg)
|
||||
defer.returnValue(
|
||||
{
|
||||
"xmlui": xml_tools.note(
|
||||
"Can't transfer file", msg, C.XMLUI_DATA_LVL_WARNING
|
||||
).toXml()
|
||||
}
|
||||
)
|
||||
return {
|
||||
"xmlui": xml_tools.note(
|
||||
"Can't transfer file", msg, C.XMLUI_DATA_LVL_WARNING
|
||||
).toXml()
|
||||
}
|
||||
|
||||
def _onFileChoosed(self, client, peer_jid, data):
|
||||
def _onFileChoosed(self, peer_jid, data, profile):
|
||||
client = self.host.getClient(profile)
|
||||
cancelled = C.bool(data.get("cancelled", C.BOOL_FALSE))
|
||||
if cancelled:
|
||||
return
|
||||
|
|
@ -147,9 +165,7 @@ class FilePlugin:
|
|||
raise exceptions.DataError(_("Invalid JID"))
|
||||
|
||||
file_choosed_id = self.host.registerCallback(
|
||||
lambda data, profile: self._onFileChoosed(
|
||||
self.host.getClient(profile), jid_, data
|
||||
),
|
||||
partial(self._onFileChoosed, jid_),
|
||||
with_data=True,
|
||||
one_shot=True,
|
||||
)
|
||||
|
|
@ -165,30 +181,31 @@ class FilePlugin:
|
|||
|
||||
return {"xmlui": xml_ui.toXml()}
|
||||
|
||||
def register(self, namespace, callback, priority=0, method_name=None):
|
||||
"""Register a fileSending method
|
||||
def register(self, manager, priority: int = 0) -> None:
|
||||
"""Register a fileSending manager
|
||||
|
||||
@param namespace(unicode): XEP namespace
|
||||
@param callback(callable): method to call (must have the same signature as [fileSend])
|
||||
@param priority(int): pririoty of this method, the higher available will be used
|
||||
@param method_name(unicode): short name for the method, namespace will be used if None
|
||||
@param manager: object implementing canHandleFileSend, and fileSend methods
|
||||
@param priority: pririoty of this manager, the higher available will be used
|
||||
"""
|
||||
for data in self._file_callbacks:
|
||||
if namespace == data[0]:
|
||||
raise exceptions.ConflictError(
|
||||
"A method with this namespace is already registered"
|
||||
)
|
||||
self._file_callbacks.append(
|
||||
(namespace, callback, priority, method_name or namespace)
|
||||
)
|
||||
self._file_callbacks.sort(key=lambda data: data[2], reverse=True)
|
||||
m_data = (manager, priority)
|
||||
if m_data in self._file_managers:
|
||||
raise exceptions.ConflictError(
|
||||
f"Manager {manager} is already registered"
|
||||
)
|
||||
if not hasattr(manager, "canHandleFileSend") or not hasattr(manager, "fileSend"):
|
||||
raise ValueError(
|
||||
f'{manager} must have both "canHandleFileSend" and "fileSend" methods to '
|
||||
'be registered')
|
||||
self._file_managers.append(m_data)
|
||||
self._file_managers.sort(key=lambda m: m[1], reverse=True)
|
||||
|
||||
def unregister(self, namespace):
|
||||
for idx, data in enumerate(self._file_callbacks):
|
||||
if data[0] == namespace:
|
||||
del [idx]
|
||||
return
|
||||
raise exceptions.NotFound("The namespace to unregister doesn't exist")
|
||||
def unregister(self, manager):
|
||||
for idx, data in enumerate(self._file_managers):
|
||||
if data[0] == manager:
|
||||
break
|
||||
else:
|
||||
raise exceptions.NotFound("The file manager {manager} is not registered")
|
||||
del self._file_managers[idx]
|
||||
|
||||
# Dialogs with user
|
||||
# the overwrite check is done here
|
||||
|
|
|
|||
Loading…
Reference in a new issue