Removed unneeded modules
This commit is contained in:
		| @@ -1,186 +0,0 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| ############################################################ | ||||
| #    Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net> | ||||
| #        | ||||
| #    This program is free software: you can redistribute it and/or modify | ||||
| #    it under the terms of the GNU General Public License as published by | ||||
| #    the Free Software Foundation, either version 2 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 General Public License for more details. | ||||
| # | ||||
| #    You should have received a copy of the GNU General Public License | ||||
| #    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| ############################################################ | ||||
| import widgetUtils | ||||
| import wx_ui | ||||
| import wx_transfer_dialogs | ||||
| import transfer | ||||
| import output | ||||
| import tempfile | ||||
| import sound | ||||
| import os | ||||
| import config | ||||
| from pubsub import pub | ||||
| from mysc.thread_utils import call_threaded | ||||
| import sound_lib | ||||
| import logging | ||||
|  | ||||
| log = logging.getLogger("extra.AudioUploader.audioUploader") | ||||
|  | ||||
| class audioUploader(object): | ||||
|  def __init__(self, configFile, completed_callback): | ||||
|   self.config = configFile | ||||
|   super(audioUploader, self).__init__() | ||||
|   self.dialog = wx_ui.audioDialog(services=self.get_available_services()) | ||||
|   self.file = None | ||||
|   self.recorded = False | ||||
|   self.recording = None | ||||
|   self.playing = None | ||||
|   widgetUtils.connect_event(self.dialog.play, widgetUtils.BUTTON_PRESSED, self.on_play) | ||||
|   widgetUtils.connect_event(self.dialog.pause, widgetUtils.BUTTON_PRESSED, self.on_pause) | ||||
|   widgetUtils.connect_event(self.dialog.record, widgetUtils.BUTTON_PRESSED, self.on_record) | ||||
|   widgetUtils.connect_event(self.dialog.attach_exists, widgetUtils.BUTTON_PRESSED, self.on_attach_exists) | ||||
|   widgetUtils.connect_event(self.dialog.discard, widgetUtils.BUTTON_PRESSED, self.on_discard) | ||||
|   if self.dialog.get_response() == widgetUtils.OK: | ||||
|    self.postprocess() | ||||
|    log.debug("Uploading file %s to %s..." % (self.file, self.dialog.get("services"))) | ||||
|    self.uploaderDialog = wx_transfer_dialogs.UploadDialog(self.file) | ||||
|    output.speak(_(u"Attaching...")) | ||||
|    if self.dialog.get("services") == "SNDUp": | ||||
|     base_url = "http://sndup.net/post.php" | ||||
|     if len(self.config["sound"]["sndup_api_key"]) > 0: | ||||
|      url = base_url + '?apikey=' + self.config['sound']['sndup_api_key'] | ||||
|     else: | ||||
|      url = base_url | ||||
|     self.uploaderFunction = transfer.Upload(field='file', url=url, filename=self.file, completed_callback=completed_callback) | ||||
|    elif self.dialog.get("services") == "TwUp": | ||||
|     url = "http://api.twup.me/post.json" | ||||
|     self.uploaderFunction = transfer.Upload(field='file', url=url, filename=self.file, completed_callback=completed_callback) | ||||
|    pub.subscribe(self.uploaderDialog.update, "uploading") | ||||
|    self.uploaderDialog.get_response() | ||||
|    self.uploaderFunction.perform_threaded() | ||||
|  | ||||
|  def get_available_services(self): | ||||
|   services = [] | ||||
|   services.append("TwUp") | ||||
|   services.append("SNDUp") | ||||
|   return services | ||||
|  | ||||
|  def on_pause(self, *args, **kwargs): | ||||
|   if self.dialog.get("pause") == _(u"Pause"): | ||||
|    self.recording.pause() | ||||
|    self.dialog.set("pause", _(u"&Resume")) | ||||
|   elif self.dialog.get("pause") == _(u"Resume"): | ||||
|    self.recording.play() | ||||
|    self.dialog.set("pause", _(U"&Pause")) | ||||
|  | ||||
|  def on_record(self, *args, **kwargs): | ||||
|   if self.recording != None: | ||||
|    self.stop_recording() | ||||
|    self.dialog.disable_control("pause") | ||||
|   else: | ||||
|    self.start_recording() | ||||
|    self.dialog.enable_control("pause") | ||||
|  | ||||
|  def start_recording(self): | ||||
|   self.dialog.disable_control("attach_exists") | ||||
|   self.file = tempfile.mktemp(suffix='.wav') | ||||
|   self.recording = sound.recording(self.file) | ||||
|   self.recording.play() | ||||
|   self.dialog.set("record", _(u"&Stop")) | ||||
|   output.speak(_(u"Recording")) | ||||
|  | ||||
|  def stop_recording(self): | ||||
|   self.recording.stop() | ||||
|   self.recording.free() | ||||
|   output.speak(_(u"Stopped")) | ||||
|   self.recorded = True | ||||
|   self.dialog.set("record", _(u"&Record")) | ||||
|   self.file_attached() | ||||
|  | ||||
|  def file_attached(self): | ||||
|   self.dialog.set("pause", _(u"&Pause")) | ||||
|   self.dialog.disable_control("record") | ||||
|   self.dialog.enable_control("play") | ||||
|   self.dialog.enable_control("discard") | ||||
|   self.dialog.disable_control("attach_exists") | ||||
|   self.dialog.enable_control("attach") | ||||
|   self.dialog.play.SetFocus() | ||||
|  | ||||
|  def on_discard(self, *args, **kwargs): | ||||
|   if self.playing: | ||||
|    self._stop() | ||||
|   if self.recording != None: | ||||
|    self.dialog.disable_control("attach") | ||||
|    self.dialog.disable_control("play") | ||||
|   self.file = None | ||||
|   self.dialog.enable_control("record") | ||||
|   self.dialog.enable_control("attach_exists") | ||||
|   self.dialog.record.SetFocus() | ||||
|   self.dialog.disable_control("discard") | ||||
|   self.recording = None | ||||
|   output.speak(_(u"Discarded")) | ||||
|  | ||||
|  def on_play(self, *args, **kwargs): | ||||
|   if not self.playing: | ||||
|    call_threaded(self._play) | ||||
|   else: | ||||
|    self._stop() | ||||
|  | ||||
|  def _play(self): | ||||
|   output.speak(_(u"Playing...")) | ||||
| #  try: | ||||
|   self.playing = sound_lib.stream.FileStream(file=unicode(self.file), flags=sound_lib.stream.BASS_UNICODE) | ||||
|   self.playing.play() | ||||
|   self.dialog.set("play", _(u"&Stop")) | ||||
|   try: | ||||
|    while self.playing.is_playing: | ||||
|     pass | ||||
|    self.dialog.set("play", _(u"&Play")) | ||||
|    self.playing.free() | ||||
|    self.playing = None | ||||
|   except: | ||||
|    pass | ||||
|  | ||||
|  def _stop(self): | ||||
|   output.speak(_(u"Stopped")) | ||||
|   self.playing.stop() | ||||
|   self.playing.free() | ||||
|   self.dialog.set("play", _(u"&Play")) | ||||
|   self.playing = None | ||||
|  | ||||
|  def postprocess(self): | ||||
|   if self.file.lower().endswith('.wav'): | ||||
|    output.speak(_(u"Recoding audio...")) | ||||
|    sound.recode_audio(self.file) | ||||
|    self.wav_file = self.file | ||||
|    self.file = '%s.ogg' % self.file[:-4] | ||||
|  | ||||
|  def cleanup(self): | ||||
|   if self.playing and self.playing.is_playing: | ||||
|    self.playing.stop() | ||||
|   if self.recording != None: | ||||
|    if self.recording.is_playing: | ||||
|     self.recording.stop() | ||||
|    try: | ||||
|     self.recording.free() | ||||
|    except: | ||||
|     pass | ||||
|    os.remove(self.file) | ||||
|    if hasattr(self, 'wav_file'): | ||||
|     os.remove(self.wav_file) | ||||
|     del(self.wav_file) | ||||
|   if hasattr(self, 'wav_file') and os.path.exists(self.file): | ||||
|    os.remove(self.file) | ||||
|  | ||||
|  | ||||
|  def on_attach_exists(self, *args, **kwargs): | ||||
|   self.file = self.dialog.get_file() | ||||
|   if self.file != False: | ||||
|    self.file_attached() | ||||
|  | ||||
| @@ -1,106 +0,0 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import pycurl | ||||
| import sys | ||||
| import threading | ||||
| import time | ||||
| import json | ||||
| import logging | ||||
| from utils import * | ||||
| from pubsub import pub | ||||
|  | ||||
| log = logging.getLogger("extra.AudioUploader.transfer") | ||||
| class Transfer(object): | ||||
|  | ||||
|  def __init__(self, url=None, filename=None, follow_location=True, completed_callback=None, verbose=False, *args, **kwargs): | ||||
|   self.url = url | ||||
|   self.filename = filename | ||||
|   log.debug("Uploading audio to %s, filename %s" % (url, filename)) | ||||
|   self.curl = pycurl.Curl() | ||||
|   self.start_time = None | ||||
|   self.completed_callback = completed_callback | ||||
|   self.background_thread = None | ||||
|   self.transfer_rate = 0 | ||||
|   self.curl.setopt(self.curl.PROGRESSFUNCTION, self.progress_callback) | ||||
|   self.curl.setopt(self.curl.URL, url) | ||||
|   self.curl.setopt(self.curl.NOPROGRESS, 0) | ||||
|   self.curl.setopt(self.curl.HTTP_VERSION, self.curl.CURL_HTTP_VERSION_1_0) | ||||
|   self.curl.setopt(self.curl.FOLLOWLOCATION, int(follow_location)) | ||||
|   self.curl.setopt(self.curl.VERBOSE, int(verbose)) | ||||
|   super(Transfer, self).__init__(*args, **kwargs) | ||||
|  | ||||
|  def elapsed_time(self): | ||||
|   if not self.start_time: | ||||
|    return 0 | ||||
|   return time.time() - self.start_time | ||||
|  | ||||
|  def progress_callback(self, down_total, down_current, up_total, up_current): | ||||
|   progress = {} | ||||
|   progress["total"] = up_total | ||||
|   progress["current"] = up_current | ||||
| #  else: | ||||
| #   print "Killed function" | ||||
| #   return | ||||
|   if progress["current"] == 0: | ||||
|    progress["percent"] = 0 | ||||
|    self.transfer_rate = 0 | ||||
|   else: | ||||
|    progress["percent"] = int((float(progress["current"]) / progress["total"]) * 100) | ||||
|    self.transfer_rate = progress["current"] / self.elapsed_time() | ||||
|   progress["speed"] = '%s/s' % convert_bytes(self.transfer_rate) | ||||
|   if self.transfer_rate: | ||||
|    progress["eta"] = (progress["total"] - progress["current"]) / self.transfer_rate | ||||
|   else: | ||||
|    progress["eta"] = 0 | ||||
|   pub.sendMessage("uploading", data=progress) | ||||
|  | ||||
|  def perform_transfer(self): | ||||
|   log.debug("starting upload...") | ||||
|   self.start_time = time.time() | ||||
|   self.curl.perform() | ||||
|   self.curl.close() | ||||
|   log.debug("Upload finished.") | ||||
|   self.complete_transfer() | ||||
|  | ||||
|  def perform_threaded(self): | ||||
|   self.background_thread = threading.Thread(target=self.perform_transfer) | ||||
|   self.background_thread.daemon = True | ||||
|   self.background_thread.start() | ||||
|  | ||||
|  def complete_transfer(self): | ||||
|   if callable(self.completed_callback): | ||||
|    self.curl.close() | ||||
|    self.completed_callback() | ||||
|  | ||||
| class Upload(Transfer): | ||||
|  | ||||
|  def __init__(self, field=None, filename=None, *args, **kwargs): | ||||
|   super(Upload, self).__init__(filename=filename, *args, **kwargs) | ||||
|   self.response = dict() | ||||
|   self.curl.setopt(self.curl.POST, 1) | ||||
|   if isinstance(filename, unicode): | ||||
|    local_filename = filename.encode(sys.getfilesystemencoding()) | ||||
|   else: | ||||
|    local_filename = filename | ||||
|   self.curl.setopt(self.curl.HTTPPOST, [(field, (self.curl.FORM_FILE, local_filename, self.curl.FORM_FILENAME, filename.encode("utf-8")))]) | ||||
|   self.curl.setopt(self.curl.HEADERFUNCTION, self.header_callback) | ||||
|   self.curl.setopt(self.curl.WRITEFUNCTION, self.body_callback) | ||||
|  | ||||
|  def header_callback(self, content): | ||||
|   self.response['header'] = content | ||||
|  | ||||
|  def body_callback(self, content): | ||||
|   self.response['body'] = content | ||||
|  | ||||
|  def get_url(self): | ||||
|   return json.loads(self.response['body'])['url'] | ||||
|  | ||||
| class Download(Transfer): | ||||
|  | ||||
|  def __init__(self, follow_location=True, *args, **kwargs): | ||||
|   super(Download, self).__init__(*args, **kwargs) | ||||
|   self.download_file = open(self.filename, 'wb') | ||||
|   self.curl.setopt(self.curl.WRITEFUNCTION, self.download_file.write) | ||||
|  | ||||
|  def complete_transfer(self): | ||||
|   self.download_file.close() | ||||
|   super(DownloadDialog, self).complete_transfer() | ||||
| @@ -1,42 +0,0 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| def convert_bytes(n): | ||||
|  K, M, G, T, P = 1 << 10, 1 << 20, 1 << 30, 1 << 40, 1 << 50 | ||||
|  if   n >= P: | ||||
|   return '%.2fPb' % (float(n) / T) | ||||
|  elif   n >= T: | ||||
|   return '%.2fTb' % (float(n) / T) | ||||
|  elif n >= G: | ||||
|   return '%.2fGb' % (float(n) / G) | ||||
|  elif n >= M: | ||||
|   return '%.2fMb' % (float(n) / M) | ||||
|  elif n >= K: | ||||
|   return '%.2fKb' % (float(n) / K) | ||||
|  else: | ||||
|   return '%d' % n | ||||
|  | ||||
| def seconds_to_string(seconds, precision=0): | ||||
|  day = seconds // 86400 | ||||
|  hour = seconds // 3600 | ||||
|  min = (seconds // 60) % 60 | ||||
|  sec = seconds - (hour * 3600) - (min * 60) | ||||
|  sec_spec = "." + str(precision) + "f" | ||||
|  sec_string = sec.__format__(sec_spec) | ||||
|  string = "" | ||||
|  if day == 1: | ||||
|   string += _(u"%d day, ") % day | ||||
|  elif day >= 2: | ||||
|   string += _(u"%d days, ") % day | ||||
|  if (hour == 1): | ||||
|   string += _(u"%d hour, ") % hour | ||||
|  elif (hour >= 2): | ||||
|   string += _("%d hours, ") % hour | ||||
|  if (min == 1): | ||||
|   string += _(u"%d minute, ") % min | ||||
|  elif (min >= 2): | ||||
|   string += _(u"%d minutes, ") % min | ||||
|  if sec >= 0 and sec <= 2: | ||||
|   string += _(u"%s second") % sec_string | ||||
|  else: | ||||
|   string += _(u"%s seconds") % sec_string | ||||
|  return string | ||||
| @@ -1,73 +0,0 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import wx | ||||
| from utils import * | ||||
| import widgetUtils | ||||
|  | ||||
| class TransferDialog(widgetUtils.BaseDialog): | ||||
|  | ||||
|  def __init__(self, filename, *args, **kwargs): | ||||
|   super(TransferDialog, self).__init__(parent=None, id=wx.NewId(), *args, **kwargs) | ||||
|   self.pane = wx.Panel(self) | ||||
|   self.progress_bar = wx.Gauge(parent=self.pane) | ||||
|   fileBox = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   fileLabel = wx.StaticText(self.pane, -1, _(u"File")) | ||||
|   self.file = wx.TextCtrl(self.pane, -1, value=filename, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(200, 100)) | ||||
|   self.file.SetFocus() | ||||
|   fileBox.Add(fileLabel) | ||||
|   fileBox.Add(self.file) | ||||
|   currentAmountBox = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   current_amount_label = wx.StaticText(self.pane, -1, _(u"Transferred")) | ||||
|   self.current_amount = wx.TextCtrl(self.pane, -1, value='0', style=wx.TE_READONLY|wx.TE_MULTILINE) | ||||
|   currentAmountBox.Add(current_amount_label) | ||||
|   currentAmountBox.Add(self.current_amount) | ||||
|   totalSizeBox = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   total_size_label = wx.StaticText(self.pane, -1, _(u"Total file size")) | ||||
|   self.total_size = wx.TextCtrl(self.pane, -1, value='0', style=wx.TE_READONLY|wx.TE_MULTILINE) | ||||
|   totalSizeBox.Add(total_size_label) | ||||
|   totalSizeBox.Add(self.total_size) | ||||
|   speedBox = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   speedLabel = wx.StaticText(self.pane, -1, _(u"Transfer rate")) | ||||
|   self.speed = wx.TextCtrl(self.pane, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, value="0 Kb/s") | ||||
|   speedBox.Add(speedLabel) | ||||
|   speedBox.Add(self.speed) | ||||
|   etaBox = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   etaLabel = wx.StaticText(self.pane, -1, _(u"Time left")) | ||||
|   self.eta = wx.TextCtrl(self.pane, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, value="Unknown", size=(200, 100)) | ||||
|   etaBox.Add(etaLabel) | ||||
|   etaBox.Add(self.eta) | ||||
|   self.create_buttons() | ||||
|   sizer = wx.BoxSizer(wx.VERTICAL) | ||||
|   sizer.Add(fileBox) | ||||
|   sizer.Add(currentAmountBox) | ||||
|   sizer.Add(totalSizeBox) | ||||
|   sizer.Add(speedBox) | ||||
|   sizer.Add(etaBox) | ||||
|   sizer.Add(self.progress_bar) | ||||
|   self.pane.SetSizerAndFit(sizer) | ||||
|  | ||||
|  def update(self, data): | ||||
|   wx.CallAfter(self.progress_bar.SetValue, data["percent"]) | ||||
|   wx.CallAfter(self.current_amount.SetValue, '%s (%d%%)' % (convert_bytes(data["current"]), data["percent"])) | ||||
|   wx.CallAfter(self.total_size.SetValue, convert_bytes(data["total"])) | ||||
|   wx.CallAfter(self.speed.SetValue, data["speed"]) | ||||
|   if data["eta"]: | ||||
|    wx.CallAfter(self.eta.SetValue, seconds_to_string(data["eta"])) | ||||
|  | ||||
|  def create_buttons(self): | ||||
|   self.cancel_button = wx.Button(parent=self.pane, id=wx.ID_CANCEL) | ||||
|  | ||||
|  def get_response(self): | ||||
|   self.Show() | ||||
|  | ||||
|  def destroy(self): | ||||
|   self.Destroy() | ||||
|  | ||||
| class UploadDialog(TransferDialog): | ||||
|  | ||||
|  def __init__(self, filename=None, *args, **kwargs): | ||||
|   super(UploadDialog, self).__init__(filename=filename, *args, **kwargs) | ||||
|  | ||||
| class DownloadDialog(TransferDialog): | ||||
|  | ||||
|  def __init__(self, *args, **kwargs): | ||||
|   super(Download, self).__init__(*args, **kwargs) | ||||
| @@ -1,78 +0,0 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| ############################################################ | ||||
| #    Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net> | ||||
| #        | ||||
| #    This program is free software: you can redistribute it and/or modify | ||||
| #    it under the terms of the GNU General Public License as published by | ||||
| #    the Free Software Foundation, either version 2 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 General Public License for more details. | ||||
| # | ||||
| #    You should have received a copy of the GNU General Public License | ||||
| #    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| ############################################################ | ||||
| import wx | ||||
| import widgetUtils | ||||
| import output | ||||
| import logging | ||||
| log = logging.getLogger("extra.AudioUploader.wx_UI") | ||||
|  | ||||
| class audioDialog(widgetUtils.BaseDialog): | ||||
|  def __init__(self, services): | ||||
|   log.debug("creating audio  dialog.") | ||||
|   super(audioDialog, self).__init__(None, -1, _(u"Attach audio")) | ||||
|   panel = wx.Panel(self) | ||||
|   sizer = wx.BoxSizer(wx.VERTICAL) | ||||
|   btnSizer = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   btnSizer2 = wx.BoxSizer(wx.HORIZONTAL) | ||||
|  | ||||
|   self.play = wx.Button(panel, -1, _(u"&Play")) | ||||
|   self.play.Disable() | ||||
|   self.pause = wx.Button(panel, -1, _(u"&Pause")) | ||||
|   self.pause.Disable() | ||||
|   self.record = wx.Button(panel, -1, _(u"&Record")) | ||||
|   self.record.SetFocus() | ||||
|   self.attach_exists = wx.Button(panel, -1, _(u"&Add an existing file")) | ||||
|   self.discard = wx.Button(panel, -1, _(u"&Discard")) | ||||
|   self.discard.Disable() | ||||
|   label = wx.StaticText(panel, -1, _(u"Upload to")) | ||||
|   self.services = wx.ComboBox(panel, -1, choices=services, value=services[0], style=wx.CB_READONLY) | ||||
|   servicesBox = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   servicesBox.Add(label, 0, wx.ALL, 5) | ||||
|   servicesBox.Add(self.services, 0, wx.ALL, 5) | ||||
|   self.attach = wx.Button(panel, wx.ID_OK, _(u"Attach")) | ||||
|   self.attach.Disable() | ||||
|   cancel = wx.Button(panel, wx.ID_CANCEL, _(u"&Cancel")) | ||||
|   btnSizer.Add(self.play, 0, wx.ALL, 5) | ||||
|   btnSizer.Add(self.pause, 0, wx.ALL, 5) | ||||
|   btnSizer.Add(self.record, 0, wx.ALL, 5) | ||||
|   btnSizer2.Add(self.attach_exists, 0, wx.ALL, 5) | ||||
|   btnSizer2.Add(self.discard, 0, wx.ALL, 5) | ||||
|   btnSizer2.Add(self.attach, 0, wx.ALL, 5) | ||||
|   btnSizer2.Add(cancel, 0, wx.ALL, 5) | ||||
|   sizer.Add(servicesBox, 0, wx.ALL, 5) | ||||
|   sizer.Add(btnSizer, 0, wx.ALL, 5) | ||||
|   sizer.Add(btnSizer2, 0, wx.ALL, 5) | ||||
|   panel.SetSizer(sizer) | ||||
|   self.SetClientSize(sizer.CalcMin()) | ||||
|  | ||||
|  def enable_control(self, control): | ||||
|   log.debug("Enabling control %s" % (control,)) | ||||
|   if hasattr(self, control): | ||||
|    getattr(self, control).Enable() | ||||
|  | ||||
|  def disable_control(self, control): | ||||
|   log.debug("Disabling control %s" % (control,)) | ||||
|   if hasattr(self, control): | ||||
|    getattr(self, control).Disable() | ||||
|  | ||||
|  def get_file(self): | ||||
|   openFileDialog = wx.FileDialog(self, _(u"Select the audio file to be uploaded"), "", "", _("Audio Files (*.mp3, *.ogg, *.wav)|*.mp3; *.ogg; *.wav"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) | ||||
|   if openFileDialog.ShowModal() == wx.ID_CANCEL: | ||||
|    return False | ||||
|   return openFileDialog.GetPath() | ||||
| @@ -1 +0,0 @@ | ||||
| from soundsTutorial import soundsTutorial | ||||
| @@ -1,25 +0,0 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| from gi.repository import Gtk | ||||
| import widgetUtils | ||||
|  | ||||
| class soundsTutorialDialog(Gtk.Dialog): | ||||
|  def __init__(self, actions): | ||||
|   super(soundsTutorialDialog, self).__init__("Sounds tutorial", None, 0, (Gtk.STOCK_CANCEL, widgetUtils.CANCEL)) | ||||
|   box = self.get_content_area() | ||||
|   label = Gtk.Label("Press enter for listen the sound") | ||||
|   self.list = widgetUtils.list("Action") | ||||
|   self.populate_actions(actions) | ||||
|   lBox = Gtk.Box(spacing=6) | ||||
|   lBox.add(label) | ||||
|   lBox.add(self.list.list) | ||||
|   box.add(lBox) | ||||
|   self.play = Gtk.Button("Play") | ||||
|   box.add(self.play) | ||||
|   self.show_all() | ||||
|  | ||||
|  def populate_actions(self, actions): | ||||
|   for i in actions: | ||||
|    self.list.insert_item(i) | ||||
|  | ||||
|  def get_selected(self): | ||||
|   return self.list.get_selected() | ||||
| @@ -1,11 +0,0 @@ | ||||
| #Reverse sort, by Bill Dengler <codeofdusk@gmail.com> for use in TWBlue http://twblue.es | ||||
| def invert_tuples(t): | ||||
|     "Invert a list of tuples, so that the 0th element becomes the -1th, and the -1th becomes the 0th." | ||||
|     res=[] | ||||
|     for i in t: | ||||
|         res.append(i[::-1]) | ||||
|     return res | ||||
|  | ||||
| def reverse_sort(t): | ||||
|     "Sorts a list of tuples/lists by their last elements, not their first." | ||||
|     return invert_tuples(sorted(invert_tuples(t))) | ||||
| @@ -1,34 +0,0 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import platform | ||||
| import widgetUtils | ||||
| import os | ||||
| import paths | ||||
| import logging | ||||
| log = logging.getLogger("extra.SoundsTutorial.soundsTutorial") | ||||
| import soundsTutorial_constants | ||||
| if platform.system() == "Windows": | ||||
|  import wx_ui as UI | ||||
| elif platform.system() == "Linux": | ||||
|  import gtk_ui as UI | ||||
|  | ||||
| class soundsTutorial(object): | ||||
|  def __init__(self, sessionObject): | ||||
|   log.debug("Creating sounds tutorial object...") | ||||
|   super(soundsTutorial, self).__init__() | ||||
|   self.session = sessionObject | ||||
|   self.actions = [] | ||||
|   log.debug("Loading actions for sounds tutorial...") | ||||
|   [self.actions.append(i[1]) for i in soundsTutorial_constants.actions] | ||||
|   self.files = [] | ||||
|   log.debug("Searching sound files...") | ||||
|   [self.files.append(i[0]) for i in soundsTutorial_constants.actions] | ||||
|   log.debug("Creating dialog...") | ||||
|   self.dialog = UI.soundsTutorialDialog(self.actions) | ||||
|   widgetUtils.connect_event(self.dialog.play, widgetUtils.BUTTON_PRESSED, self.on_play) | ||||
|   self.dialog.get_response() | ||||
|  | ||||
|  def on_play(self, *args, **kwargs): | ||||
|   try: | ||||
|    self.session.sound.play(self.files[self.dialog.get_selection()]+".ogg") | ||||
|   except: | ||||
|    log.exception("Error playing the %s sound" % (self.files[self.dialog.items.GetSelection()],)) | ||||
| @@ -1,27 +0,0 @@ | ||||
| #-*- coding: utf-8 -*- | ||||
| import reverse_sort | ||||
| import application | ||||
| actions = reverse_sort.reverse_sort([  ("audio", _(u"Audio tweet.")), | ||||
|   ("create_timeline", _(u"User timeline buffer created.")), | ||||
|     ("delete_timeline", _(u"Buffer destroied.")), | ||||
|     ("dm_received", _(u"Direct message received.")), | ||||
|     ("dm_sent", _(u"Direct message sent.")), | ||||
|     ("error", _(u"Error.")), | ||||
|   ("favourite", _(u"Tweet liked.")), | ||||
|   ("favourites_timeline_updated", _(u"Likes buffer updated.")), | ||||
|   ("geo",   _(u"Geotweet.")), | ||||
| ("limit", _(u"Boundary reached.")), | ||||
|     ("list_tweet", _(u"List updated.")), | ||||
|     ("max_length", _(u"Too many characters.")), | ||||
|     ("mention_received", _(u"Mention received.")), | ||||
|   ("new_event", _(u"New event.")), | ||||
|   ("ready", _(u"{0} is ready.").format(application.name,)), | ||||
|     ("reply_send", _(u"Mention sent.")), | ||||
|     ("retweet_send", _(u"Tweet retweeted.")), | ||||
|     ("search_updated", _(u"Search buffer updated.")), | ||||
|     ("tweet_received", _(u"Tweet received.")), | ||||
|     ("tweet_send", _(u"Tweet sent.")), | ||||
|     ("trends_updated", _(u"Trending topics buffer updated.")), | ||||
|     ("tweet_timeline", _(u"New tweet in user timeline buffer.")), | ||||
|     ("update_followers", _(u"New follower.")), | ||||
|     ("volume_changed", _(u"Volume changed."))]) | ||||
| @@ -1,29 +0,0 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import wx | ||||
| import widgetUtils | ||||
|  | ||||
| class soundsTutorialDialog(widgetUtils.BaseDialog): | ||||
|  def __init__(self, actions): | ||||
|   super(soundsTutorialDialog, self).__init__(None, -1) | ||||
|   self.SetTitle(_(u"Sounds tutorial")) | ||||
|   panel = wx.Panel(self) | ||||
|   sizer = wx.BoxSizer(wx.VERTICAL) | ||||
|   label = wx.StaticText(panel, -1, _(u"Press enter to listen to the sound for the selected event")) | ||||
|   self.items = wx.ListBox(panel, 1, choices=actions, style=wx.LB_SINGLE) | ||||
|   self.items.SetSelection(0) | ||||
|   listBox = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   listBox.Add(label) | ||||
|   listBox.Add(self.items) | ||||
|   self.play = wx.Button(panel, 1, (u"Play")) | ||||
|   self.play.SetDefault() | ||||
|   close = wx.Button(panel, wx.ID_CANCEL) | ||||
|   btnBox = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   btnBox.Add(self.play) | ||||
|   btnBox.Add(close) | ||||
|   sizer.Add(listBox) | ||||
|   sizer.Add(btnBox) | ||||
|   panel.SetSizer(sizer) | ||||
|   self.SetClientSize(sizer.CalcMin()) | ||||
|  | ||||
|  def get_selection(self): | ||||
|   return self.items.GetSelection() | ||||
| @@ -1,2 +0,0 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import completion, settings | ||||
| @@ -1,47 +0,0 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import output | ||||
| import storage | ||||
| import wx_menu | ||||
|  | ||||
| class autocompletionUsers(object): | ||||
|  def __init__(self, window, session_id): | ||||
|   super(autocompletionUsers, self).__init__() | ||||
|   self.window = window | ||||
|   self.db = storage.storage(session_id) | ||||
|  | ||||
|  def show_menu(self, mode="tweet"): | ||||
|   position = self.window.get_position() | ||||
|   if mode == "tweet": | ||||
|    text = self.window.get_text() | ||||
|    text = text[:position] | ||||
|    try: | ||||
|     pattern = text.split()[-1] | ||||
|    except IndexError: | ||||
|     output.speak(_(u"You have to start writing")) | ||||
|     return | ||||
|    if pattern.startswith("@") == True: | ||||
|     menu = wx_menu.menu(self.window.text, pattern[1:], mode=mode) | ||||
|     users = self.db.get_users(pattern[1:]) | ||||
|     if len(users) > 0: | ||||
|      menu.append_options(users) | ||||
|      self.window.popup_menu(menu) | ||||
|      menu.destroy() | ||||
|     else: | ||||
|      output.speak(_(u"There are no results in your users database")) | ||||
|    else: | ||||
|     output.speak(_(u"Autocompletion only works for users.")) | ||||
|   elif mode == "dm": | ||||
|    text = self.window.get_user() | ||||
|    try: | ||||
|     pattern = text.split()[-1] | ||||
|    except IndexError: | ||||
|     output.speak(_(u"You have to start writing")) | ||||
|     return | ||||
|    menu = wx_menu.menu(self.window.cb, pattern, mode=mode) | ||||
|    users = self.db.get_users(pattern) | ||||
|    if len(users) > 0: | ||||
|     menu.append_options(users) | ||||
|     self.window.popup_menu(menu) | ||||
|     menu.destroy() | ||||
|    else: | ||||
|     output.speak(_(u"There are no results in your users database")) | ||||
| @@ -1,43 +0,0 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import storage | ||||
| import widgetUtils | ||||
| import wx_manage | ||||
| from wxUI import commonMessageDialogs | ||||
|  | ||||
| class autocompletionManage(object): | ||||
|  def __init__(self, session): | ||||
|   super(autocompletionManage, self).__init__() | ||||
|   self.session = session | ||||
|   self.dialog = wx_manage.autocompletionManageDialog() | ||||
|   self.database = storage.storage(self.session.session_id) | ||||
|   self.users = self.database.get_all_users() | ||||
|   self.dialog.put_users(self.users) | ||||
|   widgetUtils.connect_event(self.dialog.add, widgetUtils.BUTTON_PRESSED, self.add_user) | ||||
|   widgetUtils.connect_event(self.dialog.remove, widgetUtils.BUTTON_PRESSED, self.remove_user) | ||||
|   self.dialog.get_response() | ||||
|  | ||||
|  def update_list(self): | ||||
|   item = self.dialog.users.get_selected() | ||||
|   self.dialog.users.clear() | ||||
|   self.users = self.database.get_all_users() | ||||
|   self.dialog.put_users(self.users) | ||||
|   self.dialog.users.select_item(item) | ||||
|  | ||||
|  def add_user(self, *args, **kwargs): | ||||
|   usr = self.dialog.get_user() | ||||
|   if usr == False: | ||||
|    return | ||||
|   try: | ||||
|    data = self.session.twitter.twitter.show_user(screen_name=usr) | ||||
|   except: | ||||
|    self.dialog.show_invalid_user_error() | ||||
|    return | ||||
|   self.database.set_user(data["screen_name"], data["name"], 0) | ||||
|   self.update_list() | ||||
|  | ||||
|  def remove_user(self, ev): | ||||
|   if commonMessageDialogs.delete_user_from_db() == widgetUtils.YES: | ||||
|    item = self.dialog.users.get_selected() | ||||
|    user = self.users[item] | ||||
|    self.database.remove_user(user[0]) | ||||
|    self.update_list() | ||||
| @@ -1,59 +0,0 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import storage | ||||
| import widgetUtils | ||||
| import wx_settings | ||||
| import manage | ||||
| import output | ||||
| from mysc.thread_utils import call_threaded | ||||
|  | ||||
| class autocompletionSettings(object): | ||||
|  def __init__(self, config, buffer, window): | ||||
|   super(autocompletionSettings, self).__init__() | ||||
|   self.config = config | ||||
|   self.buffer = buffer | ||||
|   self.window = window | ||||
|   self.dialog = wx_settings.autocompletionSettingsDialog() | ||||
|   self.dialog.set("friends_buffer", self.config["mysc"]["save_friends_in_autocompletion_db"]) | ||||
|   self.dialog.set("followers_buffer", self.config["mysc"]["save_followers_in_autocompletion_db"]) | ||||
|   widgetUtils.connect_event(self.dialog.viewList, widgetUtils.BUTTON_PRESSED, self.view_list) | ||||
|   if self.dialog.get_response() == widgetUtils.OK: | ||||
|    call_threaded(self.add_users_to_database) | ||||
|  | ||||
|  def add_users_to_database(self): | ||||
|   self.config["mysc"]["save_friends_in_autocompletion_db"] = self.dialog.get("friends_buffer") | ||||
|   self.config["mysc"]["save_followers_in_autocompletion_db"] = self.dialog.get("followers_buffer") | ||||
|   output.speak(_(u"Updating database... You can close this window now. A message will tell you when the process finishes.")) | ||||
|   database = storage.storage(self.buffer.session.session_id) | ||||
|   if self.dialog.get("followers_buffer") == True: | ||||
|    buffer = self.window.search_buffer("followers", self.config["twitter"]["user_name"]) | ||||
|    for i in buffer.session.db[buffer.name]["items"]: | ||||
|     database.set_user(i["screen_name"], i["name"], 1) | ||||
|   else: | ||||
|    database.remove_by_buffer(1) | ||||
|   if self.dialog.get("friends_buffer") == True: | ||||
|    buffer = self.window.search_buffer("friends", self.config["twitter"]["user_name"]) | ||||
|    for i in buffer.session.db[buffer.name]["items"]: | ||||
|     database.set_user(i["screen_name"], i["name"], 2) | ||||
|   else: | ||||
|    database.remove_by_buffer(2) | ||||
|   wx_settings.show_success_dialog() | ||||
|   self.dialog.destroy() | ||||
|    | ||||
|  def view_list(self, ev): | ||||
|   q = manage.autocompletionManage(self.buffer.session) | ||||
|  | ||||
|  | ||||
| def execute_at_startup(window, buffer, config): | ||||
|  database = storage.storage(buffer.session.session_id) | ||||
|  if config["mysc"]["save_followers_in_autocompletion_db"] == True and config["other_buffers"]["show_followers"] == True: | ||||
|   buffer = window.search_buffer("followers", config["twitter"]["user_name"]) | ||||
|   for i in buffer.session.db[buffer.name]: | ||||
|    database.set_user(i["screen_name"], i["name"], 1) | ||||
|  else: | ||||
|   database.remove_by_buffer(1) | ||||
|  if config["mysc"]["save_friends_in_autocompletion_db"] == True and config["other_buffers"]["show_friends"] == True: | ||||
|   buffer = window.search_buffer("friends", config["twitter"]["user_name"]) | ||||
|   for i in buffer.session.db[buffer.name]: | ||||
|    database.set_user(i["screen_name"], i["name"], 2) | ||||
|  else: | ||||
|   database.remove_by_buffer(2)   | ||||
| @@ -1,52 +0,0 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import sqlite3, paths | ||||
|  | ||||
| class storage(object): | ||||
|  def __init__(self, session_id): | ||||
|   self.connection = sqlite3.connect(paths.config_path("%s/autocompletionUsers.dat" % (session_id))) | ||||
|   self.cursor = self.connection.cursor() | ||||
|   if self.table_exist("users") == False: | ||||
|    self.create_table() | ||||
|  | ||||
|  def table_exist(self, table): | ||||
|   ask = self.cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='%s'" % (table)) | ||||
|   answer = ask.fetchone() | ||||
|   if answer == None: | ||||
|    return False | ||||
|   else: | ||||
|    return True | ||||
|  | ||||
|  def get_all_users(self): | ||||
|   self.cursor.execute("""select * from users""") | ||||
|   return self.cursor.fetchall() | ||||
|  | ||||
|  def get_users(self, term): | ||||
|   self.cursor.execute("""SELECT * FROM users WHERE user LIKE ?""", ('{}%'.format(term),)) | ||||
|   return self.cursor.fetchall() | ||||
|  | ||||
|  def set_user(self, screen_name, user_name, from_a_buffer): | ||||
|   self.cursor.execute("""insert or ignore into users values(?, ?, ?)""", (screen_name, user_name, from_a_buffer)) | ||||
|   self.connection.commit() | ||||
|  | ||||
|  def remove_user(self, user): | ||||
|   self.cursor.execute("""DELETE FROM users WHERE user = ?""", (user,)) | ||||
|   self.connection.commit() | ||||
|   return self.cursor.fetchone() | ||||
|  | ||||
|  def remove_by_buffer(self, bufferType): | ||||
|   """ Removes all users saved on a buffer. BufferType is 0 for no buffer, 1 for friends and 2 for followers""" | ||||
|   self.cursor.execute("""DELETE  FROM users WHERE from_a_buffer = ?""", (bufferType,)) | ||||
|   self.connection.commit() | ||||
|   return self.cursor.fetchone() | ||||
|  | ||||
|  def create_table(self): | ||||
|   self.cursor.execute(""" | ||||
|   create table users( | ||||
| user TEXT UNIQUE, | ||||
| name TEXT, | ||||
| from_a_buffer INTEGER | ||||
| )""") | ||||
|  | ||||
|  def __del__(self): | ||||
|   self.cursor.close() | ||||
|   self.connection.close() | ||||
| @@ -1,43 +0,0 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import wx | ||||
| import widgetUtils | ||||
| from multiplatform_widgets import widgets | ||||
| import application | ||||
| class autocompletionManageDialog(widgetUtils.BaseDialog): | ||||
|  def __init__(self): | ||||
|   super(autocompletionManageDialog, self).__init__(parent=None, id=-1, title=_(u"Manage Autocompletion database")) | ||||
|   panel = wx.Panel(self) | ||||
|   sizer = wx.BoxSizer(wx.VERTICAL) | ||||
|   label = wx.StaticText(panel, -1, _(u"Editing {0} users database").format(application.name,)) | ||||
|   self.users = widgets.list(panel, _(u"Username"), _(u"Name"), style=wx.LC_REPORT) | ||||
|   sizer.Add(label, 0, wx.ALL, 5) | ||||
|   sizer.Add(self.users.list, 0, wx.ALL, 5) | ||||
|   self.add = wx.Button(panel, -1, _(u"Add user")) | ||||
|   self.remove = wx.Button(panel, -1, _(u"Remove user")) | ||||
|   optionsBox = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   optionsBox.Add(self.add, 0, wx.ALL, 5) | ||||
|   optionsBox.Add(self.remove, 0, wx.ALL, 5) | ||||
|   sizer.Add(optionsBox, 0, wx.ALL, 5) | ||||
|   ok = wx.Button(panel, wx.ID_OK) | ||||
|   cancel = wx.Button(panel, wx.ID_CANCEL) | ||||
|   sizerBtn = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   sizerBtn.Add(ok, 0, wx.ALL, 5) | ||||
|   sizer.Add(cancel, 0, wx.ALL, 5) | ||||
|   sizer.Add(sizerBtn, 0, wx.ALL, 5) | ||||
|   panel.SetSizer(sizer) | ||||
|   self.SetClientSize(sizer.CalcMin()) | ||||
|  | ||||
|  def put_users(self, users): | ||||
|   for i in users: | ||||
|    j = [i[0], i[1]] | ||||
|    self.users.insert_item(False, *j) | ||||
|  | ||||
|  def get_user(self): | ||||
|   usr = False | ||||
|   userDlg = wx.TextEntryDialog(None, _(u"Twitter username"), _(u"Add user to database")) | ||||
|   if userDlg.ShowModal() == wx.ID_OK: | ||||
|    usr = userDlg.GetValue() | ||||
|   return usr | ||||
|  | ||||
|  def show_invalid_user_error(self): | ||||
|   wx.MessageDialog(None, _(u"The user does not exist"), _(u"Error!"), wx.ICON_ERROR).ShowModal() | ||||
| @@ -1,25 +0,0 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import wx | ||||
|  | ||||
| class menu(wx.Menu): | ||||
|  def __init__(self, window, pattern, mode): | ||||
|   super(menu, self).__init__() | ||||
|   self.window = window | ||||
|   self.pattern = pattern | ||||
|   self.mode = mode | ||||
|  | ||||
|  def append_options(self, options): | ||||
|   for i in options: | ||||
|    item = wx.MenuItem(self, wx.NewId(), "%s (@%s)" % (i[1], i[0])) | ||||
|    self.AppendItem(item) | ||||
|    self.Bind(wx.EVT_MENU, lambda evt, temp=i[0]: self.select_text(evt, temp), item) | ||||
|  | ||||
|  def select_text(self, ev, text): | ||||
|   if self.mode == "tweet": | ||||
|    self.window.ChangeValue(self.window.GetValue().replace("@"+self.pattern, "@"+text+" ")) | ||||
|   elif self.mode == "dm": | ||||
|    self.window.SetValue(self.window.GetValue().replace(self.pattern, text)) | ||||
|   self.window.SetInsertionPointEnd() | ||||
|  | ||||
|  def destroy(self): | ||||
|   self.Destroy() | ||||
| @@ -1,27 +0,0 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import wx | ||||
| import widgetUtils | ||||
| import application | ||||
|  | ||||
| class autocompletionSettingsDialog(widgetUtils.BaseDialog): | ||||
|  def __init__(self): | ||||
|   super(autocompletionSettingsDialog, self).__init__(parent=None, id=-1, title=_(u"Autocomplete users' settings")) | ||||
|   panel = wx.Panel(self) | ||||
|   sizer = wx.BoxSizer(wx.VERTICAL) | ||||
|   self.followers_buffer = wx.CheckBox(panel, -1, _(u"Add users from followers buffer")) | ||||
|   self.friends_buffer = wx.CheckBox(panel, -1, _(u"Add users from friends buffer")) | ||||
|   sizer.Add(self.followers_buffer, 0, wx.ALL, 5) | ||||
|   sizer.Add(self.friends_buffer, 0, wx.ALL, 5) | ||||
|   self.viewList = wx.Button(panel, -1, _(u"Manage database...")) | ||||
|   sizer.Add(self.viewList, 0, wx.ALL, 5) | ||||
|   ok = wx.Button(panel, wx.ID_OK) | ||||
|   cancel = wx.Button(panel, wx.ID_CANCEL) | ||||
|   sizerBtn = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   sizerBtn.Add(ok, 0, wx.ALL, 5) | ||||
|   sizer.Add(cancel, 0, wx.ALL, 5) | ||||
|   sizer.Add(sizerBtn, 0, wx.ALL, 5) | ||||
|   panel.SetSizer(sizer) | ||||
|   self.SetClientSize(sizer.CalcMin()) | ||||
|  | ||||
| def show_success_dialog(): | ||||
|  wx.MessageDialog(None, _(u"{0}'s database of users has been updated.").format(application.name,), _(u"Done"), wx.OK).ShowModal() | ||||
| @@ -42,7 +42,7 @@ class sessionManagerWindow(widgetUtils.BaseDialog): | ||||
| 		self.EndModal(wx.ID_OK) | ||||
|  | ||||
| 	def new_account_dialog(self): | ||||
| 		return wx.MessageDialog(self, _(u"The request for the required facebook authorization to continue will be opened on your browser. You only need to do it once. Would you like to autorhise a new account now?"), _(u"Authorisation"), wx.YES_NO).ShowModal() | ||||
| 		return wx.MessageDialog(self, _(u"Would you like to autorhise a new account now?"), _(u"Authorisation"), wx.YES_NO).ShowModal() | ||||
|  | ||||
| 	def add_new_session_to_list(self): | ||||
| 		total = self.list.get_count() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user