Mercurial > libervia-backend
comparison src/plugins/plugin_misc_file.py @ 1585:846a39900fa6
plugins XEP-0096, XEP-0260, file: sendFile method is managed by file plugin, which choose the best available method + progress_id fix
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 14 Nov 2015 19:18:05 +0100 |
parents | 833bdb227b16 |
children | b144babc2658 |
comparison
equal
deleted
inserted
replaced
1584:b57b4683dc33 | 1585:846a39900fa6 |
---|---|
19 | 19 |
20 from sat.core.i18n import _, D_ | 20 from sat.core.i18n import _, D_ |
21 from sat.core.constants import Const as C | 21 from sat.core.constants import Const as C |
22 from sat.core.log import getLogger | 22 from sat.core.log import getLogger |
23 log = getLogger(__name__) | 23 log = getLogger(__name__) |
24 from sat.core import exceptions | |
24 from sat.tools import xml_tools | 25 from sat.tools import xml_tools |
25 from twisted.internet import defer | 26 from twisted.internet import defer |
27 from twisted.words.protocols.jabber import jid | |
26 import os | 28 import os |
29 import os.path | |
27 import uuid | 30 import uuid |
28 | 31 |
29 | 32 |
30 PLUGIN_INFO = { | 33 PLUGIN_INFO = { |
31 "name": "File Tansfer", | 34 "name": "File Tansfer", |
41 CONFIRM = D_(u'{peer} wants to send the file "{name}" to you:\n{desc}\n\nThe file has a size of {size_human}\n\nDo you accept ?') | 44 CONFIRM = D_(u'{peer} wants to send the file "{name}" to you:\n{desc}\n\nThe file has a size of {size_human}\n\nDo you accept ?') |
42 CONFIRM_TITLE = D_(u'Confirm file transfer') | 45 CONFIRM_TITLE = D_(u'Confirm file transfer') |
43 CONFIRM_OVERWRITE = D_(u'File {} already exists, are you sure you want to overwrite ?') | 46 CONFIRM_OVERWRITE = D_(u'File {} already exists, are you sure you want to overwrite ?') |
44 CONFIRM_OVERWRITE_TITLE = D_(u'File exists') | 47 CONFIRM_OVERWRITE_TITLE = D_(u'File exists') |
45 | 48 |
49 PROGRESS_ID_KEY = 'progress_id' | |
50 | |
46 | 51 |
47 class SatFile(object): | 52 class SatFile(object): |
48 """A file-like object to have high level files manipulation""" | 53 """A file-like object to have high level files manipulation""" |
49 # TODO: manage "with" statement | 54 # TODO: manage "with" statement |
50 | 55 |
52 """ | 57 """ |
53 @param host: %(doc_host)s | 58 @param host: %(doc_host)s |
54 @param path(str): path of the file to get | 59 @param path(str): path of the file to get |
55 @param mode(str): same as for built-in "open" function | 60 @param mode(str): same as for built-in "open" function |
56 @param uid(unicode, None): unique id identifing this progressing element | 61 @param uid(unicode, None): unique id identifing this progressing element |
62 This uid will be used with self.host.progressGet | |
57 will be automaticaly generated if None | 63 will be automaticaly generated if None |
58 @param size(None, int): size of the file | 64 @param size(None, int): size of the file |
59 """ | 65 """ |
60 self.host = host | 66 self.host = host |
61 self.uid = uid or unicode(uuid.uuid4()) | 67 self.uid = uid or unicode(uuid.uuid4()) |
98 File=SatFile | 104 File=SatFile |
99 | 105 |
100 def __init__(self, host): | 106 def __init__(self, host): |
101 log.info(_("plugin File initialization")) | 107 log.info(_("plugin File initialization")) |
102 self.host = host | 108 self.host = host |
109 host.bridge.addMethod("fileSend", ".plugin", in_sign='sssss', out_sign='a{ss}', method=self._fileSend, async=True) | |
110 self._file_callbacks = [] | |
111 | |
112 def _fileSend(self, peer_jid_s, filepath, name="", file_desc="", profile=C.PROF_KEY_NONE): | |
113 return self.fileSend(jid.JID(peer_jid_s), filepath, name or None, file_desc or None, profile) | |
114 | |
115 @defer.inlineCallbacks | |
116 def fileSend(self, peer_jid, filepath, filename=None, file_desc=None, profile=C.PROF_KEY_NONE): | |
117 """Send a file using best available method | |
118 | |
119 @param peer_jid(jid.JID): jid of the destinee | |
120 @param filepath(str): absolute path to the file | |
121 @param filename(unicode, None): name to use, or None to find it from filepath | |
122 @param file_desc(unicode, None): description of the file | |
123 @param profile: %(doc_profile)s | |
124 @return (dict): action dictionary, with progress id in case of success, else xmlui message | |
125 """ | |
126 if not os.path.isfile(filepath): | |
127 raise exceptions.DataError(u"The given path doesn't link to a file") | |
128 if not filename: | |
129 filename = os.path.basename(filepath) or '_' | |
130 for namespace, callback, priority, method_name in self._file_callbacks: | |
131 has_feature = yield self.host.hasFeature(namespace, peer_jid, profile) | |
132 if has_feature: | |
133 log.info(u"{name} method will be used to send the file".format(name=method_name)) | |
134 progress_id = yield defer.maybeDeferred(callback, peer_jid, filepath, filename, file_desc, profile) | |
135 defer.returnValue({'progress': progress_id}) | |
136 msg = u"Can't find any method to send file to {jid}".format(jid=peer_jid.full()) | |
137 log.warning(msg) | |
138 defer.returnValue({'xmlui': xml_tools.note(u"Can't transfer file", msg, C.XMLUI_DATA_LVL_WARNING).toXml()}) | |
139 | |
140 def register(self, namespace, callback, priority=0, method_name=None): | |
141 """Register a fileSending method | |
142 | |
143 @param namespace(unicode): XEP namespace | |
144 @param callback(callable): method to call (must have the same signature as [fileSend]) | |
145 @param priority(int): pririoty of this method, the higher available will be used | |
146 @param method_name(unicode): short name for the method, namespace will be used if None | |
147 """ | |
148 for data in self._file_callbacks: | |
149 if namespace == data[0]: | |
150 raise exceptions.ConflictError(u'A method with this namespace is already registered') | |
151 self._file_callbacks.append((namespace, callback, priority, method_name or namespace)) | |
152 self._file_callbacks.sort(key=lambda data: data[2], reverse=True) | |
153 | |
154 def unregister(self, namespace): | |
155 for idx, data in enumerate(self._file_callbacks): | |
156 if data[0] == namespace: | |
157 del [idx] | |
158 return | |
159 raise exceptions.NotFound(u"The namespace to unregister doesn't exist") | |
103 | 160 |
104 # Dialogs with user | 161 # Dialogs with user |
105 # the overwrite check is done here | 162 # the overwrite check is done here |
106 | 163 |
107 def _openFileWrite(self, file_path, transfer_data, file_data, profile): | 164 def _openFileWrite(self, file_path, transfer_data, file_data, profile): |
108 assert 'file_obj' not in transfer_data | 165 assert 'file_obj' not in transfer_data |
109 transfer_data['file_obj'] = SatFile( | 166 transfer_data['file_obj'] = SatFile( |
110 self.host, | 167 self.host, |
111 file_path, | 168 file_path, |
112 'w', | 169 'w', |
170 uid=file_data[PROGRESS_ID_KEY], | |
113 size=file_data['size'], | 171 size=file_data['size'], |
114 profile=profile, | 172 profile=profile, |
115 ) | 173 ) |
116 | 174 |
117 def _gotConfirmation(self, data, peer_jid, transfer_data, file_data, profile): | 175 def _gotConfirmation(self, data, peer_jid, transfer_data, file_data, profile): |
165 It MUST contain the following keys: | 223 It MUST contain the following keys: |
166 - peer_jid (jid.JID): other peer jid | 224 - peer_jid (jid.JID): other peer jid |
167 - name (unicode): name of the file to trasnsfer | 225 - name (unicode): name of the file to trasnsfer |
168 the name must not be empty or contain a "/" character | 226 the name must not be empty or contain a "/" character |
169 - size (int): size of the file | 227 - size (int): size of the file |
228 - progress_id (unicode): id to use for progression | |
170 It may content the key used in CONFIRM constant | 229 It may content the key used in CONFIRM constant |
171 It *MUST NOT* contain the "peer" key | 230 It *MUST NOT* contain the "peer" key |
172 "file_path" will be added to this dict once destination selected | 231 "file_path" will be added to this dict once destination selected |
173 "size_human" will also be added with human readable file size | 232 "size_human" will also be added with human readable file size |
174 @param profile: %(doc_profile)s | 233 @param profile: %(doc_profile)s |
175 return (defer.Deferred): True if transfer is accepted | 234 return (defer.Deferred): True if transfer is accepted |
176 """ | 235 """ |
177 filename = file_data['name'] | 236 filename = file_data['name'] |
178 assert filename and not '/' in filename | 237 assert filename and not '/' in filename |
238 assert PROGRESS_ID_KEY in file_data | |
179 # human readable size | 239 # human readable size |
180 file_data['size_human'] = u'{:.6n} Mio'.format(float(file_data['size'])/(1024**2)) | 240 file_data['size_human'] = u'{:.6n} Mio'.format(float(file_data['size'])/(1024**2)) |
181 d = xml_tools.deferDialog(self.host, | 241 d = xml_tools.deferDialog(self.host, |
182 _(CONFIRM).format(peer=peer_jid.full(), **file_data), | 242 _(CONFIRM).format(peer=peer_jid.full(), **file_data), |
183 _(CONFIRM_TITLE), | 243 _(CONFIRM_TITLE), |